"""Operations without model weights, run for real on synthetic media.""" from __future__ import annotations from pathlib import Path import numpy as np from fastapi.testclient import TestClient from app.media_io import parse_ebur128 from .conftest import AUTH def test_audio_features_on_click_track(client: TestClient, media_root: Path) -> None: response = client.post("/v1/audioFeatures", headers=AUTH, json={"path": f"{media_root}/clicks.wav"}) assert response.status_code == 200, response.text body = response.json() assert body["integratedLufs"] is not None and -60 < body["integratedLufs"] < 0 assert body["truePeakDb"] is not None and body["truePeakDb"] <= 1.0 tempo = body["tempoBpm"] assert tempo is not None and any(abs(tempo - bpm) < 6 for bpm in (60, 120, 240)) beats = body["beatsMs"] assert len(beats) >= 6 gaps = np.diff(beats) assert np.median(gaps) % 500 < 40 or 500 - np.median(gaps) % 500 < 40 # The trailing 1.5 s are silent. assert any(7400 <= s["startMs"] <= 7700 and s["endMs"] >= 9400 for s in body["silences"]), body[ "silences" ] energy = body["energy"] assert energy[0]["tMs"] == 0 and all(0.0 <= e["value"] <= 1.0 for e in energy) assert max(e["value"] for e in energy) == 1.0 assert all(isinstance(t, int) for t in beats) def test_audio_features_without_audio_stream(client: TestClient, media_root: Path) -> None: response = client.post("/v1/audioFeatures", headers=AUTH, json={"path": f"{media_root}/disc.mp4"}) assert response.status_code == 200 assert response.json() == { "integratedLufs": None, "truePeakDb": None, "tempoBpm": None, "beatsMs": [], "energy": [], "silences": [], } def test_parse_ebur128_summary() -> None: stderr = """ [Parsed_ebur128_0 @ 0x0] Summary: Integrated loudness: I: -23.4 LUFS Threshold: -33.6 LUFS True peak: Peak: -1.2 dBFS """ assert parse_ebur128(stderr) == (-23.4, -1.2) assert parse_ebur128("Summary:\n I: -inf LUFS\n Peak: -inf dBFS") == (None, None) def test_scene_detection_finds_hard_cut(client: TestClient, media_root: Path) -> None: response = client.post("/v1/detectScenes", headers=AUTH, json={"path": f"{media_root}/cut.mp4"}) assert response.status_code == 200, response.text body = response.json() shots = body["shots"] assert len(shots) == 2, shots assert abs(shots[1]["startMs"] - 2000) <= 80 assert shots[0]["startMs"] == 0 and abs(shots[-1]["endMs"] - 4000) <= 80 assert all(0.0 <= s["score"] <= 1.0 for s in shots) assert body["scenes"][0]["startMs"] == 0 assert body["scenes"][-1]["endMs"] == shots[-1]["endMs"] def test_saliency_points_at_bright_spot(client: TestClient, media_root: Path) -> None: response = client.post( "/v1/saliency", headers=AUTH, json={"path": f"{media_root}/spot.mp4", "timesMs": [0, 200]} ) assert response.status_code == 200, response.text points = response.json()["points"] assert [p["tMs"] for p in points] == [0, 200] for point in points: assert point["cx"] > 0.6 assert 0.3 < point["cy"] < 0.7 assert 0.0 < point["strength"] <= 1.0