Files
jellyfin-scene-search/scene_search/frames.py
T
2026-10-06 13:27:23 +01:00

43 lines
1.2 KiB
Python

from __future__ import annotations
import hashlib
import shutil
import subprocess
from pathlib import Path
def require_ffmpeg() -> None:
missing = [name for name in ("ffmpeg", "ffprobe") if shutil.which(name) is None]
if missing:
raise RuntimeError(f"Missing required executable(s): {', '.join(missing)}")
def sha256_file(path: Path, chunk_size: int = 4 * 1024 * 1024) -> str:
digest = hashlib.sha256()
with path.open("rb") as fh:
while chunk := fh.read(chunk_size):
digest.update(chunk)
return digest.hexdigest()
def media_id_for(path: Path, content_sha256: str) -> str:
return hashlib.sha256(f"file:{content_sha256}".encode()).hexdigest()[:32]
def extract_frames(path: Path, out_dir: Path, interval_seconds: float) -> list[Path]:
require_ffmpeg()
out_dir.mkdir(parents=True, exist_ok=True)
for old in out_dir.glob("frame_*.jpg"):
old.unlink()
output = out_dir / "frame_%06d.jpg"
cmd = [
"ffmpeg", "-hide_banner", "-loglevel", "error", "-y",
"-i", str(path),
"-vf", f"fps=1/{interval_seconds}",
"-q:v", "3",
str(output),
]
subprocess.run(cmd, check=True)
return sorted(out_dir.glob("frame_*.jpg"))