fix(discover): drop suggestions for non-artists (no core discography)

ListenBrainz surfaces producers/session players/band members (Max Martin,
Dominic Howard) that co-occur in listens but have no releases of their own —
they showed as bare rows with an empty page. _derive_albums now drops a
surfaced artist (deletes their pending suggestion) when their MB discography
has no core release-group (Album/Single/EP, no secondary type), catching both
empty discographies and soundtrack/production-only credits, while keeping real
singles/EP-only artists. On browse failure the suggestion is kept.

Test fake browser gains default_core so scoring tests keep surfacing candidates.
worker 249 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-15 00:38:56 +02:00
parent 9915bfbda2
commit 4a768b7122
4 changed files with 70 additions and 24 deletions
+10 -3
View File
@@ -72,17 +72,24 @@ class FailingAdapter(_BaseFake):
class FakeMbBrowser:
"""In-memory MbBrowser for tests. `releases` maps artist mbid -> [ReleaseGroupInfo]."""
"""In-memory MbBrowser for tests. `releases` maps artist mbid -> [ReleaseGroupInfo].
`default_core=True` returns a synthetic core Album for any artist not in `releases`, so
scoring tests don't trip the "no core discography → drop the suggestion" filter."""
def __init__(self, artists=None, releases=None):
def __init__(self, artists=None, releases=None, default_core=False):
self._artists = list(artists or [])
self._releases = dict(releases or {})
self._default_core = default_core
def search_artist(self, name: str) -> list[ArtistHit]:
return list(self._artists)
def browse_release_groups(self, artist_mbid: str) -> list[ReleaseGroupInfo]:
return list(self._releases.get(artist_mbid, []))
if artist_mbid in self._releases:
return list(self._releases[artist_mbid])
if self._default_core:
return [ReleaseGroupInfo(f"rg-{artist_mbid}", "Album", "Album", (), "2000-01-01")]
return []
class FakeAudioProbe:
+10
View File
@@ -204,6 +204,16 @@ def _derive_albums(conn: psycopg.Connection, browser: MbBrowser, surfaced: list[
except Exception as e: # one artist's browse failure must not abort the sweep
print(f"worker: discovery album browse failed for {artist['mbid']}: {e}", flush=True)
continue
# Drop suggestions for "artists" with no real discography of their own — producers,
# session players, band members, soundtrack-only credits. ListenBrainz surfaces them
# (they co-occur in listens) but they have no core release-group to follow or want, and
# their page is empty. Require at least one core release (Album/Single/EP, no secondary
# type); on browse failure we keep the suggestion rather than drop a real artist.
if not any(is_core_release(g.primary_type, g.secondary_types) for g in groups):
with conn.cursor() as cur:
cur.execute("DELETE FROM \"DiscoverySuggestion\" WHERE status = 'pending' "
'AND "artistMbid" = %s', (artist["mbid"],))
continue
core = [g for g in groups
if _album_kind_ok(g, cfg.albums_only) and g.rg_mbid not in have]
core.sort(key=lambda g: g.first_release_date or "", reverse=True)