Files
Lyra/worker/tests/test_slskd_search_fallback.py
T
Jonathan 434cd2b22c feat(worker): recover blocklisted Soulseek searches via album/artist-only fallback
The Soulseek server silently drops searches containing certain blocklisted terms
(major-label artists / album titles — Adele, Rihanna, Beyoncé, "Lemonade", …),
returning 0 responses ("TimedOut"). The blocked term poisons the whole query even
though the OTHER field usually isn't blocked and the content is on the network
(e.g. searching "Beyoncé …" → 0, but "I Am… Sasha Fierce" alone → 250 with 136
real Beyoncé folders).

When the primary "{artist} {album}" search returns nothing, retry with each field
alone (normalized to fold accents/punctuation) and keep only results whose file
path still matches the dropped field (_keep_matching), so a same-titled album by a
different artist is never grabbed. Recovers blocklisted albums with a distinctive
title; safely yields nothing when the title is too generic (e.g. Adele "21") rather
than mis-grabbing. Fallback only fires on a 0-response primary, so the happy path is
unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 19:06:07 +02:00

151 lines
5.7 KiB
Python

"""Soulseek search fallback: the Soulseek server drops searches containing blocklisted terms
(major-label artists / album titles) → 0 responses. search_album retries with album-only then
artist-only, keeping only path-matching results so it never grabs a same-titled foreign album."""
import json
import lyra_worker.adapters._slskd as slskd_mod
from lyra_worker.adapters._slskd import (
SlskdClient,
_keep_matching,
_norm,
_path_matches,
)
# --- pure helpers -----------------------------------------------------------------------------
def test_norm_strips_diacritics_and_punctuation():
assert _norm("Beyoncé") == "beyonce"
assert _norm("I Am… Sasha Fierce") == "i am sasha fierce"
assert _norm("AC/DC") == "ac dc"
def test_path_matches_is_accent_and_punctuation_tolerant():
p = r"@@bckfj\All\Beyonce\I Am... Sasha Fierce CD1\03 Halo.flac"
assert _path_matches("Beyoncé", p) # accent folded
assert _path_matches("I Am… Sasha Fierce", p) # ellipsis / punctuation ignored
assert not _path_matches("Rihanna", p)
def test_path_matches_short_needle_requires_whole_string():
assert _path_matches("U2", r"x\U2 - Achtung Baby\01.flac")
assert not _path_matches("U2", r"x\Blur - 13\01.flac")
def test_keep_matching_drops_foreign_and_empty_responses():
responses = [
{"username": "a", "files": [{"filename": r"x\Rihanna - Unapologetic\01.flac"}]},
{"username": "b", "files": [{"filename": r"x\Someone Else - Unapologetic\01.flac"}]},
]
kept = _keep_matching(responses, "Rihanna")
assert [r["username"] for r in kept] == ["a"]
# --- fallback orchestration (HTTP faked) ------------------------------------------------------
class _Resp:
def __init__(self, payload):
self._p = payload
def raise_for_status(self):
pass
def json(self):
return self._p
class _FakeSearch:
"""Routes slskd search HTTP by URL: POST returns an id, GET on the search returns a completed
state, GET on /responses returns the canned responses for that query. Records queries sent."""
def __init__(self, by_query):
self.by_query = by_query # {searchText: [responses]}
self.queries = [] # searchText values POSTed, in order
self._id_query = {}
self._n = 0
def post(self, url, **kw):
text = kw["json"]["searchText"]
self.queries.append(text)
self._n += 1
sid = f"s{self._n}"
self._id_query[sid] = text
return _Resp({"id": sid})
def get(self, url, **kw):
if url.endswith("/responses"):
sid = url.rsplit("/", 2)[-2]
return _Resp(self.by_query.get(self._id_query[sid], []))
return _Resp({"state": "Completed, TimedOut"})
def _client():
return SlskdClient({"slskd.url": "http://x", "slskd.api_key": "k", "slskd.downloadsDir": "/x"})
def _install(monkeypatch, fake):
monkeypatch.setattr(slskd_mod, "requests", fake)
monkeypatch.setattr(slskd_mod.time, "sleep", lambda _s: None)
def test_primary_hit_does_not_trigger_fallback(monkeypatch):
fake = _FakeSearch({
"Rihanna Unapologetic": [
{"username": "a", "hasFreeUploadSlot": True, "uploadSpeed": 1, "queueLength": 0,
"files": [{"filename": r"x\Rihanna - Unapologetic\01.flac", "size": 5}]},
],
})
_install(monkeypatch, fake)
out = _client().search_album("Rihanna", "Unapologetic")
assert len(out) == 1
assert fake.queries == ["Rihanna Unapologetic"] # no fallback searches
def test_album_only_fallback_recovers_blocklisted_artist(monkeypatch):
# Primary (artist blocked) → 0; album-only returns the artist's folder → recovered.
fake = _FakeSearch({
"Beyoncé I Am… Sasha Fierce": [], # blocked
"i am sasha fierce": [
{"username": "a", "hasFreeUploadSlot": True, "uploadSpeed": 1, "queueLength": 0,
"files": [{"filename": r"x\Beyonce - I Am... Sasha Fierce\01.flac", "size": 5}]},
{"username": "b", "hasFreeUploadSlot": True, "uploadSpeed": 1, "queueLength": 0,
"files": [{"filename": r"x\Other Artist - I Am Sasha Fierce\01.flac", "size": 5}]},
],
})
_install(monkeypatch, fake)
out = _client().search_album("Beyoncé", "I Am… Sasha Fierce")
assert fake.queries == ["Beyoncé I Am… Sasha Fierce", "i am sasha fierce"]
peers = [json.loads(c["source_ref"])["username"] for c in out]
assert peers == ["a"] # foreign same-titled album dropped
def test_artist_only_fallback_when_album_title_blocked(monkeypatch):
# Album-only also blocked/empty → fall through to artist-only, filtered by album in path.
fake = _FakeSearch({
"Foo Lemonade": [],
"lemonade": [], # album title itself blocked
"foo": [
{"username": "a", "hasFreeUploadSlot": True, "uploadSpeed": 1, "queueLength": 0,
"files": [{"filename": r"x\Foo - Lemonade\01.flac", "size": 5}]},
],
})
_install(monkeypatch, fake)
out = _client().search_album("Foo", "Lemonade")
assert fake.queries == ["Foo Lemonade", "lemonade", "foo"]
assert len(out) == 1
def test_fallback_yields_nothing_when_title_too_generic(monkeypatch):
# 'Adele 21': artist blocked, album title '21' returns lots of non-Adele content → filtered
# to nothing rather than grabbing a wrong album.
fake = _FakeSearch({
"Adele 21": [],
"21": [
{"username": "z", "hasFreeUploadSlot": True, "uploadSpeed": 1, "queueLength": 0,
"files": [{"filename": r"x\21 Savage - Savage Mode\01.flac", "size": 5}]},
],
"adele": [], # artist blocked
})
_install(monkeypatch, fake)
out = _client().search_album("Adele", "21")
assert out == []