feat(quality): re-port AAC opt-in tier (#886) onto ranked-targets

The 2.7.4 merge replaced the bucket-based quality filter with core.quality.model
and silently dropped #886's AAC handling. Restore it in the new model:

- v2_qualities_to_ranked_targets maps an enabled 'aac' tier to a format-only
  target (slskd AAC rarely carries a bitrate, so no min_bitrate gate); priority
  order (above MP3, below FLAC) comes from the caller's sort.
- soulseek filter_results_by_quality_preference drops AAC/.m4a candidates when
  the profile has no AAC target, so AAC stays OFF-by-default instead of slipping
  through via fallback.
- the three factory presets carry the legacy qualities dict again with the
  disabled aac tier (used by the settings UI + the #886 opt-in toggle).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
dev 2026-06-24 21:33:53 +02:00
parent 8934c6418c
commit 13bfdaeda2
3 changed files with 33 additions and 0 deletions

View file

@ -233,6 +233,11 @@ def v2_qualities_to_ranked_targets(qualities: dict) -> List[dict]:
'mp3_320': {'format': 'mp3', 'min_bitrate': 320},
'mp3_256': {'format': 'mp3', 'min_bitrate': 256},
'mp3_192': {'format': 'mp3', 'min_bitrate': 192},
# AAC (#886): opt-in tier. Match on format alone — Soulseek AAC/.m4a
# rarely carries a bitrate attribute, so a min_bitrate gate would
# reject every bitrate-less AAC. Priority order (above MP3, below FLAC)
# is preserved by the caller's priority sort, not by min_bitrate.
'aac': {'format': 'aac'},
}
enabled = [
(cfg.get('priority', 999), name, cfg)

View file

@ -2048,6 +2048,16 @@ class SoulseekClient(DownloadSourcePlugin):
targets = [QualityTarget.from_dict(t) for t in (raw_targets or [])]
fallback_enabled = profile.get('fallback_enabled', True)
# AAC (#886) is an opt-in tier: keep AAC/.m4a candidates ONLY when the
# active profile explicitly targets AAC. Otherwise drop them BEFORE
# ranking — without this, fallback_enabled would hand back AAC files the
# user never asked for (pre-#886 such files landed in the dropped
# 'other' bucket and were never returned).
if not any((t.format or '').lower() == 'aac' for t in targets):
results = [r for r in results if (r.audio_quality.format or '').lower() != 'aac']
if not results:
return []
logger.debug(
"Quality Filter: profile='%s', %d targets, %d candidates",
profile.get('preset', 'custom'), len(targets), len(results),

View file

@ -8907,18 +8907,36 @@ class MusicDatabase:
{"label": "MP3 256kbps", "format": "mp3", "min_bitrate": 256},
{"label": "MP3 192kbps", "format": "mp3", "min_bitrate": 192},
]
# Legacy v2 ``qualities`` dict carried alongside ranked_targets for
# backwards compat — read by the settings UI and the #886 AAC opt-in
# toggle. AAC ships OFF in every preset; its priority sits it above MP3
# but below FLAC (space_saver puts it at 0.5, still above its MP3 tiers).
def _quals(*, flac_en, flac_prio, mp3_320_en, mp3_256_en, mp3_192_en,
mp3_320_prio=2, mp3_256_prio=3, mp3_192_prio=4, aac_prio=1.5):
return {
"flac": {"enabled": flac_en, "min_kbps": 500, "max_kbps": 10000, "priority": flac_prio, "bit_depth": "any"},
"mp3_320": {"enabled": mp3_320_en, "min_kbps": 280, "max_kbps": 500, "priority": mp3_320_prio},
"mp3_256": {"enabled": mp3_256_en, "min_kbps": 200, "max_kbps": 400, "priority": mp3_256_prio},
"mp3_192": {"enabled": mp3_192_en, "min_kbps": 150, "max_kbps": 300, "priority": mp3_192_prio},
"aac": {"enabled": False, "min_kbps": 128, "max_kbps": 400, "priority": aac_prio},
}
presets = {
"audiophile": {
"version": 3, "preset": "audiophile", "fallback_enabled": False,
"ranked_targets": _FLAC_24BIT_TARGETS,
"qualities": _quals(flac_en=True, flac_prio=1, mp3_320_en=False, mp3_256_en=False, mp3_192_en=False),
},
"balanced": {
"version": 3, "preset": "balanced", "fallback_enabled": True,
"ranked_targets": _FLAC_HI_RES_TARGETS + _MP3_TARGETS,
"qualities": _quals(flac_en=True, flac_prio=1, mp3_320_en=True, mp3_256_en=True, mp3_192_en=False),
},
"space_saver": {
"version": 3, "preset": "space_saver", "fallback_enabled": True,
"ranked_targets": _MP3_TARGETS,
"qualities": _quals(flac_en=False, flac_prio=4, mp3_320_en=True, mp3_256_en=True, mp3_192_en=True,
mp3_320_prio=1, mp3_256_prio=2, mp3_192_prio=3, aac_prio=0.5),
},
}