forked from LiveCarta/ContentGeneration
Refactor src layout and add logging lifecycle + tests
This commit is contained in:
85
src/concat_merged.py
Normal file
85
src/concat_merged.py
Normal file
@@ -0,0 +1,85 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user