"""The editor's crop lands on the part of the picture the frame was drawn on, also when a video's pixels are not square. The browser reports such a video by its shown size (320 stored, 640 shown), and ffmpeg's `crop` counts stored pixels: measured on a real clip, a frame over the middle half kept half the frame's red surroundings. So a crop carries the size it was measured on. Real ffmpeg: the pixels of the output are the oracle. """ import shutil import subprocess from pathlib import Path import pytest from app.config import settings from app.downloads import edit, pipeline, service, storage from app.downloads import upload as uploadmod from app.downloads.models import DownloadJob, MediaAsset pytestmark = pytest.mark.skipif(shutil.which("ffmpeg") is None, reason="ffmpeg not available") def _source(path: Path, size: str, box: str, sar: int) -> None: """Red, with the picture's middle half green: a crop of that middle holds no red.""" subprocess.run( [ "ffmpeg", "-y", "-loglevel", "error", "-f", "lavfi", "-i", f"color=c=red:s={size}:d=2:r=10", "-f", "lavfi", "-i", "sine=duration=2", "-vf", f"drawbox={box}:c=lime:t=fill,setsar={sar}", "-c:v", "libx264", "-pix_fmt", "yuv420p", "-c:a", "aac", str(path), ], check=True, ) # fmt: skip def _red_and_green(path: Path) -> tuple[int, int]: raw = subprocess.run( ["ffmpeg", "-loglevel", "error", "-i", str(path), "-frames:v", "1", "-f", "rawvideo", "-pix_fmt", "rgb24", "-"], check=True, capture_output=True, ).stdout # fmt: skip red = green = 0 for i in range(0, len(raw) - 2, 3): if raw[i] > 180 and raw[i + 1] < 80: red += 1 elif raw[i + 1] > 180 and raw[i] < 80: green += 1 return red, green def _cut(db, make_user, tmp_path, src: Path, crop: dict) -> Path: user = make_user() source = uploadmod.ingest_upload( db, user.id, filename="clip.mp4", src_path=src, size=src.stat().st_size ) assert source.status == "done" job = service.enqueue_edit(db, user.id, source, {"crop": crop}) asset = db.get(MediaAsset, job.asset_id) job.status, asset.status = "running", "downloading" # the worker's claim db.commit() pipeline._process_edit(job.id, asset.id) db.expire_all() job, asset = db.get(DownloadJob, job.id), db.get(MediaAsset, job.asset_id) assert (job.status, job.error, asset.status) == ("done", None, "ready") return storage.abs_path(str(tmp_path), asset.rel_path) def test_a_crop_of_a_video_with_wide_pixels_keeps_what_the_frame_showed( db, make_user, tmp_path, monkeypatch ): monkeypatch.setattr(settings, "files_root", str(tmp_path)) src = tmp_path / "in.mp4" _source(src, "320x240", "x=80:y=60:w=160:h=120", sar=2) # What the editor sends for the middle half of this video: the browser shows it at 640 x 240. final = _cut( db, make_user, tmp_path, src, {"x": 160, "y": 60, "w": 320, "h": 120, "frame_w": 640, "frame_h": 240}, ) # fmt: skip red, green = _red_and_green(final) assert (red, green) == (0, 160 * 120) def test_a_crop_of_square_pixels_is_cut_where_it_was_drawn(db, make_user, tmp_path, monkeypatch): monkeypatch.setattr(settings, "files_root", str(tmp_path)) src = tmp_path / "in.mp4" _source(src, "480x640", "x=120:y=160:w=240:h=320", sar=1) final = _cut( db, make_user, tmp_path, src, {"x": 120, "y": 160, "w": 240, "h": 320, "frame_w": 480, "frame_h": 640}, ) # fmt: skip assert _red_and_green(final) == (0, 240 * 320) def test_a_crop_stored_before_it_carried_its_frame_is_cut_as_it_always_was(): spec = edit.normalize_edit_spec({"crop": {"x": 4, "y": 6, "w": 100, "h": 50}}) assert spec["crop"] == {"x": 4, "y": 6, "w": 100, "h": 50} cmd = edit.build_edit_cmd(Path("in.mp4"), Path("out.mp4"), spec, "mp4") assert cmd[cmd.index("-vf") + 1].startswith("crop=100:50:4:6,") @pytest.mark.parametrize("frame", [{"frame_w": 0, "frame_h": 240}, {"frame_w": "x", "frame_h": 2}]) def test_a_frame_that_cannot_be_read_is_not_divided_by(frame): spec = edit.normalize_edit_spec({"crop": {"x": 4, "y": 6, "w": 100, "h": 50, **frame}}) assert spec["crop"] == {"x": 4, "y": 6, "w": 100, "h": 50}