fix(worker): trust the requested artist when it's in the file path (stop MP3-over-FLAC)
Confidence scoring read the artist from the immediate folder as "Artist - Album", so
common FLAC rip layouts — artist in a parent folder ("Rihanna\(2009) Rated R\…") or
buried ("2009 - Rihanna - Rated R") — parsed with no/garbage artist and scored below
the 0.7 gate. Real FLACs got filtered out and a clean-named MP3 (0.88) won, so Lyra
downloaded lossy when lossless was available (Rihanna "Rated R": 8 FLACs all <0.5).
_parse_search_responses now takes the requested artist and, when it appears anywhere
in the candidate's path (via _path_matches), sets it as the candidate artist so
confidence reflects reality. Especially helps blocklisted-artist fallbacks, whose
album-only results come from varied folder structures. 337 tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -70,7 +70,7 @@ def _keep_matching(responses: list, needle: str) -> list:
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def _parse_search_responses(responses: list) -> list[dict]:
|
def _parse_search_responses(responses: list, artist: str = "") -> list[dict]:
|
||||||
"""Turn slskd search responses into album candidates (one per peer+directory), ordered so the
|
"""Turn slskd search responses into album candidates (one per peer+directory), ordered so the
|
||||||
peers that will FINISH first come first: a free upload slot, then the shortest estimated
|
peers that will FINISH first come first: a free upload slot, then the shortest estimated
|
||||||
transfer time (album bytes ÷ the peer's advertised speed), then a shorter queue. Ranking on
|
transfer time (album bytes ÷ the peer's advertised speed), then a shorter queue. Ranking on
|
||||||
@@ -104,7 +104,14 @@ def _parse_search_responses(responses: list) -> list[dict]:
|
|||||||
est_seconds = total_bytes / (speed if speed > 0 else 1)
|
est_seconds = total_bytes / (speed if speed > 0 else 1)
|
||||||
lossless = all(f["ext"] in _LOSSLESS_EXT for f in dfiles)
|
lossless = all(f["ext"] in _LOSSLESS_EXT for f in dfiles)
|
||||||
dirname = _basename(directory)
|
dirname = _basename(directory)
|
||||||
if " - " in dirname:
|
# Trust the REQUESTED artist when it appears anywhere in the path — many rips put the
|
||||||
|
# artist in a parent folder ("Rihanna\(2009) Rated R\…") or bury it ("2009 - Rihanna -
|
||||||
|
# Rated R"), which the "Artist - Album" folder parse gets wrong; that tanked the
|
||||||
|
# confidence score and lost real FLACs to clean-named MP3s. album stays the folder name
|
||||||
|
# (fuzzy-matched downstream); artist is what the parse gets wrong.
|
||||||
|
if artist and _path_matches(artist, dfiles[0]["filename"]):
|
||||||
|
guess_artist, guess_album = artist, dirname
|
||||||
|
elif " - " in dirname:
|
||||||
guess_artist, guess_album = (p.strip() for p in dirname.split(" - ", 1))
|
guess_artist, guess_album = (p.strip() for p in dirname.split(" - ", 1))
|
||||||
else:
|
else:
|
||||||
guess_artist, guess_album = "", dirname
|
guess_artist, guess_album = "", dirname
|
||||||
@@ -194,7 +201,7 @@ class SlskdClient:
|
|||||||
if alt:
|
if alt:
|
||||||
responses = alt
|
responses = alt
|
||||||
break
|
break
|
||||||
return _parse_search_responses(responses)
|
return _parse_search_responses(responses, artist)
|
||||||
|
|
||||||
def download(self, source_ref: str, dest: str, on_progress: Callable[[float], None]) -> dict:
|
def download(self, source_ref: str, dest: str, on_progress: Callable[[float], None]) -> dict:
|
||||||
ref = json.loads(source_ref)
|
ref = json.loads(source_ref)
|
||||||
|
|||||||
@@ -110,6 +110,27 @@ def test_parse_ranks_free_and_fast_peers_first():
|
|||||||
assert peers == ["fast", "slow", "queued"] # free+fast first; no-slot peer last despite speed
|
assert peers == ["fast", "slow", "queued"] # free+fast first; no-slot peer last despite speed
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_uses_requested_artist_when_present_in_path():
|
||||||
|
# Real "Rated R" MP3-over-FLAC bug: the artist is in a PARENT folder ("Rihanna\(2009) Rated
|
||||||
|
# R\…"), so the "Artist - Album" folder parse yields no artist and confidence tanks. When the
|
||||||
|
# requested artist is in the path, trust it so the candidate scores as Rihanna.
|
||||||
|
responses = [
|
||||||
|
{"username": "p", "hasFreeUploadSlot": True, "uploadSpeed": 1000, "queueLength": 0,
|
||||||
|
"files": [{"filename": r"@@x\Music\Rihanna\(2009) Rated R\01. Mad House.flac", "size": 5}]},
|
||||||
|
]
|
||||||
|
c = _parse_search_responses(responses, artist="Rihanna")[0]
|
||||||
|
assert c["artist"] == "Rihanna"
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_without_artist_falls_back_to_folder_parse():
|
||||||
|
responses = [
|
||||||
|
{"username": "p", "hasFreeUploadSlot": True, "uploadSpeed": 1000, "queueLength": 0,
|
||||||
|
"files": [{"filename": r"x\Some Band - Some Album\01.flac", "size": 5}]},
|
||||||
|
]
|
||||||
|
c = _parse_search_responses(responses)[0] # no artist arg → folder parse
|
||||||
|
assert c["artist"] == "Some Band"
|
||||||
|
|
||||||
|
|
||||||
def test_parse_prefers_shorter_transfer_over_raw_speed():
|
def test_parse_prefers_shorter_transfer_over_raw_speed():
|
||||||
# The Hybrid Theory case: a peer advertising higher speed but serving a ~2x-larger rip (24-bit
|
# The Hybrid Theory case: a peer advertising higher speed but serving a ~2x-larger rip (24-bit
|
||||||
# / bloated FLAC) should lose to a peer with lower speed but a smaller standard rip that
|
# / bloated FLAC) should lose to a peer with lower speed but a smaller standard rip that
|
||||||
|
|||||||
Reference in New Issue
Block a user