Browse Source

fix: validate ffprobe output structure

master
Yutsuo 4 days ago
parent
commit
2040839c8f
  1. 16
      src/voice_transcriptor/services/media_probe.py
  2. 6
      tests/test_media_probe.py

16
src/voice_transcriptor/services/media_probe.py

@ -50,18 +50,28 @@ def validate_media_path(path: Path) -> Path:
def parse_probe_output(path: Path, size_bytes: int, payload: str) -> MediaInfo: def parse_probe_output(path: Path, size_bytes: int, payload: str) -> MediaInfo:
try: try:
data: dict[str, Any] = json.loads(payload) data: Any = json.loads(payload)
except (json.JSONDecodeError, TypeError) as exc: except (json.JSONDecodeError, TypeError) as exc:
raise ProbeOutputError("FFprobe returned invalid data.") from exc raise ProbeOutputError("FFprobe returned invalid data.") from exc
if not isinstance(data, dict):
raise ProbeOutputError("FFprobe returned invalid data.")
format_data = data.get("format", {})
streams = data.get("streams", [])
if (
not isinstance(format_data, dict)
or not isinstance(streams, list)
or not all(isinstance(stream, dict) for stream in streams)
):
raise ProbeOutputError("FFprobe returned invalid data.")
duration: float | None = None duration: float | None = None
raw_duration = data.get("format", {}).get("duration") raw_duration = format_data.get("duration")
try: try:
if raw_duration is not None: if raw_duration is not None:
duration = float(raw_duration) duration = float(raw_duration)
except (TypeError, ValueError): except (TypeError, ValueError):
duration = None duration = None
codec = next( codec = next(
(stream.get("codec_name") for stream in data.get("streams", []) if stream.get("codec_type") == "audio"), (stream.get("codec_name") for stream in streams if stream.get("codec_type") == "audio"),
None, None,
) )
return MediaInfo(path=path, size_bytes=size_bytes, duration_seconds=duration, audio_codec=codec) return MediaInfo(path=path, size_bytes=size_bytes, duration_seconds=duration, audio_codec=codec)

6
tests/test_media_probe.py

@ -69,6 +69,12 @@ def test_rejects_malformed_probe_json(tmp_path: Path) -> None:
parse_probe_output(tmp_path / "bad.mov", 0, "not json") parse_probe_output(tmp_path / "bad.mov", 0, "not json")
@pytest.mark.parametrize("payload", ["[]", '{"format": []}', '{"streams": [null]}'])
def test_rejects_structurally_invalid_probe_json(tmp_path: Path, payload: str) -> None:
with pytest.raises(ProbeOutputError, match="invalid data"):
parse_probe_output(tmp_path / "bad.mov", 0, payload)
def test_rejects_missing_file(tmp_path: Path) -> None: def test_rejects_missing_file(tmp_path: Path) -> None:
with pytest.raises(MediaProbeError, match="does not exist"): with pytest.raises(MediaProbeError, match="does not exist"):
validate_media_path(tmp_path / "missing.mov") validate_media_path(tmp_path / "missing.mov")

Loading…
Cancel
Save