diegocade1: DSD files (.dsf, ~500MB DSD64) were labeled "Low Quality" and nagged to upgrade.
two independent causes, both fixed (additive — no existing format/behaviour changed):
1) DSF was an unrecognized format -> bottom 'unknown' tier -> "Low Quality":
- source_map: map .dsf/.dff -> 'dsf' (also lights it up in AUDIO_EXTENSIONS, so Soulseek can
match a DSF if one exists)
- model.tier_score: 'dsf' base 102 (just above FLAC) — lands in the lossless range
- probe_audio_quality: add a DSD branch returning format='dsf' (mutagen.dsf for .dsf detail;
.dff classifies lossless without measured detail) instead of None
- settings UI: DSD in RT_LOSSLESS_FORMATS + a "DSD (DSF / DFF)" option in the profile dropdown
2) the actual cause of the screenshot's findings — the truncation guard falsely called DSF
"broken (only ~12% decodes)": ffmpeg decodes DSD to PCM at a different rate than the DSD
container's 2.8 MHz, so astats samples ÷ container-rate massively under-counts. now
detect_broken_audio skips the truncation check for DSD (silence detection still applies).
8 seam tests: dsf/dff -> 'dsf'; dsf tier in lossless range (with + without measured bitrate);
is_dsd_path; and a contrast pair proving the same 12%-decode numbers flag a .flac but skip a
.dsf. 230 quality/import/silence tests green, ruff + JS integrity clean.
148 lines
4.6 KiB
Python
148 lines
4.6 KiB
Python
"""Tests for core.quality.source_map — each download source's tier/string
|
|
mapped into a unified AudioQuality.
|
|
|
|
These mappers express each source's *claimed* capability tier. The values
|
|
are later verified post-download by the quality guard, so over-claiming is
|
|
the failure mode to avoid: an unknown tier must NOT pretend to be lossless.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from core.quality.source_map import (
|
|
quality_from_tidal_tier,
|
|
quality_from_qobuz,
|
|
quality_from_deezer,
|
|
quality_from_amazon,
|
|
format_from_extension,
|
|
AUDIO_EXTENSIONS,
|
|
)
|
|
|
|
|
|
# ── Shared extension → format (used by every extension-based source) ────────
|
|
|
|
@pytest.mark.parametrize("ext,fmt", [
|
|
("flac", "flac"), (".flac", "flac"),
|
|
("mp3", "mp3"),
|
|
("m4a", "aac"), ("aac", "aac"), ("mp4", "aac"),
|
|
("ogg", "ogg"), ("oga", "ogg"),
|
|
("opus", "opus"),
|
|
("wav", "wav"), ("wave", "wav"),
|
|
("aiff", "wav"), ("aif", "wav"), # PCM → wav tier
|
|
("wma", "wma"),
|
|
("alac", "alac"),
|
|
("dsf", "dsf"), (".dsf", "dsf"), ("dff", "dsf"), # DSD → dsf tier (#939)
|
|
("xyz", "unknown"), ("", "unknown"), (None, "unknown"),
|
|
])
|
|
def test_format_from_extension(ext, fmt):
|
|
assert format_from_extension(ext) == fmt
|
|
|
|
|
|
def test_audio_extensions_cover_all_known_formats():
|
|
for e in (".flac", ".mp3", ".m4a", ".ogg", ".opus", ".wav", ".aiff", ".wma"):
|
|
assert e in AUDIO_EXTENSIONS
|
|
|
|
|
|
# ── Tidal / HiFi (Tidal-backed) ────────────────────────────────────────────
|
|
|
|
@pytest.mark.parametrize("tier", ["HI_RES_LOSSLESS", "HI_RES", "hi_res", "hires", "HIRES"])
|
|
def test_tidal_hires_is_flac_24_96(tier):
|
|
aq = quality_from_tidal_tier(tier)
|
|
assert aq.format == "flac"
|
|
assert aq.bit_depth == 24
|
|
assert aq.sample_rate == 96000
|
|
|
|
|
|
@pytest.mark.parametrize("tier", ["LOSSLESS", "lossless"])
|
|
def test_tidal_lossless_is_flac_16_44(tier):
|
|
aq = quality_from_tidal_tier(tier)
|
|
assert aq.format == "flac"
|
|
assert aq.bit_depth == 16
|
|
assert aq.sample_rate == 44100
|
|
|
|
|
|
def test_tidal_high_is_lossy_aac_320():
|
|
aq = quality_from_tidal_tier("HIGH")
|
|
assert aq.format == "aac"
|
|
assert aq.bitrate == 320
|
|
assert aq.bit_depth is None # lossy: no bit depth
|
|
|
|
|
|
def test_tidal_low_is_lossy_aac_96():
|
|
aq = quality_from_tidal_tier("LOW")
|
|
assert aq.format == "aac"
|
|
assert aq.bitrate == 96
|
|
|
|
|
|
def test_tidal_unknown_tier_does_not_overclaim():
|
|
# An unrecognised tier must not masquerade as lossless.
|
|
aq = quality_from_tidal_tier("SOMETHING_NEW")
|
|
assert aq.format == "unknown"
|
|
assert aq.bit_depth is None
|
|
assert aq.sample_rate is None
|
|
|
|
|
|
# ── Qobuz (real API values) ────────────────────────────────────────────────
|
|
|
|
def test_qobuz_hires_khz_to_hz():
|
|
aq = quality_from_qobuz(96.0, 24)
|
|
assert aq.format == "flac"
|
|
assert aq.sample_rate == 96000
|
|
assert aq.bit_depth == 24
|
|
|
|
|
|
def test_qobuz_cd_quality_fractional_khz():
|
|
aq = quality_from_qobuz(44.1, 16)
|
|
assert aq.format == "flac"
|
|
assert aq.sample_rate == 44100
|
|
assert aq.bit_depth == 16
|
|
|
|
|
|
def test_qobuz_192k():
|
|
aq = quality_from_qobuz(192.0, 24)
|
|
assert aq.sample_rate == 192000
|
|
assert aq.bit_depth == 24
|
|
|
|
|
|
# ── Deezer (config code) ───────────────────────────────────────────────────
|
|
|
|
def test_deezer_flac_is_16_44():
|
|
aq = quality_from_deezer("flac")
|
|
assert aq.format == "flac"
|
|
assert aq.bit_depth == 16
|
|
assert aq.sample_rate == 44100
|
|
|
|
|
|
def test_deezer_mp3_320():
|
|
aq = quality_from_deezer("mp3_320")
|
|
assert aq.format == "mp3"
|
|
assert aq.bitrate == 320
|
|
assert aq.bit_depth is None
|
|
|
|
|
|
def test_deezer_mp3_128():
|
|
aq = quality_from_deezer("mp3_128")
|
|
assert aq.format == "mp3"
|
|
assert aq.bitrate == 128
|
|
|
|
|
|
# ── Amazon (real sampleRate preferred, tier fallback) ──────────────────────
|
|
|
|
def test_amazon_prefers_real_sample_rate():
|
|
aq = quality_from_amazon("HD", sample_rate=88200, bit_depth=24)
|
|
assert aq.format == "flac"
|
|
assert aq.sample_rate == 88200
|
|
assert aq.bit_depth == 24
|
|
|
|
|
|
def test_amazon_hd_tier_fallback():
|
|
aq = quality_from_amazon("HD")
|
|
assert aq.format == "flac"
|
|
assert aq.sample_rate == 44100
|
|
assert aq.bit_depth == 16
|
|
|
|
|
|
def test_amazon_uhd_tier_fallback():
|
|
aq = quality_from_amazon("UHD")
|
|
assert aq.format == "flac"
|
|
assert aq.bit_depth == 24
|
|
assert aq.sample_rate == 96000
|