feat(discover): suggest full albums only (filter singles/EPs)

Discovery's "Suggested albums" surfaced singles and EPs because
_derive_albums filtered with is_core_release (Album/Single/EP). Add a
discovery-only _album_kind_ok gate that, when discover.albumsOnly (new
config, default true), requires primary_type == "Album" with no secondary
types. is_core_release is untouched — the monitor/scan still use it.

Exposed discover.albumsOnly in the discover config route + a Discovery
settings toggle. Worker + web tests added.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-14 20:37:28 +02:00
parent 7957c621cf
commit c9905937cc
4 changed files with 58 additions and 1 deletions
+27
View File
@@ -37,6 +37,33 @@ def test_album_skips_release_groups_already_in_library(conn):
assert _album_rows(conn) == [("c1", "rg-fresh", "Fresh", "Album")] # owned rg excluded
def test_albums_only_excludes_singles_and_eps_by_default(conn):
insert_watched_artist(conn, mbid="s1", name="Seed")
src = FakeSimilaritySource(similar={"s1": [SimilarArtist("c1", "Cand", 0.9)]})
browser = FakeMbBrowser(releases={"c1": [
ReleaseGroupInfo("rg-single", "A Single", "Single", (), "2022-01-01"),
ReleaseGroupInfo("rg-ep", "An EP", "EP", (), "2021-01-01"),
ReleaseGroupInfo("rg-album", "A Record", "Album", (), "2020-01-01"),
]})
# albums_only defaults True: only the full-length Album is suggested
run_discovery(conn, [src], browser, DiscoveryConfig(albums_per_artist=5))
assert _album_rows(conn) == [("c1", "rg-album", "A Record", "Album")]
def test_albums_only_false_includes_singles_and_eps(conn):
insert_watched_artist(conn, mbid="s1", name="Seed")
src = FakeSimilaritySource(similar={"s1": [SimilarArtist("c1", "Cand", 0.9)]})
browser = FakeMbBrowser(releases={"c1": [
ReleaseGroupInfo("rg-single", "A Single", "Single", (), "2022-01-01"),
ReleaseGroupInfo("rg-album", "A Record", "Album", (), "2020-01-01"),
]})
run_discovery(conn, [src], browser, DiscoveryConfig(albums_per_artist=5, albums_only=False))
assert _album_rows(conn) == [
("c1", "rg-album", "A Record", "Album"),
("c1", "rg-single", "A Single", "Single"),
]
def test_albums_per_artist_zero_derives_none(conn):
insert_watched_artist(conn, mbid="s1", name="Seed")
src = FakeSimilaritySource(similar={"s1": [SimilarArtist("c1", "Cand", 0.9)]})