#!/usr/bin/env -S python3.12 from pathlib import Path import logging import subprocess import argparse logger = logging.getLogger(__name__) def hardcode_subs( video_path: Path, subtitle_path: Path, lang: str, save_path: Path | None = None, resolution: str | None = None, quality: float | None = None, font_size: int = 40, ) -> Path: if not save_path: save_path = video_path.with_name(f"{video_path.stem}.{lang}.hardcoded.mp4") vf_filters = [f"subtitles='{subtitle_path}:force_style=Fontsize={font_size}'"] if resolution: resolution_map = {"1080p": 1920, "720p": 1280, "480p": 854} max_width = resolution_map.get(resolution) if max_width: vf_filters.append(f"scale='min({max_width},iw)':-2") vf = ",".join(vf_filters) crf = str(quality) if quality else "35" cmd = [ "ffmpeg", "-y", "-hwaccel", "videotoolbox", "-i", str(video_path), "-vf", vf, "-c:v", "h264_videotoolbox", "-crf", crf, "-c:a", "copy", str(save_path), ] logger.info(f"Hardcoding subtitles into video: {save_path.name}") subprocess.run(cmd, check=True) return save_path def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="Hardcode subtitles into a video.", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("video", type=Path, help="Path to the video file") parser.add_argument("subtitle", type=Path, help="Path to the subtitle file") parser.add_argument("--out", "-o", type=Path, help="Path to the output video file") parser.add_argument("--lang", default="en", help="Language code for subtitles") parser.add_argument( "--resolution", choices=["1080p", "720p", "480p"], help="Set output video resolution (max width)", ) parser.add_argument( "--quality", "-q", type=float, default=35, help="Set CRF quality factor (lower is better, default 35)", ) parser.add_argument( "--font-size", type=int, default=40, help="Font size for hardcoded subtitles", ) return parser.parse_args() def main(): logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s") args = parse_args() if not args.video.exists(): raise FileNotFoundError(f"Video file not found: {args.video}") if not args.subtitle.exists(): raise FileNotFoundError(f"Subtitle file not found: {args.subtitle}") save_path = args.out or args.video.with_name(f"{args.video.stem}.{args.lang}.hardcoded.mp4") hardcoded_video = hardcode_subs( video_path=args.video, subtitle_path=args.subtitle, lang=args.lang, save_path=save_path, resolution=args.resolution, quality=args.quality, font_size=args.font_size, ) logging.info(f"Hardcoded video saved at: {hardcoded_video}") if __name__ == "__main__": main()