Files
playground/hardcode_subs.py

102 lines
3.0 KiB
Python
Executable File

#!/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()