marquee / video.py
mamuflih13's picture
chores
4778b0b
Raw
History Blame Contribute Delete
2.42 kB
import json
import subprocess
import tempfile
SHORT_CAP = 720
LONG_CAP = 1280
def _probe(video_path: str) -> dict:
out = subprocess.run(
["ffprobe", "-v", "quiet", "-print_format", "json",
"-show_format", "-show_streams", video_path],
capture_output=True, text=True, check=True,
)
return json.loads(out.stdout)
def get_duration(video_path: str) -> float:
return float(_probe(video_path)["format"]["duration"])
def get_display_dims(video_path: str) -> tuple[int, int]:
"""Return (width, height) as DISPLAYED — i.e. after rotation metadata is
applied. Phone portrait clips are often stored landscape + a rotate flag;
if we ignored it the target size (and detection) would be sideways.
"""
info = _probe(video_path)
v = next(s for s in info["streams"] if s["codec_type"] == "video")
w, h = int(v["width"]), int(v["height"])
rot = 0
if "tags" in v and "rotate" in v["tags"]:
rot = int(v["tags"]["rotate"])
for sd in v.get("side_data_list", []):
if "rotation" in sd:
rot = int(sd["rotation"])
if abs(rot) % 180 == 90: # 90 or 270 -> swap
w, h = h, w
return w, h
def compute_target_size(w: int, h: int) -> tuple[int, int]:
"""Scale = min(720/short, 1280/long, 1.0); dims forced even; no upscale."""
short, long = min(w, h), max(w, h)
s = min(SHORT_CAP / short, LONG_CAP / long, 1.0)
out_w = max(2, int(round(w * s / 2)) * 2)
out_h = max(2, int(round(h * s / 2)) * 2)
return out_w, out_h
def normalize_video(video_path: str) -> tuple[str, int, int]:
"""ffmpeg: autorotate + scale to target + re-encode H.264 (+ keep audio).
Returns (normalized_path, out_w, out_h). Everything downstream uses this.
"""
w, h = get_display_dims(video_path)
out_w, out_h = compute_target_size(w, h)
# NamedTemporaryFile gives us a safe unique path (mktemp is deprecated /
# racy); close it and let ffmpeg overwrite via -y.
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4")
out_path = tmp.name
tmp.close()
subprocess.run(
["ffmpeg", "-y", "-i", video_path,
"-vf", f"scale={out_w}:{out_h}",
"-c:v", "libx264", "-preset", "veryfast", "-pix_fmt", "yuv420p",
"-c:a", "aac", "-movflags", "+faststart", out_path],
check=True, capture_output=True,
)
return out_path, out_w, out_h