soulsync/tests/downloads/test_track_link.py
BoulderBadgeDad cea0e9d63c Manual download search: paste a Tidal/Qobuz track link to grab the exact version (#813)
When a track shows "Not found", the manual search now accepts a pasted Tidal or
Qobuz track link, not just a typed query (CubeComming: the fuzzy search misses
versions; he can find the track on Tidal but can't get it to appear).

How it works (robust, reuses the proven path): parse the link → (source,
track_id) → fetch the track via the source client's get_track → build a clean
"artist title (version)" query → run THAT source's normal search → bubble the
result whose id matches the link to the top. So the candidate is a normal,
already-downloadable streaming result — no hand-built download encoding — and
it downloads through the existing verified flow.

Degrades gracefully: if the source isn't connected or the link can't be
resolved, it falls back to a normal text search of the raw input — the user is
never worse off than typing it themselves. Scoped to Tidal + Qobuz (the
streaming sources that download by track id, with public track URLs); Soulseek
can't take a link (P2P, no ids), YouTube/SoundCloud are URL-native via a
different path (future).

- core/downloads/track_link.py: pure parse_download_track_link (tidal/qobuz
  /track/<id>, slug/region suffixes, scheme-less) + query_from_track_payload
  (per-source title/artist, Tidal version-append).
- manual-search endpoint: link detection → resolve → restrict to that source →
  id-match bubble.
- placeholder hint mentions pasting a link; maxlength 200→300 for long URLs.

Tests: 14 (parser shapes + payload extraction incl. remix version-append +
qobuz performer/album-artist fallback). JS valid.
2026-06-08 15:03:54 -07:00

80 lines
2.7 KiB
Python

"""Parse pasted Tidal/Qobuz track links for the manual download search (#813)."""
from core.downloads.track_link import parse_download_track_link as p
def test_tidal_track_url_with_region_suffix():
assert p('https://tidal.com/track/434945950/u') == ('tidal', '434945950')
def test_tidal_browse_and_listen_hosts():
assert p('https://tidal.com/browse/track/434945950') == ('tidal', '434945950')
assert p('https://listen.tidal.com/track/434945950') == ('tidal', '434945950')
def test_qobuz_track_urls():
assert p('https://open.qobuz.com/track/12345678') == ('qobuz', '12345678')
assert p('https://play.qobuz.com/track/12345678') == ('qobuz', '12345678')
def test_scheme_less():
assert p('tidal.com/track/999') == ('tidal', '999')
def test_id_with_slug_suffix():
assert p('https://www.qobuz.com/track/555-some-slug') == ('qobuz', '555')
def test_non_track_links_rejected():
assert p('https://tidal.com/album/123') is None # album, not track
assert p('https://tidal.com/artist/123') is None
assert p('https://open.spotify.com/track/abc') is None # unsupported source
assert p('https://example.com/track/123') is None
def test_garbage_rejected():
assert p('') is None
assert p('just some text') is None
assert p('Habbit (T-Mass Remix)') is None
# ── query_from_track_payload (pure per-source parsing) ──
from core.downloads.track_link import query_from_track_payload as q
def test_tidal_payload_appends_version():
# Tidal attributes: title + version → remix link searches for the remix.
raw = {'title': 'Habbit', 'version': 'T-Mass Remix',
'artists': [{'name': 'Rain Man'}, {'name': 'Krysta Youngs'}]}
assert q('tidal', raw) == 'Rain Man Habbit (T-Mass Remix)'
def test_tidal_payload_no_version_no_artist():
assert q('tidal', {'title': 'Bloom'}) == 'Bloom'
def test_tidal_payload_singular_artist():
assert q('tidal', {'title': 'X', 'artist': {'name': 'Jinco'}}) == 'Jinco X'
def test_tidal_version_already_in_title_not_doubled():
raw = {'title': 'Bloom (Nurko Remix)', 'version': 'Nurko Remix',
'artists': [{'name': 'Dabin'}]}
assert q('tidal', raw) == 'Dabin Bloom (Nurko Remix)'
def test_qobuz_payload_performer():
raw = {'title': "What's Good For Me", 'performer': {'name': 'Jinco'}}
assert q('qobuz', raw) == "Jinco What's Good For Me"
def test_qobuz_payload_falls_back_to_album_artist():
raw = {'title': 'Song', 'album': {'artist': {'name': 'Some Artist'}}}
assert q('qobuz', raw) == 'Some Artist Song'
def test_payload_non_dict_or_empty():
assert q('tidal', None) is None
assert q('tidal', {}) is None
assert q('qobuz', 'garbage') is None