fix(mb): rank release groups by credited artist, not title alone

The worker MB resolver picked release groups on (title_ratio, is_album)
and never scored the artist — even though resolve() knows the followed
artist. For a generic title, same-titled release groups by *different*
artists tie on title, and the is_album tiebreak (added in 06e7f91 for
Off the Wall) then actively preferred a foreign Album over the correct
release. Concretely, following Lake Street Dive's "Fun Machine" EP
resolved to an unrelated band "Bastards of Melody"'s same-titled Album,
which then propagated as the downloaded/imported artist.

Make credited-artist similarity the primary sort key, above title and
above is_album. It's a soft signal, never a hard filter, so credited-
name variations (feat., punctuation) still resolve; is_album now only
breaks ties within the same artist+title, preserving the Off the Wall
fix. Verified live: resolve("Lake Street Dive", "Fun Machine") now
returns the 6-track LSD EP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-15 12:32:12 +02:00
parent c6945bd89e
commit 927266d042
2 changed files with 70 additions and 20 deletions
+32 -8
View File
@@ -13,15 +13,39 @@ def _is_album(g: dict) -> bool:
return (g.get("primary-type") or "").casefold() == "album" return (g.get("primary-type") or "").casefold() == "album"
def _best_release_group(album: str, groups: list) -> dict | None: def _credited_artist(g: dict) -> str:
"""Choose the best release-group match for `album`. Highest title similarity wins; """Primary credited artist name of a release-group search result, or '' if absent."""
an Album primary-type breaks ties so a famous album (e.g. Michael Jackson's "Off the credit = g.get("artist-credit")
Wall") isn't resolved to its same-named single, whose short tracklist would then be if isinstance(credit, list) and credit and isinstance(credit[0], dict):
mapped positionally onto the full album's files. Returns None below the title threshold. return (credit[0].get("artist") or {}).get("name", "") or ""
Pure — no network I/O.""" return ""
def _best_release_group(artist: str, album: str, groups: list) -> dict | None:
"""Choose the best release-group match for `artist`/`album`.
Ranked, highest first, on `(artist_ratio, title_ratio, is_album)`:
* artist match dominates — MusicBrainz relevance ties same-titled release groups by
*different* artists (e.g. "Fun Machine" exists as a Lake Street Dive EP and an
unrelated band's Album), and the artist we followed is trustworthy, so a credited
artist that matches the request outranks everything else;
* title similarity is next;
* an Album primary-type only breaks ties *within* the same artist+title, so a famous
album (Michael Jackson's "Off the Wall") still isn't resolved to its same-named
single whose short tracklist would map positionally onto the album's files.
Artist is a ranking signal, never a hard filter — a low match sinks a candidate but
never drops the release, so credited-name variations (feat., punctuation) still resolve.
Returns None below the title threshold. Pure — no network I/O."""
if not groups: if not groups:
return None return None
best = max(groups, key=lambda g: (_ratio(album, g.get("title", "")), _is_album(g))) best = max(
groups,
key=lambda g: (
_ratio(artist, _credited_artist(g)),
_ratio(album, g.get("title", "")),
_is_album(g),
),
)
if _ratio(album, best.get("title", "")) < _MIN_TITLE_RATIO: if _ratio(album, best.get("title", "")) < _MIN_TITLE_RATIO:
return None return None
return best return best
@@ -46,7 +70,7 @@ class MusicBrainzResolver:
res = musicbrainzngs.search_release_groups(query=album, artist=artist, limit=5) res = musicbrainzngs.search_release_groups(query=album, artist=artist, limit=5)
groups = res.get("release-group-list", []) groups = res.get("release-group-list", [])
rg = _best_release_group(album, groups) rg = _best_release_group(artist, album, groups)
if rg is None: if rg is None:
return None return None
+38 -12
View File
@@ -4,33 +4,59 @@ from lyra_worker._musicbrainz import _best_release_group
# path (network) is covered by test_musicbrainz_live.py. # path (network) is covered by test_musicbrainz_live.py.
def _rg(title, primary_type): def _rg(title, primary_type, artist=""):
return {"id": title.lower(), "title": title, "primary-type": primary_type} g = {"id": title.lower(), "title": title, "primary-type": primary_type}
if artist:
g["artist-credit"] = [{"artist": {"name": artist, "id": artist.lower()}}]
return g
def test_prefers_album_over_same_titled_single(): def test_prefers_album_over_same_titled_single():
# Real-world "Off the Wall" bug: MusicBrainz returns the Single release-group # Real-world "Off the Wall" bug: MusicBrainz returns the Single release-group
# first, tied on title with the Album. We must pick the Album so a 10-track # first, tied on title with the Album. We must pick the Album so a 10-track
# download isn't tagged from the 2-track single's tracklist. # download isn't tagged from the 2-track single's tracklist.
groups = [_rg("Off the Wall", "Single"), _rg("Off the Wall", "Album")] groups = [
assert _best_release_group("Off the Wall", groups)["primary-type"] == "Album" _rg("Off the Wall", "Single", "Michael Jackson"),
_rg("Off the Wall", "Album", "Michael Jackson"),
]
assert _best_release_group("Michael Jackson", "Off the Wall", groups)["primary-type"] == "Album"
def test_album_preference_is_order_independent(): def test_album_preference_is_order_independent():
groups = [_rg("Off the Wall", "Album"), _rg("Off the Wall", "Single")] groups = [
assert _best_release_group("Off the Wall", groups)["primary-type"] == "Album" _rg("Off the Wall", "Album", "Michael Jackson"),
_rg("Off the Wall", "Single", "Michael Jackson"),
]
assert _best_release_group("Michael Jackson", "Off the Wall", groups)["primary-type"] == "Album"
def test_closer_title_still_wins_over_album_type(): def test_closer_title_still_wins_over_album_type():
# Title similarity dominates; the album-type preference only breaks ties. # Title similarity dominates the album-type preference (for a single artist).
groups = [_rg("Continuum", "Single"), _rg("Continuum Deluxe Reissue", "Album")] groups = [
assert _best_release_group("Continuum", groups)["title"] == "Continuum" _rg("Continuum", "Single", "John Mayer"),
_rg("Continuum Deluxe Reissue", "Album", "John Mayer"),
]
assert _best_release_group("John Mayer", "Continuum", groups)["title"] == "Continuum"
def test_below_threshold_returns_none(): def test_below_threshold_returns_none():
groups = [_rg("Something Totally Different", "Album")] groups = [_rg("Something Totally Different", "Album", "Michael Jackson")]
assert _best_release_group("Off the Wall", groups) is None assert _best_release_group("Michael Jackson", "Off the Wall", groups) is None
def test_empty_groups_returns_none(): def test_empty_groups_returns_none():
assert _best_release_group("Off the Wall", []) is None assert _best_release_group("Michael Jackson", "Off the Wall", []) is None
def test_prefers_credited_artist_over_same_titled_other_artist_album():
# Real-world "Fun Machine" bug: Lake Street Dive's EP is what we followed, but two
# unrelated bands also have a release group literally titled "Fun Machine" as an Album.
# All three tie on title (1.0), so the old is_album tiebreak wrongly grabbed a foreign
# Album. The followed artist must win over title-tied albums by other artists.
groups = [
_rg("Fun Machine", "EP", "Lake Street Dive"),
_rg("Fun Machine", "Album", "Bastards of Melody"),
_rg("Fun Machine", "Album", "Teledildonics 5000"),
]
best = _best_release_group("Lake Street Dive", "Fun Machine", groups)
assert best["artist-credit"][0]["artist"]["name"] == "Lake Street Dive"