import shutil import subprocess import pytest from PIL import Image from app.media.converter import analyze, converted_frames VIDEO_CASES = [ ("mp4", "mpeg4"), ("mov", "mpeg4"), ("mkv", "ffv1"), ("avi", "mpeg4"), ("webm", "libvpx-vp9"), ] @pytest.mark.skipif(not shutil.which("ffmpeg"), reason="FFmpeg is not installed") @pytest.mark.parametrize(("extension", "codec"), VIDEO_CASES) def test_common_video_containers_probe_sample_and_merge_identical_frames(tmp_path, extension, codec): source = tmp_path / ("constant." + extension) result = subprocess.run([ "ffmpeg", "-nostdin", "-v", "error", "-f", "lavfi", "-i", "color=c=red:s=96x54:r=30:d=0.3", "-an", "-c:v", codec, "-y", str(source), ], stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, check=False, timeout=30) if result.returncode: pytest.skip(codec + " encoder unavailable") metadata = analyze(source, tmp_path / ("previews-" + extension)) frames = list(converted_frames(source, metadata, { "fit_mode": "contain", "center_x": 0.5, "center_y": 0.5, "zoom": 1, "transparency_color": "#000000", "padding_color": "#010203", })) assert metadata["decoder"] == "ffmpeg" assert metadata["dynamic"] is True assert 250 <= metadata["duration_ms"] <= 350 assert len(frames) == 1 assert frames[0][1] >= 250 assert len(frames[0][0]) == 64 * 64 * 3 @pytest.mark.skipif(not shutil.which("ffmpeg"), reason="FFmpeg is not installed") def test_ffmpeg_free_crop_keeps_the_complete_wide_frame_and_void_padding(tmp_path): source = tmp_path / "wide.mkv" result = subprocess.run([ "ffmpeg", "-nostdin", "-v", "error", "-f", "lavfi", "-i", "color=c=red:s=96x48:r=20:d=0.1", "-an", "-c:v", "ffv1", "-y", str(source), ], stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, check=False, timeout=30) if result.returncode: pytest.skip("ffv1 encoder unavailable") metadata = analyze(source, tmp_path / "wide-previews") frames = list(converted_frames(source, metadata, { "fit_mode": "crop", "center_x": 0.5, "center_y": 0.5, "zoom": 1, "transparency_color": "#00FF00", "padding_color": "#0000FF", })) output = Image.frombytes("RGB", (64, 64), frames[0][0]) assert output.getpixel((32, 0)) == (0, 0, 255) assert output.getpixel((32, 32))[0] > 240 assert output.getpixel((32, 63)) == (0, 0, 255) @pytest.mark.skipif(not shutil.which("heif-enc"), reason="libheif examples are not installed") def test_heic_uses_local_libheif_decoder(tmp_path): png = tmp_path / "source.png" source = tmp_path / "source.heic" Image.new("RGB", (32, 20), (4, 5, 6)).save(png) result = subprocess.run( ["heif-enc", "-q", "100", str(png), "-o", str(source)], stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, check=False, timeout=30, ) if result.returncode: pytest.skip("HEIC encoder unavailable") metadata = analyze(source, tmp_path / "heic-previews") frames = list(converted_frames(source, metadata, { "fit_mode": "stretch", "center_x": 0.5, "center_y": 0.5, "zoom": 1, "transparency_color": "#000000", "padding_color": "#000000", })) assert metadata["decoder"] in {"pillow", "heif"} assert metadata["dynamic"] is False assert len(frames) == 1