fix(hifi): decline 30s preview manifests, fall through to a real source

Some Monochrome instances only have 30-second Tidal DOWNLOAD access: the HLS
variant playlist for a 220s track comes back as ~30s of segments + ENDLIST
(verified live on us-west.monochrome.tf — lossless=30s, hires=403). The client
downloaded that 30s file, which then got quarantined by the new audio guard.

Detect it at manifest time: sum the playlist's EXTINF runtime and compare to
the track's real duration (get_track_info). When the playlist is < 85% of the
track, decline the manifest and rotate the instance, so the download falls
through to a real source (Soulseek/Qobuz/Tidal/Deezer) instead of fetching a
preview. Best-effort — unknown duration disables the check (the post-download
audio guard remains the safety net).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
dev 2026-06-14 16:42:25 +02:00
parent b6be680d23
commit 975cf4cf3d
2 changed files with 122 additions and 2 deletions

View file

@ -40,6 +40,30 @@ from core.quality.source_map import quality_from_tidal_tier, quality_tier_for_so
logger = get_logger("hifi_client")
# A media playlist whose total runtime is below this fraction of the track's
# real duration is a preview (some Monochrome instances only have 30s Tidal
# DOWNLOAD access — a 220s track comes back as ~30s of segments + ENDLIST).
_PREVIEW_DURATION_RATIO = 0.85
_EXTINF_RE = re.compile(r'#EXTINF:\s*([0-9.]+)')
def hls_total_seconds(playlist_text: str) -> float:
"""Sum the ``#EXTINF`` segment durations in an HLS media playlist."""
return sum(float(x) for x in _EXTINF_RE.findall(playlist_text or ''))
def is_preview_playlist(playlist_s: float, track_s: float,
ratio: float = _PREVIEW_DURATION_RATIO) -> bool:
"""True when the playlist runtime is far shorter than the track's real
duration (a preview). Returns False when either duration is unknown, so a
missing reference never false-positives the post-download audio guard is
the safety net.
"""
if not playlist_s or not track_s or track_s <= 0:
return False
return playlist_s < track_s * ratio
# HLS quality presets mapping to /trackManifests/ format parameters
HLS_QUALITY_MAP = {
'hires': {
@ -623,7 +647,8 @@ class HiFiClient(DownloadSourcePlugin):
return init_uri, segment_uris
def _get_hls_manifest(self, track_id: int, quality: str = 'lossless') -> Optional[Dict]:
def _get_hls_manifest(self, track_id: int, quality: str = 'lossless',
expected_duration_s: float = 0) -> Optional[Dict]:
q_info = HLS_QUALITY_MAP.get(quality, HLS_QUALITY_MAP['lossless'])
formats = q_info['formats']
@ -663,6 +688,7 @@ class HiFiClient(DownloadSourcePlugin):
logger.warning(f"Failed to parse HLS playlist for track {track_id}: {e}")
return None
media_text = playlist_text
if '#EXT-X-STREAM-INF' in playlist_text and segment_uris:
playlist_uri = segment_uris[0]
try:
@ -670,11 +696,26 @@ class HiFiClient(DownloadSourcePlugin):
variant_resp = self.session.get(playlist_uri, allow_redirects=True, timeout=30)
variant_resp.raise_for_status()
variant_text = variant_resp.text
media_text = variant_text
init_uri, segment_uris = self._parse_hls_playlist(variant_text, playlist_uri)
except Exception as e:
logger.warning(f"Failed to fetch variant playlist for track {track_id}: {e}")
return None
# Preview detection — some instances only have 30s Tidal DOWNLOAD
# access, returning a playlist far shorter than the real track. Decline
# it (and rotate the instance) so the orchestrator falls through to a
# real source instead of fetching a 30s file that gets quarantined.
playlist_s = hls_total_seconds(media_text)
if is_preview_playlist(playlist_s, expected_duration_s):
logger.warning(
f"HiFi manifest for track {track_id} ({quality}) is a "
f"{playlist_s:.0f}s preview of a {expected_duration_s:.0f}s track — "
f"declining this instance"
)
self._rotate_instance(self._current_instance)
return None
if init_uri:
logger.info(f"HiFi HLS manifest for track {track_id}: "
f"init segment + {len(segment_uris)} segments ({quality})")
@ -842,12 +883,21 @@ class HiFiClient(DownloadSourcePlugin):
MIN_AUDIO_SIZE = 100 * 1024
# Real track duration — lets _get_hls_manifest reject 30s preview
# manifests before we download them. Best-effort: 0 disables the check.
try:
_info = self.get_track_info(track_id) or {}
expected_duration_s = float(_info.get('duration_s') or 0)
except Exception:
expected_duration_s = 0
for q_key in chain:
if self.shutdown_check and self.shutdown_check():
logger.info("Shutdown detected, aborting HiFi download")
return None
manifest_info = self._get_hls_manifest(track_id, quality=q_key)
manifest_info = self._get_hls_manifest(track_id, quality=q_key,
expected_duration_s=expected_duration_s)
if (
not manifest_info
or (

View file

@ -0,0 +1,70 @@
"""HiFi/Monochrome preview detection.
Some Monochrome instances only have 30-second Tidal preview access for
usage=DOWNLOAD: the HLS variant playlist for a 220s track contains only ~30s
of segments (with #EXT-X-ENDLIST). Detect that at manifest time so HiFi
declines and the orchestrator falls through to a real source instead of
downloading the 30s file and quarantining it after the fact.
"""
import sys
import types
import pytest
# hifi_client imports config/db at module load; stub the heavy bits if needed.
from core.hifi_client import hls_total_seconds, is_preview_playlist
_PREVIEW_PLAYLIST = """#EXTM3U
#EXT-X-VERSION:6
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:4
#EXTINF:3.994,
seg1.mp4
#EXTINF:3.994,
seg2.mp4
#EXTINF:3.994,
seg3.mp4
#EXTINF:3.994,
seg4.mp4
#EXTINF:3.994,
seg5.mp4
#EXTINF:3.994,
seg6.mp4
#EXTINF:3.994,
seg7.mp4
#EXTINF:1.9,
seg8.mp4
#EXT-X-ENDLIST
"""
def test_total_seconds_sums_extinf():
assert hls_total_seconds(_PREVIEW_PLAYLIST) == pytest.approx(29.86, abs=0.1)
def test_total_seconds_empty():
assert hls_total_seconds("") == 0.0
assert hls_total_seconds("#EXTM3U\nno segments\n") == 0.0
def test_preview_when_playlist_far_shorter_than_track():
# 30s playlist for a 220s track → preview.
assert is_preview_playlist(playlist_s=29.9, track_s=220) is True
def test_not_preview_when_playlist_matches_track():
assert is_preview_playlist(playlist_s=218.0, track_s=220) is False
def test_not_preview_when_track_duration_unknown():
# No reference → can't decide; don't false-positive (post-download guard
# is the safety net).
assert is_preview_playlist(playlist_s=29.9, track_s=0) is False
assert is_preview_playlist(playlist_s=0, track_s=220) is False
def test_short_real_track_not_flagged():
# A genuinely ~30s track whose playlist is ~30s is not a preview.
assert is_preview_playlist(playlist_s=29.0, track_s=31) is False