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:
@@ -17,6 +17,7 @@ class DiscoveryConfig:
|
||||
similar_per_seed: int = 20
|
||||
albums_per_artist: int = 1
|
||||
min_score: float = 0.0
|
||||
albums_only: bool = True
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, config: dict) -> "DiscoveryConfig":
|
||||
@@ -32,6 +33,12 @@ class DiscoveryConfig:
|
||||
except (KeyError, TypeError, ValueError):
|
||||
return default
|
||||
|
||||
def _bool(key: str, default: bool) -> bool:
|
||||
raw = config.get(key)
|
||||
if raw is None:
|
||||
return default
|
||||
return str(raw).strip().lower() in _TRUE
|
||||
|
||||
return cls(
|
||||
enabled=str(config.get("discover.enabled", "")).strip().lower() in _TRUE,
|
||||
interval_hours=_int("discover.intervalHours", 168),
|
||||
@@ -39,6 +46,7 @@ class DiscoveryConfig:
|
||||
similar_per_seed=_int("discover.similarPerSeed", 20),
|
||||
albums_per_artist=_int("discover.albumsPerArtist", 1),
|
||||
min_score=_float("discover.minScore", 0.0),
|
||||
albums_only=_bool("discover.albumsOnly", True),
|
||||
)
|
||||
|
||||
|
||||
@@ -122,6 +130,18 @@ def _upsert_album(conn: psycopg.Connection, artist: dict, rg) -> int:
|
||||
return cur.rowcount
|
||||
|
||||
|
||||
def _album_kind_ok(g, albums_only: bool) -> bool:
|
||||
"""Which release groups qualify as discovery album suggestions.
|
||||
|
||||
Discovery-only policy: when albums_only (default), suggest full studio Albums
|
||||
only — no Singles/EPs, no secondary types (live/compilation/etc.). Falls back
|
||||
to the broader is_core_release (Album/Single/EP) when the toggle is off. This
|
||||
deliberately does NOT touch is_core_release, which the monitor/scan still use."""
|
||||
if albums_only:
|
||||
return (g.primary_type or "") == "Album" and len(g.secondary_types or ()) == 0
|
||||
return is_core_release(g.primary_type, g.secondary_types)
|
||||
|
||||
|
||||
def _derive_albums(conn: psycopg.Connection, browser: MbBrowser, surfaced: list[dict],
|
||||
cfg: DiscoveryConfig) -> int:
|
||||
if cfg.albums_per_artist <= 0:
|
||||
@@ -135,7 +155,7 @@ def _derive_albums(conn: psycopg.Connection, browser: MbBrowser, surfaced: list[
|
||||
print(f"worker: discovery album browse failed for {artist['mbid']}: {e}", flush=True)
|
||||
continue
|
||||
core = [g for g in groups
|
||||
if is_core_release(g.primary_type, g.secondary_types) and g.rg_mbid not in have]
|
||||
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)
|
||||
for rg in core[: cfg.albums_per_artist]:
|
||||
albums += _upsert_album(conn, artist, rg)
|
||||
|
||||
Reference in New Issue
Block a user