diff --git a/worker/lyra_worker/_musicbrainz.py b/worker/lyra_worker/_musicbrainz.py index 03a1b3d..ec73c13 100644 --- a/worker/lyra_worker/_musicbrainz.py +++ b/worker/lyra_worker/_musicbrainz.py @@ -37,6 +37,12 @@ def _is_album(g: dict) -> bool: return (g.get("primary-type") or "").casefold() == "album" +def _rg_year(g: dict) -> int: + """First-release year of a release-group, or 9999 when unknown (so undated groups lose ties).""" + frd = (g.get("first-release-date") or "")[:4] + return int(frd) if frd.isdigit() else 9999 + + def _credited_artist(g: dict) -> str: """Primary credited artist name of a release-group search result, or '' if absent.""" credit = g.get("artist-credit") @@ -56,7 +62,12 @@ def _best_release_group(artist: str, album: str, groups: list) -> dict | None: * 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. + single whose short tracklist would map positionally onto the album's files; + * finally, the EARLIEST release-group wins — MusicBrainz lists the original album and its + later reissues/anniversary editions as separate same-titled groups (Linkin Park "Hybrid + Theory": a 2000 original and a 2023 13-track reissue), which tie on everything above, so + MB's arbitrary relevance order used to pick a reissue whose longer tracklist/runtime then + failed completeness checks against the standard album a source delivers. Prefer the original. 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.""" @@ -68,6 +79,7 @@ def _best_release_group(artist: str, album: str, groups: list) -> dict | None: _ratio(artist, _credited_artist(g)), _ratio(album, g.get("title", "")), _is_album(g), + -_rg_year(g), # tie-break: the original (earliest) group over later reissues ), ) if _ratio(album, best.get("title", "")) < _MIN_TITLE_RATIO: diff --git a/worker/tests/test_musicbrainz.py b/worker/tests/test_musicbrainz.py index 48d8f25..2da579a 100644 --- a/worker/tests/test_musicbrainz.py +++ b/worker/tests/test_musicbrainz.py @@ -51,6 +51,21 @@ def test_empty_groups_returns_none(): assert _best_release_group("Michael Jackson", "Off the Wall", []) is None +def test_prefers_original_group_over_later_reissue(): + # Real "Hybrid Theory" bug: MB lists a 2000 original and a 2023 reissue as separate same-titled + # Album groups (tie on artist/title/type). The reissue's longer tracklist fails completeness + # checks, so the original (earliest) must win regardless of MB's relevance order. + groups = [ + {"id": "reissue", "title": "Hybrid Theory", "primary-type": "Album", + "first-release-date": "2023-07-21", + "artist-credit": [{"artist": {"name": "Linkin Park"}}]}, + {"id": "original", "title": "Hybrid Theory", "primary-type": "Album", + "first-release-date": "2000-10-24", + "artist-credit": [{"artist": {"name": "Linkin Park"}}]}, + ] + assert _best_release_group("Linkin Park", "Hybrid Theory", groups)["id"] == "original" + + 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.