From c7dc56d8b586f62ffa554ac1851dabb0293d148e Mon Sep 17 00:00:00 2001 From: CarolinePascal Date: Fri, 24 Apr 2026 17:19:19 +0200 Subject: [PATCH] chore(format): fixing formatting issues --- src/lerobot/datasets/aggregate.py | 2 +- src/lerobot/datasets/dataset_writer.py | 4 +--- src/lerobot/datasets/lerobot_dataset.py | 2 +- src/lerobot/datasets/pyav_utils.py | 24 +++++-------------- src/lerobot/datasets/video_utils.py | 31 ++++++++++++++----------- tests/datasets/test_video_encoding.py | 27 +++++++++++---------- 6 files changed, 42 insertions(+), 48 deletions(-) diff --git a/src/lerobot/datasets/aggregate.py b/src/lerobot/datasets/aggregate.py index c9bb88da4..76d1371f3 100644 --- a/src/lerobot/datasets/aggregate.py +++ b/src/lerobot/datasets/aggregate.py @@ -416,7 +416,7 @@ def aggregate_videos(src_meta, dst_meta, videos_idx, video_files_size_in_mb, chu concatenate_video_files( [dst_path, src_path], dst_path, - compatibilty_check=True, + compatibility_check=True, ) # Update duration of this destination file dst_file_durations[dst_key] = current_dst_duration + src_duration diff --git a/src/lerobot/datasets/dataset_writer.py b/src/lerobot/datasets/dataset_writer.py index 645d2752a..4841f7d3b 100644 --- a/src/lerobot/datasets/dataset_writer.py +++ b/src/lerobot/datasets/dataset_writer.py @@ -502,9 +502,7 @@ class DatasetWriter: # Update video info (only needed when first episode is encoded) if episode_index == 0: - self._meta.update_video_info( - video_key, camera_encoder_config=self._camera_encoder_config - ) + self._meta.update_video_info(video_key, camera_encoder_config=self._camera_encoder_config) write_info(self._meta.info, self._meta.root) metadata = { diff --git a/src/lerobot/datasets/lerobot_dataset.py b/src/lerobot/datasets/lerobot_dataset.py index 79efa330b..af53bb87f 100644 --- a/src/lerobot/datasets/lerobot_dataset.py +++ b/src/lerobot/datasets/lerobot_dataset.py @@ -36,8 +36,8 @@ from .utils import ( ) from .video_utils import ( StreamingVideoEncoder, - get_safe_default_video_backend, VideoEncoderConfig, + get_safe_default_video_backend, ) logger = logging.getLogger(__name__) diff --git a/src/lerobot/datasets/pyav_utils.py b/src/lerobot/datasets/pyav_utils.py index 9879251df..ca9dc12f5 100644 --- a/src/lerobot/datasets/pyav_utils.py +++ b/src/lerobot/datasets/pyav_utils.py @@ -95,9 +95,7 @@ def detect_available_encoders_pyav(encoders: list[str] | str) -> list[str]: return available -def _is_field_supported( - field_name: str, vcodec: str, options: dict[str, av.option.Option] -) -> bool: +def _is_field_supported(field_name: str, vcodec: str, options: dict[str, av.option.Option]) -> bool: """Whether tuning option *field_name* is meaningful for *vcodec*.""" # GOP is a stream-level option (AVStream.gop_size) not stored in private options. # Every video codec accepts it. @@ -118,20 +116,14 @@ def _is_field_supported( return field_name in options -def _check_numeric_range( - label: str, num: float, opt: av.option.Option, vcodec: str -) -> None: +def _check_numeric_range(label: str, num: float, opt: av.option.Option, vcodec: str) -> None: """Raise if *num* lies outside *opt*'s numeric range (no-op if range is degenerate).""" lo, hi = float(opt.min), float(opt.max) if lo < hi and not (lo <= num <= hi): - raise ValueError( - f"{label}={num} is out of range for codec {vcodec!r}; must be in [{lo}, {hi}]" - ) + raise ValueError(f"{label}={num} is out of range for codec {vcodec!r}; must be in [{lo}, {hi}]") -def _validate_option_value( - vcodec: str, field_name: str, value: Any, opt: av.option.Option -) -> None: +def _validate_option_value(vcodec: str, field_name: str, value: Any, opt: av.option.Option) -> None: """Range-check numeric *value* and choice-check string *value* against *opt*. Type mismatches fall through to FFmpeg's own validation at encode time. @@ -154,9 +146,7 @@ def _validate_option_value( return -def _validate_extra_option( - vcodec: str, key: str, value: Any, opt: av.option.Option -) -> None: +def _validate_extra_option(vcodec: str, key: str, value: Any, opt: av.option.Option) -> None: """Validate an ``extra_options`` entry: enforce numeric range/type only. Non-numeric options are passed through (FFmpeg accepts many ad-hoc strings). @@ -208,9 +198,7 @@ def _check_tuning_fields( # Enforce a positive integer value. if field_name == "g": if isinstance(value, bool) or not isinstance(value, int) or value < 1: - raise ValueError( - f"g={value!r} must be a positive integer for codec {vcodec!r}" - ) + raise ValueError(f"g={value!r} must be a positive integer for codec {vcodec!r}") continue # Value shape is only cross-checkable when the field maps directly # to a private option: ``preset`` is literally ``"preset"``; diff --git a/src/lerobot/datasets/video_utils.py b/src/lerobot/datasets/video_utils.py index 1590c4551..821c51f26 100644 --- a/src/lerobot/datasets/video_utils.py +++ b/src/lerobot/datasets/video_utils.py @@ -100,7 +100,6 @@ class VideoEncoderConfig: self.validate() - def detect_available_encoders(self, encoders: list[str] | str) -> list[str]: """Detect available encoders based on the video backend.""" if self.video_backend == "pyav": @@ -108,13 +107,11 @@ class VideoEncoderConfig: else: return [] - def validate(self) -> None: """Validate the video encoder config.""" if self.video_backend == "pyav": check_video_encoder_config_pyav(self) - def resolve_vcodec(self) -> None: """Validate vcodec and resolve 'auto' to best available HW encoder, fallback to libsvtav1. @@ -123,9 +120,7 @@ class VideoEncoderConfig: a host missing the requested encoder. """ if self.vcodec not in VALID_VIDEO_CODECS: - raise ValueError( - f"Invalid vcodec '{self.vcodec}'. Must be one of: {sorted(VALID_VIDEO_CODECS)}" - ) + raise ValueError(f"Invalid vcodec '{self.vcodec}'. Must be one of: {sorted(VALID_VIDEO_CODECS)}") if self.vcodec == "auto": available = self.detect_available_encoders(HW_ENCODERS) for encoder in HW_ENCODERS: @@ -142,7 +137,6 @@ class VideoEncoderConfig: return raise ValueError(f"Unsupported video codec: {self.vcodec} with video backend {self.video_backend}") - def get_codec_options(self, encoder_threads: int | None = None) -> dict[str, str]: """Translate the tuning fields to codec-specific FFmpeg options. @@ -563,7 +557,10 @@ def encode_video_frames( def concatenate_video_files( - input_video_paths: list[Path | str], output_video_path: Path, overwrite: bool = True, compatibilty_check: bool = False + input_video_paths: list[Path | str], + output_video_path: Path, + overwrite: bool = True, + compatibility_check: bool = False, ): """ Concatenate multiple video files into a single video file using pyav. @@ -576,7 +573,7 @@ def concatenate_video_files( input_video_paths: Ordered list of input video file paths to concatenate. output_video_path: Path to the output video file. overwrite: Whether to overwrite the output video file if it already exists. Default is True. - compatibilty_check: Whether to check if the input videos are compatible. Default is False. + compatibility_check: Whether to check if the input videos are compatible. Default is False. Note: - Creates a temporary directory for intermediate files that is cleaned up after use. @@ -596,12 +593,20 @@ def concatenate_video_files( raise FileNotFoundError("No input video paths provided.") # This check may be skipped at recording time as videos are encoded with the same encoder config. - if compatibilty_check: + if compatibility_check: reference_video_info = get_video_info(input_video_paths[0]) for input_path in input_video_paths[1:]: video_info = get_video_info(input_path) - if video_info["video.height"] != reference_video_info["video.height"] or video_info["video.width"] != reference_video_info["video.width"] or video_info["video.fps"] != reference_video_info["video.fps"] or video_info["video.codec"] != reference_video_info["video.codec"] or video_info["video.pix_fmt"] != reference_video_info["video.pix_fmt"]: - raise ValueError(f"Input video {input_path} is not compatible with the reference video {input_video_paths[0]}.") + if ( + video_info["video.height"] != reference_video_info["video.height"] + or video_info["video.width"] != reference_video_info["video.width"] + or video_info["video.fps"] != reference_video_info["video.fps"] + or video_info["video.codec"] != reference_video_info["video.codec"] + or video_info["video.pix_fmt"] != reference_video_info["video.pix_fmt"] + ): + raise ValueError( + f"Input video {input_path} is not compatible with the reference video {input_video_paths[0]}." + ) # Create a temporary .ffconcat file to list the input video paths with tempfile.NamedTemporaryFile(mode="w", suffix=".ffconcat", delete=False) as tmp_concatenate_file: @@ -1058,7 +1063,7 @@ def get_video_info( Args: video_path: Path to the encoded video file to probe. - camera_encoder_config: If provided, record the exact encoder settings used to encode this + camera_encoder_config: If provided, record the exact encoder settings used to encode this video. Stream-derived values take precedence — encoder fields are only written for keys not already populated from the video file itself. """ diff --git a/tests/datasets/test_video_encoding.py b/tests/datasets/test_video_encoding.py index 7826ab42b..3053bc75e 100644 --- a/tests/datasets/test_video_encoding.py +++ b/tests/datasets/test_video_encoding.py @@ -28,7 +28,7 @@ import av # noqa: E402 from lerobot.datasets.image_writer import write_image from lerobot.datasets.lerobot_dataset import LeRobotDataset -from lerobot.datasets.pyav_utils import detect_available_encoders_pyav, get_codec +from lerobot.datasets.pyav_utils import get_codec from lerobot.datasets.utils import INFO_PATH from lerobot.datasets.video_utils import ( VALID_VIDEO_CODECS, @@ -38,12 +38,11 @@ from lerobot.datasets.video_utils import ( get_video_info, ) + # Per-codec skip markers — validation tests only fire when the codec is available def _require_encoder(vcodec: str) -> pytest.MarkDecorator: """Skip the test if ``vcodec`` is not available in the local FFmpeg build.""" - return pytest.mark.skipif( - get_codec(vcodec) is None, reason=f"{vcodec!r} not in local FFmpeg build" - ) + return pytest.mark.skipif(get_codec(vcodec) is None, reason=f"{vcodec!r} not in local FFmpeg build") require_libsvtav1 = _require_encoder("libsvtav1") @@ -306,7 +305,9 @@ def _write_frames(imgs_dir: Path, num_frames: int = 4, height: int = 64, width: write_image(arr, imgs_dir / f"frame-{i:06d}.png") -def _encode_video(path: Path, num_frames: int = 4, fps: int = 30, cfg: VideoEncoderConfig | None = None) -> Path: +def _encode_video( + path: Path, num_frames: int = 4, fps: int = 30, cfg: VideoEncoderConfig | None = None +) -> Path: imgs_dir = path.parent / f"imgs_{path.stem}" _write_frames(imgs_dir, num_frames=num_frames) encode_video_frames(imgs_dir, path, fps=fps, camera_encoder_config=cfg, overwrite=True) @@ -321,11 +322,13 @@ def _read_feature_info(dataset: LeRobotDataset) -> dict: def _add_frames(dataset: LeRobotDataset, num_frames: int) -> None: shape = dataset.meta.features[VIDEO_KEY]["shape"] for _ in range(num_frames): - dataset.add_frame({ - VIDEO_KEY: np.random.randint(0, 256, shape, dtype=np.uint8), - "action": np.zeros(2, dtype=np.float32), - "task": "test", - }) + dataset.add_frame( + { + VIDEO_KEY: np.random.randint(0, 256, shape, dtype=np.uint8), + "action": np.zeros(2, dtype=np.float32), + "task": "test", + } + ) class TestGetVideoInfo: @@ -480,7 +483,7 @@ class TestConcatenateVideoFiles: concatenate_video_files( [ARTIFACTS / "clip_4frames.mp4", ARTIFACTS / "clip_h264.mp4"], tmp_path / "out.mp4", - compatibilty_check=True, + compatibility_check=True, ) def test_compatibility_check_raises_on_different_resolution(self, tmp_path): @@ -488,7 +491,7 @@ class TestConcatenateVideoFiles: concatenate_video_files( [ARTIFACTS / "clip_4frames.mp4", ARTIFACTS / "clip_32x48.mp4"], tmp_path / "out.mp4", - compatibilty_check=True, + compatibility_check=True, )