feat(lidarr): MusicBrainz track-to-album resolver
Add musicbrainz_best_album() that resolves an artist+track pair to its best studio album via the MusicBrainz search API, with a 1 req/sec courtesy rate-limiter. Prefers plain studio albums over compilations, singles, and live releases; falls back to any release group when no studio album is found. Never raises — returns None on any failure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
79
tests/test_musicbrainz.py
Normal file
79
tests/test_musicbrainz.py
Normal file
@@ -0,0 +1,79 @@
|
||||
import server.mf # noqa: F401
|
||||
import musicfetch_core as mf
|
||||
|
||||
|
||||
class _FakeResp:
|
||||
def __init__(self, payload):
|
||||
self._payload = payload
|
||||
def raise_for_status(self):
|
||||
pass
|
||||
def json(self):
|
||||
return self._payload
|
||||
|
||||
|
||||
MB_PAYLOAD = {
|
||||
"recordings": [
|
||||
{
|
||||
"artist-credit": [{"name": "Daft Punk"}],
|
||||
"releases": [
|
||||
{"date": "2001",
|
||||
"release-group": {"id": "single-mbid", "title": "Harder, Better, Faster, Stronger",
|
||||
"primary-type": "Single", "secondary-types": []}},
|
||||
{"date": "2002",
|
||||
"release-group": {"id": "comp-mbid", "title": "Musique, Vol. 1",
|
||||
"primary-type": "Album", "secondary-types": ["Compilation"]}},
|
||||
{"date": "2001",
|
||||
"release-group": {"id": "48117b90-a16e-34ca-a514-19c702df1158",
|
||||
"title": "Discovery", "primary-type": "Album",
|
||||
"secondary-types": []}},
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def test_picks_studio_album_over_single_and_comp(monkeypatch):
|
||||
monkeypatch.setattr(mf.requests, "get", lambda *a, **k: _FakeResp(MB_PAYLOAD))
|
||||
monkeypatch.setattr(mf.time, "sleep", lambda *_: None)
|
||||
out = mf.musicbrainz_best_album("Daft Punk", "Harder Better Faster Stronger")
|
||||
assert out["album_title"] == "Discovery"
|
||||
assert out["artist"] == "Daft Punk"
|
||||
assert out["year"] == "2001"
|
||||
assert out["rg_mbid"] == "48117b90-a16e-34ca-a514-19c702df1158"
|
||||
|
||||
|
||||
def test_returns_none_on_empty(monkeypatch):
|
||||
monkeypatch.setattr(mf.requests, "get", lambda *a, **k: _FakeResp({"recordings": []}))
|
||||
monkeypatch.setattr(mf.time, "sleep", lambda *_: None)
|
||||
assert mf.musicbrainz_best_album("Nobody", "Nothing") is None
|
||||
|
||||
|
||||
def test_returns_none_on_exception(monkeypatch):
|
||||
def boom(*a, **k):
|
||||
raise mf.requests.exceptions.RequestException("network down")
|
||||
monkeypatch.setattr(mf.requests, "get", boom)
|
||||
monkeypatch.setattr(mf.time, "sleep", lambda *_: None)
|
||||
assert mf.musicbrainz_best_album("Daft Punk", "Discovery") is None
|
||||
|
||||
|
||||
def test_falls_back_to_any_releasegroup_when_no_studio(monkeypatch):
|
||||
payload = {"recordings": [{"artist-credit": [{"name": "X"}], "releases": [
|
||||
{"date": "2010", "release-group": {"id": "live1", "title": "Live Thing",
|
||||
"primary-type": "Album", "secondary-types": ["Live"]}},
|
||||
]}]}
|
||||
monkeypatch.setattr(mf.requests, "get", lambda *a, **k: _FakeResp(payload))
|
||||
monkeypatch.setattr(mf.time, "sleep", lambda *_: None)
|
||||
out = mf.musicbrainz_best_album("X", "Y")
|
||||
assert out["album_title"] == "Live Thing"
|
||||
|
||||
|
||||
def test_first_artist_credit_only(monkeypatch):
|
||||
payload = {"recordings": [{"artist-credit": [{"name": "SLVMLORD"}, {"name": "Travis Bradley"}],
|
||||
"releases": [{"date": "2025",
|
||||
"release-group": {"id": "x", "title": "Album X",
|
||||
"primary-type": "Album",
|
||||
"secondary-types": []}}]}]}
|
||||
monkeypatch.setattr(mf.requests, "get", lambda *a, **k: _FakeResp(payload))
|
||||
monkeypatch.setattr(mf.time, "sleep", lambda *_: None)
|
||||
out = mf.musicbrainz_best_album("SLVMLORD", "Under My Skin")
|
||||
assert out["artist"] == "SLVMLORD"
|
||||
Reference in New Issue
Block a user