refactor: make adapter .tier the single source of truth for ranking
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,7 @@ class _BaseFake:
|
|||||||
matched_album=target.album,
|
matched_album=target.album,
|
||||||
quality=_QUALITIES.get(self.name, _QUALITIES["youtube"]),
|
quality=_QUALITIES.get(self.name, _QUALITIES["youtube"]),
|
||||||
track_count=tracks,
|
track_count=tracks,
|
||||||
|
source_tier=self.tier,
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ def run_pipeline(
|
|||||||
found: list[Candidate] = []
|
found: list[Candidate] = []
|
||||||
for adapter in adapters:
|
for adapter in adapters:
|
||||||
for c in adapter.search(target):
|
for c in adapter.search(target):
|
||||||
found.append(replace(c, confidence=score_confidence(target, c)))
|
found.append(replace(c, source_tier=adapter.tier, confidence=score_confidence(target, c)))
|
||||||
_persist_candidates(conn, job_id, found)
|
_persist_candidates(conn, job_id, found)
|
||||||
|
|
||||||
# 3. rank
|
# 3. rank
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
from lyra_worker.types import Candidate, Quality
|
from lyra_worker.types import Candidate, Quality
|
||||||
|
|
||||||
_SOURCE_TIERS = {"qobuz": 0, "soulseek": 1, "youtube": 2}
|
|
||||||
|
|
||||||
|
|
||||||
def quality_class(q: Quality) -> int:
|
def quality_class(q: Quality) -> int:
|
||||||
"""3 = hi-res lossless, 2 = CD lossless, 1 = lossy."""
|
"""3 = hi-res lossless, 2 = CD lossless, 1 = lossy."""
|
||||||
@@ -13,11 +11,6 @@ def quality_class(q: Quality) -> int:
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
def source_tier(source: str) -> int:
|
|
||||||
"""Lower is better. Unknown sources rank worst."""
|
|
||||||
return _SOURCE_TIERS.get(source, 99)
|
|
||||||
|
|
||||||
|
|
||||||
def rank_key(c: Candidate) -> tuple[int, int]:
|
def rank_key(c: Candidate) -> tuple[int, int]:
|
||||||
"""Sort key; larger is better. Quality class first, then source tier."""
|
"""Sort key; larger is better. Quality class first, then source tier (from the adapter)."""
|
||||||
return (quality_class(c.quality), -source_tier(c.source))
|
return (quality_class(c.quality), -c.source_tier)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class Candidate:
|
|||||||
matched_album: str
|
matched_album: str
|
||||||
quality: Quality
|
quality: Quality
|
||||||
track_count: int
|
track_count: int
|
||||||
|
source_tier: int = 99
|
||||||
confidence: float = 0.0
|
confidence: float = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
from lyra_worker.quality import quality_class, source_tier, rank_key
|
from lyra_worker.quality import quality_class, rank_key
|
||||||
from lyra_worker.types import Candidate, Quality
|
from lyra_worker.types import Candidate, Quality
|
||||||
|
|
||||||
HIRES = Quality(fmt="FLAC", lossless=True, bit_depth=24, sample_rate=96000)
|
HIRES = Quality(fmt="FLAC", lossless=True, bit_depth=24, sample_rate=96000)
|
||||||
CD = Quality(fmt="FLAC", lossless=True, bit_depth=16, sample_rate=44100)
|
CD = Quality(fmt="FLAC", lossless=True, bit_depth=16, sample_rate=44100)
|
||||||
MP3 = Quality(fmt="MP3", lossless=False, bitrate_kbps=320)
|
MP3 = Quality(fmt="MP3", lossless=False, bitrate_kbps=320)
|
||||||
|
|
||||||
|
_TIERS = {"qobuz": 0, "soulseek": 1, "youtube": 2}
|
||||||
|
|
||||||
|
|
||||||
def _cand(source, quality):
|
def _cand(source, quality):
|
||||||
return Candidate(source=source, source_ref="x", matched_artist="A",
|
return Candidate(source=source, source_ref="x", matched_artist="A",
|
||||||
matched_album="B", quality=quality, track_count=10)
|
matched_album="B", quality=quality, track_count=10,
|
||||||
|
source_tier=_TIERS[source])
|
||||||
|
|
||||||
|
|
||||||
def test_quality_class_tiers():
|
def test_quality_class_tiers():
|
||||||
@@ -17,12 +20,6 @@ def test_quality_class_tiers():
|
|||||||
assert quality_class(MP3) == 1
|
assert quality_class(MP3) == 1
|
||||||
|
|
||||||
|
|
||||||
def test_source_tier_order():
|
|
||||||
assert source_tier("qobuz") == 0
|
|
||||||
assert source_tier("soulseek") == 1
|
|
||||||
assert source_tier("youtube") == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_design_ranking_order():
|
def test_design_ranking_order():
|
||||||
# Qobuz hi-res > Qobuz lossless > Soulseek FLAC > Soulseek MP3 > YouTube
|
# Qobuz hi-res > Qobuz lossless > Soulseek FLAC > Soulseek MP3 > YouTube
|
||||||
ordered = [
|
ordered = [
|
||||||
|
|||||||
@@ -6,11 +6,14 @@ CD = Quality(fmt="FLAC", lossless=True, bit_depth=16, sample_rate=44100)
|
|||||||
MP3 = Quality(fmt="MP3", lossless=False, bitrate_kbps=320)
|
MP3 = Quality(fmt="MP3", lossless=False, bitrate_kbps=320)
|
||||||
TARGET = MBTarget(artist="Radiohead", album="In Rainbows", track_count=10)
|
TARGET = MBTarget(artist="Radiohead", album="In Rainbows", track_count=10)
|
||||||
|
|
||||||
|
_TIERS = {"qobuz": 0, "soulseek": 1, "youtube": 2}
|
||||||
|
|
||||||
|
|
||||||
def _cand(source, quality, artist="Radiohead", album="In Rainbows", tracks=10):
|
def _cand(source, quality, artist="Radiohead", album="In Rainbows", tracks=10):
|
||||||
return Candidate(source=source, source_ref=f"{source}:{quality.fmt}",
|
return Candidate(source=source, source_ref=f"{source}:{quality.fmt}",
|
||||||
matched_artist=artist, matched_album=album,
|
matched_artist=artist, matched_album=album,
|
||||||
quality=quality, track_count=tracks)
|
quality=quality, track_count=tracks,
|
||||||
|
source_tier=_TIERS[source])
|
||||||
|
|
||||||
|
|
||||||
def test_returns_empty_for_no_candidates():
|
def test_returns_empty_for_no_candidates():
|
||||||
|
|||||||
Reference in New Issue
Block a user