fix: Odesli links 'not found' when Odesli omits YouTube link
Root cause: Odesli's linksByPlatform frequently lacks youtube/youtubeMusic (confirmed live for many Spotify tracks). resolve_link_hits only added a YouTube hit when Odesli supplied one, so with no Lidarr match the hit list was empty -> server 404 'No results found'. Fix: always run a normal youtube_search (via build_combined_hits) for the fallback; when Odesli DID return a link, insert its exact track as the first YouTube hit. Lidarr-first ordering preserved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
18
musicfetch
18
musicfetch
@@ -898,18 +898,22 @@ def get_artist_from_metadata(meta: dict) -> str:
|
||||
|
||||
def resolve_link_hits(url: str, limit: int) -> tuple[str, list[Hit]]:
|
||||
"""Resolve a non-YouTube/SoundCloud link via Odesli into a search query plus
|
||||
hits: Lidarr album candidates for "Artist - Title", followed by the EXACT
|
||||
YouTube track from the shared link (not a fuzzy re-search). Raises OdesliError
|
||||
if the link can't be resolved."""
|
||||
Lidarr-first, YouTube-fallback hits for "Artist - Title". Odesli frequently
|
||||
omits a YouTube link, so we always run a normal youtube_search for the
|
||||
fallback; when Odesli DID supply a link, its exact track is inserted as the
|
||||
preferred (first) YouTube hit. Raises OdesliError if the link can't resolve."""
|
||||
r = odesli_resolve(url)
|
||||
if r is None:
|
||||
raise OdesliError(url)
|
||||
query = f"{r.artist} - {r.title}".strip(" -")
|
||||
hits = lidarr_search(query, limit)
|
||||
hits = build_combined_hits(query, limit, yt_first=False,
|
||||
lidarr_only=False, yt_only=False)
|
||||
if r.youtube_url:
|
||||
hits = hits + [Hit(source="youtube", kind="track", title=r.title,
|
||||
artist=r.artist, thumbnail=r.thumb,
|
||||
payload={"url": r.youtube_url})]
|
||||
exact = Hit(source="youtube", kind="track", title=r.title,
|
||||
artist=r.artist, thumbnail=r.thumb,
|
||||
payload={"url": r.youtube_url})
|
||||
idx = next((i for i, h in enumerate(hits) if h.source == "youtube"), len(hits))
|
||||
hits = hits[:idx] + [exact] + hits[idx:]
|
||||
return query, hits
|
||||
|
||||
|
||||
|
||||
@@ -107,28 +107,38 @@ def _resolved(yt="https://music.youtube.com/watch?v=YYY"):
|
||||
thumb="https://img/cover.jpg", youtube_url=yt)
|
||||
|
||||
|
||||
def test_resolve_link_hits_builds_query_and_exact_yt(monkeypatch):
|
||||
def _ytsearch_hit():
|
||||
return mf.Hit(source="youtube", kind="track", title="Bloom (search)",
|
||||
artist="ODESZA", payload={"videoId": "srch"})
|
||||
|
||||
|
||||
def test_resolve_link_hits_lidarr_first_then_exact_then_search(monkeypatch):
|
||||
# Odesli supplies a YouTube link -> exact track is the FIRST youtube hit,
|
||||
# ahead of the fuzzy youtube_search results, and after the Lidarr hits.
|
||||
monkeypatch.setattr(mf, "odesli_resolve", lambda url: _resolved())
|
||||
lid = mf.Hit(source="lidarr", kind="album", title="A Moment Apart",
|
||||
artist="ODESZA", album="A Moment Apart", year="2017",
|
||||
payload={"album": {"id": 9}})
|
||||
monkeypatch.setattr(mf, "lidarr_search", lambda q, limit: [lid])
|
||||
monkeypatch.setattr(mf, "youtube_search", lambda q, limit: [_ytsearch_hit()])
|
||||
query, hits = mf.resolve_link_hits("https://open.spotify.com/track/abc", 10)
|
||||
assert query == "ODESZA - Bloom"
|
||||
assert hits[0].source == "lidarr"
|
||||
yt = hits[-1]
|
||||
assert yt.source == "youtube" and yt.kind == "track"
|
||||
assert yt.title == "Bloom" and yt.artist == "ODESZA"
|
||||
assert yt.payload["url"] == "https://music.youtube.com/watch?v=YYY"
|
||||
yt = [h for h in hits if h.source == "youtube"]
|
||||
assert yt[0].payload.get("url") == "https://music.youtube.com/watch?v=YYY" # exact first
|
||||
assert any(h.payload.get("videoId") == "srch" for h in yt) # search fallback present
|
||||
|
||||
|
||||
def test_resolve_link_hits_no_yt_link_lidarr_only(monkeypatch):
|
||||
def test_resolve_link_hits_no_odesli_yt_uses_search_fallback(monkeypatch):
|
||||
# Regression: Odesli often omits YouTube links. With no Lidarr match and no
|
||||
# Odesli YouTube link, a normal youtube_search must still yield hits (not empty).
|
||||
monkeypatch.setattr(mf, "odesli_resolve", lambda url: _resolved(yt=""))
|
||||
lid = mf.Hit(source="lidarr", kind="album", title="X", artist="ODESZA",
|
||||
payload={"album": {"id": 9}})
|
||||
monkeypatch.setattr(mf, "lidarr_search", lambda q, limit: [lid])
|
||||
monkeypatch.setattr(mf, "lidarr_search", lambda q, limit: [])
|
||||
monkeypatch.setattr(mf, "youtube_search", lambda q, limit: [_ytsearch_hit()])
|
||||
query, hits = mf.resolve_link_hits("https://open.spotify.com/track/abc", 10)
|
||||
assert all(h.source == "lidarr" for h in hits)
|
||||
assert hits, "must not be empty when YouTube search finds the track"
|
||||
assert all(h.source == "youtube" for h in hits)
|
||||
assert not any(h.payload.get("url") for h in hits) # no exact Odesli hit injected
|
||||
|
||||
|
||||
def test_resolve_link_hits_odesli_miss_raises(monkeypatch):
|
||||
|
||||
Reference in New Issue
Block a user