diff --git a/worker/lyra_worker/adapters/_slskd.py b/worker/lyra_worker/adapters/_slskd.py index 6b870fa..85efba2 100644 --- a/worker/lyra_worker/adapters/_slskd.py +++ b/worker/lyra_worker/adapters/_slskd.py @@ -70,7 +70,7 @@ def _keep_matching(responses: list, needle: str) -> list: 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 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 @@ -104,7 +104,14 @@ def _parse_search_responses(responses: list) -> list[dict]: est_seconds = total_bytes / (speed if speed > 0 else 1) lossless = all(f["ext"] in _LOSSLESS_EXT for f in dfiles) 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)) else: guess_artist, guess_album = "", dirname @@ -194,7 +201,7 @@ class SlskdClient: if alt: responses = alt 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: ref = json.loads(source_ref) diff --git a/worker/tests/test_slskd_cancel.py b/worker/tests/test_slskd_cancel.py index c30cc4a..fcc804c 100644 --- a/worker/tests/test_slskd_cancel.py +++ b/worker/tests/test_slskd_cancel.py @@ -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 +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(): # 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