#!/usr/bin/env python3 """Concatenate merged_*.mp4 files into a single output using ffmpeg concat demuxer.""" from __future__ import annotations import argparse import logging import re import subprocess import tempfile from pathlib import Path from logging_config import configure_logging, debug_log_lifecycle SCRIPT_DIR = Path(__file__).resolve().parent DEFAULT_BASE_DIR = SCRIPT_DIR.parents[1] DEFAULT_MERGED_DIR = DEFAULT_BASE_DIR / "merged" DEFAULT_OUTPUT = DEFAULT_BASE_DIR / "results" / "run_3" / "final_output.mp4" LOGGER = logging.getLogger(__name__) def shot_number(path: Path) -> int: match = re.search(r"merged_(\d+)\.mp4$", path.name) return int(match.group(1)) if match else -1 def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--merged-dir", type=Path, default=DEFAULT_MERGED_DIR) parser.add_argument("--output", type=Path, default=DEFAULT_OUTPUT) parser.add_argument( "--log-level", default=None, help="Logging level (overrides LOG_LEVEL env var)", ) return parser.parse_args() @debug_log_lifecycle def main() -> int: args = parse_args() configure_logging(args.log_level) videos = sorted(args.merged_dir.glob("merged_*.mp4"), key=shot_number) if not videos: LOGGER.warning("No merged videos found in %s", args.merged_dir) return 1 args.output.parent.mkdir(parents=True, exist_ok=True) with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as tmp: filelist = Path(tmp.name) for video in videos: tmp.write(f"file '{video}'\\n") try: LOGGER.info("Concatenating the following files:\n%s", filelist.read_text().rstrip()) subprocess.run( [ "ffmpeg", "-f", "concat", "-safe", "0", "-i", str(filelist), "-c", "copy", "-y", str(args.output), ], check=True, ) finally: filelist.unlink(missing_ok=True) LOGGER.info("Done") return 0 if __name__ == "__main__": raise SystemExit(main())