#!/usr/bin/env python3.11 import argparse import logging import subprocess from pathlib import Path def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( description="Encode middle 1-min slice of video with multiple CRF values.", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) parser.add_argument("video", type=Path, help="Input video file") parser.add_argument("--codec", choices=["x264", "x265"], default="x265", help="Codec to use") parser.add_argument("--crf", nargs="+", type=int, default=[15, 20, 25, 30, 35, 40], help="CRF values") return parser.parse_args() def read_video_duration(video_path: Path) -> float: cmd = [ "ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", str(video_path), ] result = subprocess.run(cmd, capture_output=True, text=True, check=True) return float(result.stdout.strip()) def encode_clip(clip_path: Path, codec: str, crf: int, out_path: Path) -> Path: # clip_path is actually the input video, we encode the middle slice on the fly total_duration = read_video_duration(clip_path) slice_duration = 60 start = max(0, int(total_duration // 2 - slice_duration // 2)) if codec == "x264": lib = "h264_videotoolbox" else: lib = "hevc_videotoolbox" cmd = [ "ffmpeg", "-y", "-ss", str(start), "-t", str(slice_duration), "-i", str(clip_path), "-c:v", lib, "-q:v", str(crf), "-preset", "medium", "-c:a", "copy", str(out_path), ] logging.info(f"Encoding {out_path.name} with {lib} q:v={crf} (hwaccel) from middle slice") subprocess.run(cmd, check=True) def main(): logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s") args = parse_args() outdir = args.video.parent outdir.mkdir(parents=True, exist_ok=True) for crf in args.crf: out_path = outdir / f"{args.video.stem}.{args.codec}_crf{crf}.mp4" encode_clip(clip_path=args.video, codec=args.codec, crf=crf, out_path=out_path) if __name__ == "__main__": main()