Files
claude-video/tests/conftest.py
T

84 lines
2.7 KiB
Python
Raw Normal View History

"""Shared pytest fixtures: ffmpeg-synthesized clips and scripts/ on sys.path."""
from __future__ import annotations
import subprocess
import sys
from pathlib import Path
import pytest
# Make the bundled scripts importable (mirrors watch.py's sys.path insert).
SCRIPTS_DIR = Path(__file__).resolve().parent.parent / "skills" / "watch" / "scripts"
sys.path.insert(0, str(SCRIPTS_DIR))
# 14 visually distinct fills → 14 abrupt cuts → x264 emits a keyframe per cut.
COLORS = [
"red", "green", "blue", "white", "black", "yellow", "cyan",
"magenta", "gray", "orange", "purple", "brown", "navy", "olive",
]
def _run(cmd: list[str]) -> None:
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
raise RuntimeError(f"ffmpeg failed: {' '.join(cmd)}\n{result.stderr}")
def build_cut_clip(
path: Path,
n: int = 14,
seg: float = 0.4,
size: str = "320x240",
fps: int = 10,
) -> None:
"""Concatenate ``n`` solid-color segments into one clip with ``n`` cuts.
Each color change is a hard scene cut, so the scene selector finds ~n-1
changes. x264's own scenecut detection is unreliable on flat fills, so we
force a keyframe at every ``seg`` boundary — giving ~n real keyframes for
the keyframe engine to find.
"""
inputs: list[str] = []
for i in range(n):
color = COLORS[i % len(COLORS)]
inputs += ["-f", "lavfi", "-t", str(seg), "-i", f"color=c={color}:s={size}:r={fps}"]
streams = "".join(f"[{i}:v]" for i in range(n))
filt = f"{streams}concat=n={n}:v=1:a=0[out]"
_run([
"ffmpeg", "-hide_banner", "-loglevel", "error", "-y",
*inputs,
"-filter_complex", filt, "-map", "[out]",
"-c:v", "libx264", "-pix_fmt", "yuv420p",
"-force_key_frames", f"expr:gte(t,n_forced*{seg})",
str(path),
])
def build_static_clip(
path: Path,
duration: float = 3.0,
size: str = "320x240",
fps: int = 10,
) -> None:
"""One solid color: 1 keyframe, no scene changes → triggers both fallbacks."""
_run([
"ffmpeg", "-hide_banner", "-loglevel", "error", "-y",
"-f", "lavfi", "-t", str(duration), "-i", f"color=c=blue:s={size}:r={fps}",
"-c:v", "libx264", "-pix_fmt", "yuv420p", "-g", "600",
str(path),
])
@pytest.fixture(scope="session")
def cut_clip(tmp_path_factory: pytest.TempPathFactory) -> Path:
path = tmp_path_factory.mktemp("clips") / "cuts.mp4"
build_cut_clip(path)
return path
@pytest.fixture(scope="session")
def static_clip(tmp_path_factory: pytest.TempPathFactory) -> Path:
path = tmp_path_factory.mktemp("clips") / "static.mp4"
build_static_clip(path)
return path