feat(db/m7-cover-sources): SQL queries for provider settings + recheck

- Extends GetAlbumCoverageRollup's with_art FILTER to include
  'theaudiodb' so F's coverage gauge surfaces TheAudioDB-found rows
  as with_art rather than silently undercounting.
- Modifies ListAlbumsMissingCover to take the current sources_version
  as a parameter; eligibility now includes stale-'none' rows.
- Adds SetAlbumCoverWithVersion (atomic write of path + source +
  version stamp) and the parallel SetArtistArtWithVersion +
  ClearArtistArtNone + ListArtistsMissingArt for the artist-art
  enricher.
- New coverart_settings.sql with ListProviderSettings,
  UpsertProviderSettings (boot reconciliation, preserves operator
  values on conflict), UpdateProviderSettings (admin PATCH; tri-
  state api_key handling), GetCurrentSourcesVersion,
  BumpSourcesVersion (RETURNING the new value).
This commit is contained in:
2026-05-06 12:28:18 -04:00
parent a86897b35e
commit 526a78b302
12 changed files with 550 additions and 88 deletions
+13 -2
View File
@@ -136,15 +136,26 @@ SELECT a.id AS album_id,
-- Invariant: with_art + pending + settled = total. (pending_no_mbid is
-- not part of the sum — it's a subset of pending, surfaced separately.)
--
-- The IN list below ('sidecar','embedded','mbcaa') must stay in sync
-- The IN list below ('sidecar','embedded','mbcaa','theaudiodb') must stay in sync
-- with the cover_art_source CHECK constraint in migration
-- 0016_album_cover_source.up.sql. If a new source value is added there
-- without updating this query, with_art will silently undercount.
SELECT
COUNT(*) AS total,
COUNT(*) FILTER (WHERE cover_art_source IN ('sidecar','embedded','mbcaa')) AS with_art,
COUNT(*) FILTER (WHERE cover_art_source IN ('sidecar','embedded','mbcaa','theaudiodb')) AS with_art,
COUNT(*) FILTER (WHERE cover_art_source IS NULL) AS pending,
COUNT(*) FILTER (WHERE cover_art_source = 'none') AS settled,
COUNT(*) FILTER (WHERE cover_art_source IS NULL AND mbid IS NULL) AS pending_no_mbid
FROM albums;
-- name: SetAlbumCoverWithVersion :exec
-- M7 cover-sources: records a successful or settled enrichment with the
-- current sources_version stamp. cover_art_path is NULL when source is
-- 'none' (no file written). Atomic; called per album per pass.
UPDATE albums
SET cover_art_path = $2,
cover_art_source = $3,
cover_art_sources_version = $4,
updated_at = now()
WHERE id = $1;
+44 -1
View File
@@ -11,7 +11,15 @@ DO UPDATE SET
RETURNING *;
-- name: GetArtistByID :one
SELECT * FROM artists WHERE id = $1;
-- M7 cover-sources: enricher reads the artist's mbid + current art
-- columns to drive eligibility check. (Modified from SELECT * to
-- explicitly include the new artist_art_* columns added in migration
-- 0018.)
SELECT a.id, a.name, a.sort_name, a.mbid,
a.artist_thumb_path, a.artist_fanart_path,
a.artist_art_source, a.artist_art_sources_version
FROM artists a
WHERE a.id = $1;
-- name: GetArtistByName :one
SELECT * FROM artists WHERE name = $1 LIMIT 1;
@@ -83,3 +91,38 @@ UPDATE artists
SET mbid = $2,
updated_at = now()
WHERE id = $1 AND mbid IS NULL;
-- name: ListArtistsMissingArt :many
-- M7 cover-sources: returns artist rows the artist-art enricher should
-- attempt. Eligible: artist_art_source IS NULL OR (= 'none' AND stale
-- artist_art_sources_version). MBID-less artists are returned but the
-- enricher skips them at the row.mbid == nil guard inside the chain.
SELECT a.id
FROM artists a
WHERE a.artist_art_source IS NULL
OR (a.artist_art_source = 'none' AND a.artist_art_sources_version != $1)
ORDER BY a.created_at
LIMIT $2;
-- name: SetArtistArtWithVersion :exec
-- M7 cover-sources: records a successful or settled artist-art
-- enrichment with the current sources_version stamp. thumb_path /
-- fanart_path are NULL on a 'none' result, or independently NULL if
-- the provider returned only one of the two image types (e.g.
-- TheAudioDB had a thumb but no fanart for this artist).
UPDATE artists
SET artist_thumb_path = $2,
artist_fanart_path = $3,
artist_art_source = $4,
artist_art_sources_version = $5,
updated_at = now()
WHERE id = $1;
-- name: ClearArtistArtNone :exec
-- M7 cover-sources: parallel of ClearAlbumCoverNone. Used by the
-- eligibility loop to flip stale 'none' rows back to NULL.
UPDATE artists
SET artist_art_source = NULL,
updated_at = now()
WHERE id = $1
AND artist_art_source = 'none';
+46
View File
@@ -0,0 +1,46 @@
-- M7 cover-sources: queries for the cover_art_provider_settings table
-- and the cover_art_sources_meta singleton. The SettingsService
-- consumes these.
-- name: ListProviderSettings :many
-- Returns all rows from cover_art_provider_settings. Used by the
-- SettingsService at boot for reconciliation and by the admin GET
-- handler.
SELECT provider_id, enabled, api_key, display_order, created_at, updated_at
FROM cover_art_provider_settings
ORDER BY display_order, provider_id;
-- name: UpsertProviderSettings :exec
-- INSERT a default row for a newly-registered provider, or update an
-- existing row's display_order on boot reconciliation. Does NOT
-- overwrite enabled / api_key on conflict — the operator's settings
-- persist across restarts.
INSERT INTO cover_art_provider_settings (provider_id, enabled, display_order)
VALUES ($1, $2, $3)
ON CONFLICT (provider_id) DO UPDATE
SET display_order = EXCLUDED.display_order,
updated_at = now();
-- name: UpdateProviderSettings :exec
-- Admin PATCH applies one or both fields. Pass NULL for enabled to
-- leave it unchanged. Pass FALSE for the apiKeyChanged flag to leave
-- the api_key unchanged regardless of apiKey value; pass TRUE with
-- empty string to clear, or TRUE with non-empty to set.
UPDATE cover_art_provider_settings
SET enabled = COALESCE($2, enabled),
api_key = CASE WHEN $3::boolean THEN $4 ELSE api_key END,
updated_at = now()
WHERE provider_id = $1;
-- name: GetCurrentSourcesVersion :one
SELECT current_version FROM cover_art_sources_meta WHERE id = true;
-- name: BumpSourcesVersion :one
-- Increments and returns the new version. Called by SettingsService
-- only when the *enabled set* changes (not on key/display_order
-- changes). RETURNING gives the new value back so the handler can
-- include it in the response without a follow-up SELECT.
UPDATE cover_art_sources_meta
SET current_version = current_version + 1
WHERE id = true
RETURNING current_version;
+9 -5
View File
@@ -1,13 +1,17 @@
-- M7 #353: album cover enrichment queries.
-- name: ListAlbumsMissingCover :many
-- Albums where cover_art_source has never been set. Used by the boot
-- backfill and post-scan enrichment passes. LIMIT supplied by caller.
SELECT a.id, a.mbid, a.title, a.artist_id
-- M7 cover-sources: returns rows the enricher should attempt. Eligible:
-- cover_art_source IS NULL (never tried), OR cover_art_source = 'none'
-- AND cover_art_sources_version != current (settled under an older
-- source set; recheck against the current chain). LIMIT supplied by
-- caller for batching.
SELECT a.id
FROM albums a
WHERE a.cover_art_source IS NULL
ORDER BY a.created_at ASC
LIMIT $1;
OR (a.cover_art_source = 'none' AND a.cover_art_sources_version != $1)
ORDER BY a.created_at
LIMIT $2;
-- name: ListAlbumsRetryMissing :many
-- Bulk-retry candidates: NULL (never tried) plus 'none' (tried, failed).