Integrity check: don't quarantine valid streamed FLAC as 'zero-length' (#756)
HiFi assembles FLAC from HLS segments and demuxes with `ffmpeg -c copy`, which preserves total_samples=0 in the STREAMINFO of Tidal's fragmented/ streamed FLAC. Every audio frame is present and the file plays fine, but mutagen computes length = total_samples / sample_rate = 0, so the integrity check rejected it as 'zero-length audio' and quarantined nearly every HiFi download. Users confirmed the quarantined files play normally once restored. Length 0 is not proof of corruption at that point: the file already passed the size gate, was identified as a real audio format, and has a valid info block — a genuinely empty/truncated/stub file fails one of those earlier checks instead. Treat length 0 as 'length unknown': accept the file and skip the duration cross-check we can't perform without a length. mutagen never decoded/validated frame data anyway, so this doesn't weaken real corruption detection — size, parse, format, info-block, and duration-drift guards all remain. Tests: a large valid-parse length-0 file (streamed-FLAC signature) is now accepted; a tiny length-0 stub still fails (size gate fires first).
This commit is contained in:
parent
2824c25ec6
commit
d6f37f9667
2 changed files with 68 additions and 4 deletions
|
|
@ -183,11 +183,29 @@ def check_audio_integrity(
|
|||
checks["actual_length_s"] = actual_length_s
|
||||
|
||||
if actual_length_s <= 0:
|
||||
# Length 0 is NOT proof of corruption here: the file already passed the
|
||||
# size gate, was identified as a real audio format, and has a valid
|
||||
# info block. A genuinely empty/truncated/stub file fails one of those
|
||||
# earlier checks instead. The real cause of a clean-but-zero-length
|
||||
# parse is "length unknown" — fragmented / streamed FLAC carries
|
||||
# total_samples=0 in its STREAMINFO even though every audio frame is
|
||||
# present and the file plays fine. HiFi is the common trigger: it
|
||||
# assembles FLAC from HLS segments and demuxes with `ffmpeg -c copy`,
|
||||
# which preserves total_samples=0, so mutagen computes length 0 and the
|
||||
# file was wrongly quarantined (#756). Treat it as unknown length:
|
||||
# accept the file and skip the duration cross-check we can't perform
|
||||
# without a length. mutagen never decoded/validated frame data anyway,
|
||||
# so accepting here doesn't weaken real corruption detection.
|
||||
logger.warning(
|
||||
"[Integrity] %s parsed cleanly (%d bytes, format=%s) but reports "
|
||||
"length 0 — treating as unknown length (likely streamed/fragmented "
|
||||
"FLAC), not rejecting",
|
||||
os.path.basename(file_path), size, type(audio).__name__,
|
||||
)
|
||||
return IntegrityResult(
|
||||
ok=False,
|
||||
reason="Mutagen reports zero-length audio — file has no playable "
|
||||
"audio data",
|
||||
checks={**checks, "mutagen_parse": "zero_length"},
|
||||
ok=True,
|
||||
checks={**checks, "mutagen_parse": "zero_length_unknown",
|
||||
"length_check": "skipped_unknown_length"},
|
||||
)
|
||||
|
||||
# --- Check 3: duration agreement (optional) ---
|
||||
|
|
|
|||
|
|
@ -124,6 +124,52 @@ def test_accepts_valid_wav_with_no_expected_duration(tmp_path: Path) -> None:
|
|||
assert result.checks["length_check"] == "skipped"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Zero-length-but-valid parse (#756): streamed / fragmented FLAC
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_accepts_zero_length_when_parse_is_otherwise_valid(tmp_path: Path, monkeypatch) -> None:
|
||||
"""#756: HiFi assembles FLAC from HLS segments and demuxes with
|
||||
`ffmpeg -c copy`, leaving STREAMINFO total_samples=0. Mutagen then
|
||||
reports length 0 even though every audio frame is present and the file
|
||||
plays fine. Such a file — large, identifiable format, valid info block —
|
||||
must be ACCEPTED (unknown length), not quarantined as 'zero-length'."""
|
||||
import mutagen
|
||||
|
||||
f = tmp_path / "streamed.flac"
|
||||
f.write_bytes(b"\x00" * (50 * 1024)) # clears size gate; bytes irrelevant (mutagen mocked)
|
||||
|
||||
# Valid parse, valid info block, but length 0 — the streamed-FLAC signature.
|
||||
fake_audio = SimpleNamespace(info=SimpleNamespace(length=0))
|
||||
monkeypatch.setattr(mutagen, "File", lambda *a, **k: fake_audio)
|
||||
|
||||
# Even with an expected duration present, it must accept (can't compare to
|
||||
# an unknown length) rather than reject.
|
||||
result = file_integrity.check_audio_integrity(str(f), expected_duration_ms=200_000)
|
||||
|
||||
assert result.ok is True
|
||||
assert result.checks["mutagen_parse"] == "zero_length_unknown"
|
||||
assert result.checks["length_check"] == "skipped_unknown_length"
|
||||
|
||||
|
||||
def test_zero_length_still_rejects_when_too_small(tmp_path: Path, monkeypatch) -> None:
|
||||
"""A genuinely empty/stub file with length 0 must still fail — the size
|
||||
gate fires before parse, so the relaxation can't let real stubs through."""
|
||||
import mutagen
|
||||
|
||||
f = tmp_path / "stub.flac"
|
||||
f.write_bytes(b"\x00" * 200) # below the 10KB size gate
|
||||
|
||||
fake_audio = SimpleNamespace(info=SimpleNamespace(length=0))
|
||||
monkeypatch.setattr(mutagen, "File", lambda *a, **k: fake_audio)
|
||||
|
||||
result = file_integrity.check_audio_integrity(str(f))
|
||||
|
||||
assert result.ok is False
|
||||
assert "too small" in result.reason.lower()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Duration agreement check
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Reference in a new issue