#!/usr/bin/env python3 """Transcode video archive files to browser-compatible H.264 MP4 format.""" import os import sys import subprocess import shutil from pathlib import Path VIDEO_EXTS = {".mp4", ".mkv", ".mov", ".avi", ".webm", ".m4v"} def probe_codec(file_path: Path) -> str: cmd = [ "ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=codec_name", "-of", "default=noprint_wrappers=1:nokey=1", str(file_path) ] res = subprocess.run(cmd, capture_output=True, text=True) return res.stdout.strip().lower() def transcode_video(file_path: Path): tmp_path = file_path.with_name(f"_transcoded_{file_path.stem}.mp4") cmd = [ "ffmpeg", "-hide_banner", "-loglevel", "error", "-y", "-i", str(file_path), "-c:v", "libx264", "-preset", "fast", "-crf", "22", "-c:a", "aac", "-b:a", "128k", "-movflags", "+faststart", str(tmp_path) ] res = subprocess.run(cmd) if res.returncode == 0 and tmp_path.exists() and tmp_path.stat().st_size > 0: # Save as .mp4 target_path = file_path.with_suffix(".mp4") if target_path != file_path and file_path.exists(): file_path.unlink() shutil.move(str(tmp_path), str(target_path)) print(f" ✅ Transcoded -> {target_path}") else: if tmp_path.exists(): tmp_path.unlink() print(f" ❌ Failed to transcode {file_path}") def process_archive(archive_dir: Path): if not archive_dir.exists(): print(f"Archive directory {archive_dir} does not exist.") return print(f"Scanning {archive_dir} for non-H.264 videos...") for root, _, files in os.walk(archive_dir): for file in sorted(files): file_path = Path(root) / file if file_path.suffix.lower() in VIDEO_EXTS and not file.startswith("_transcoded_"): codec = probe_codec(file_path) if not codec: print(f"⚠️ Could not probe codec or unreadable: {file_path}") continue if codec != "h264": print(f"🔄 Converting {file_path} (current codec: {codec})...") transcode_video(file_path) else: print(f" ✓ Already H.264: {file_path}") if __name__ == "__main__": target = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("data/archive") process_archive(target)