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
+55 -14
View File
@@ -72,7 +72,7 @@ func (q *Queries) DeleteAlbumIfEmpty(ctx context.Context, id pgtype.UUID) (Delet
}
const getAlbumByArtistAndTitle = `-- name: GetAlbumByArtistAndTitle :one
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source FROM albums WHERE artist_id = $1 AND title = $2 LIMIT 1
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version FROM albums WHERE artist_id = $1 AND title = $2 LIMIT 1
`
type GetAlbumByArtistAndTitleParams struct {
@@ -95,12 +95,13 @@ func (q *Queries) GetAlbumByArtistAndTitle(ctx context.Context, arg GetAlbumByAr
&i.CreatedAt,
&i.UpdatedAt,
&i.CoverArtSource,
&i.CoverArtSourcesVersion,
)
return i, err
}
const getAlbumByID = `-- name: GetAlbumByID :one
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source FROM albums WHERE id = $1
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version FROM albums WHERE id = $1
`
func (q *Queries) GetAlbumByID(ctx context.Context, id pgtype.UUID) (Album, error) {
@@ -117,6 +118,7 @@ func (q *Queries) GetAlbumByID(ctx context.Context, id pgtype.UUID) (Album, erro
&i.CreatedAt,
&i.UpdatedAt,
&i.CoverArtSource,
&i.CoverArtSourcesVersion,
)
return i, err
}
@@ -124,7 +126,7 @@ func (q *Queries) GetAlbumByID(ctx context.Context, id pgtype.UUID) (Album, erro
const getAlbumCoverageRollup = `-- name: GetAlbumCoverageRollup :one
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
@@ -149,7 +151,7 @@ type GetAlbumCoverageRollupRow struct {
// 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.
@@ -167,7 +169,7 @@ func (q *Queries) GetAlbumCoverageRollup(ctx context.Context) (GetAlbumCoverageR
}
const listAlbumsAlphaByArtist = `-- name: ListAlbumsAlphaByArtist :many
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, artists.sort_name AS artist_sort_name
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, albums.cover_art_sources_version, artists.sort_name AS artist_sort_name
FROM albums
JOIN artists ON artists.id = albums.artist_id
ORDER BY artists.sort_name, albums.sort_title
@@ -206,6 +208,7 @@ func (q *Queries) ListAlbumsAlphaByArtist(ctx context.Context, arg ListAlbumsAlp
&i.Album.CreatedAt,
&i.Album.UpdatedAt,
&i.Album.CoverArtSource,
&i.Album.CoverArtSourcesVersion,
&i.ArtistSortName,
); err != nil {
return nil, err
@@ -219,7 +222,7 @@ func (q *Queries) ListAlbumsAlphaByArtist(ctx context.Context, arg ListAlbumsAlp
}
const listAlbumsAlphaByName = `-- name: ListAlbumsAlphaByName :many
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source FROM albums ORDER BY sort_title LIMIT $1 OFFSET $2
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version FROM albums ORDER BY sort_title LIMIT $1 OFFSET $2
`
type ListAlbumsAlphaByNameParams struct {
@@ -247,6 +250,7 @@ func (q *Queries) ListAlbumsAlphaByName(ctx context.Context, arg ListAlbumsAlpha
&i.CreatedAt,
&i.UpdatedAt,
&i.CoverArtSource,
&i.CoverArtSourcesVersion,
); err != nil {
return nil, err
}
@@ -259,7 +263,7 @@ func (q *Queries) ListAlbumsAlphaByName(ctx context.Context, arg ListAlbumsAlpha
}
const listAlbumsAlphaWithArtist = `-- name: ListAlbumsAlphaWithArtist :many
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, artists.name AS artist_name
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, albums.cover_art_sources_version, artists.name AS artist_name
FROM albums
JOIN artists ON artists.id = albums.artist_id
ORDER BY albums.sort_title, albums.id
@@ -298,6 +302,7 @@ func (q *Queries) ListAlbumsAlphaWithArtist(ctx context.Context, arg ListAlbumsA
&i.Album.CreatedAt,
&i.Album.UpdatedAt,
&i.Album.CoverArtSource,
&i.Album.CoverArtSourcesVersion,
&i.ArtistName,
); err != nil {
return nil, err
@@ -311,7 +316,7 @@ func (q *Queries) ListAlbumsAlphaWithArtist(ctx context.Context, arg ListAlbumsA
}
const listAlbumsByArtist = `-- name: ListAlbumsByArtist :many
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source FROM albums WHERE artist_id = $1 ORDER BY release_date NULLS LAST, sort_title
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version FROM albums WHERE artist_id = $1 ORDER BY release_date NULLS LAST, sort_title
`
func (q *Queries) ListAlbumsByArtist(ctx context.Context, artistID pgtype.UUID) ([]Album, error) {
@@ -334,6 +339,7 @@ func (q *Queries) ListAlbumsByArtist(ctx context.Context, artistID pgtype.UUID)
&i.CreatedAt,
&i.UpdatedAt,
&i.CoverArtSource,
&i.CoverArtSourcesVersion,
); err != nil {
return nil, err
}
@@ -346,7 +352,7 @@ func (q *Queries) ListAlbumsByArtist(ctx context.Context, artistID pgtype.UUID)
}
const listAlbumsByGenre = `-- name: ListAlbumsByGenre :many
SELECT DISTINCT ON (albums.id) albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source
SELECT DISTINCT ON (albums.id) albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, albums.cover_art_sources_version
FROM albums
JOIN tracks ON tracks.album_id = albums.id
WHERE tracks.genre = $1
@@ -381,6 +387,7 @@ func (q *Queries) ListAlbumsByGenre(ctx context.Context, arg ListAlbumsByGenrePa
&i.CreatedAt,
&i.UpdatedAt,
&i.CoverArtSource,
&i.CoverArtSourcesVersion,
); err != nil {
return nil, err
}
@@ -446,7 +453,7 @@ func (q *Queries) ListAlbumsMissingMbidWithTrack(ctx context.Context, limit int3
}
const listAlbumsNewest = `-- name: ListAlbumsNewest :many
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source FROM albums ORDER BY created_at DESC LIMIT $1 OFFSET $2
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version FROM albums ORDER BY created_at DESC LIMIT $1 OFFSET $2
`
type ListAlbumsNewestParams struct {
@@ -474,6 +481,7 @@ func (q *Queries) ListAlbumsNewest(ctx context.Context, arg ListAlbumsNewestPara
&i.CreatedAt,
&i.UpdatedAt,
&i.CoverArtSource,
&i.CoverArtSourcesVersion,
); err != nil {
return nil, err
}
@@ -486,7 +494,7 @@ func (q *Queries) ListAlbumsNewest(ctx context.Context, arg ListAlbumsNewestPara
}
const listAlbumsRandom = `-- name: ListAlbumsRandom :many
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source FROM albums ORDER BY random() LIMIT $1
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version FROM albums ORDER BY random() LIMIT $1
`
func (q *Queries) ListAlbumsRandom(ctx context.Context, limit int32) ([]Album, error) {
@@ -509,6 +517,7 @@ func (q *Queries) ListAlbumsRandom(ctx context.Context, limit int32) ([]Album, e
&i.CreatedAt,
&i.UpdatedAt,
&i.CoverArtSource,
&i.CoverArtSourcesVersion,
); err != nil {
return nil, err
}
@@ -521,7 +530,7 @@ func (q *Queries) ListAlbumsRandom(ctx context.Context, limit int32) ([]Album, e
}
const listRecentlyAddedAlbumsWithArtist = `-- name: ListRecentlyAddedAlbumsWithArtist :many
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, artists.name AS artist_name
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, albums.cover_art_sources_version, artists.name AS artist_name
FROM albums
JOIN artists ON artists.id = albums.artist_id
ORDER BY albums.created_at DESC, albums.id
@@ -557,6 +566,7 @@ func (q *Queries) ListRecentlyAddedAlbumsWithArtist(ctx context.Context, limit i
&i.Album.CreatedAt,
&i.Album.UpdatedAt,
&i.Album.CoverArtSource,
&i.Album.CoverArtSourcesVersion,
&i.ArtistName,
); err != nil {
return nil, err
@@ -570,7 +580,7 @@ func (q *Queries) ListRecentlyAddedAlbumsWithArtist(ctx context.Context, limit i
}
const searchAlbums = `-- name: SearchAlbums :many
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source FROM albums
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version FROM albums
WHERE title ILIKE '%' || $1 || '%'
ORDER BY sort_title
LIMIT $2 OFFSET $3
@@ -602,6 +612,7 @@ func (q *Queries) SearchAlbums(ctx context.Context, arg SearchAlbumsParams) ([]A
&i.CreatedAt,
&i.UpdatedAt,
&i.CoverArtSource,
&i.CoverArtSourcesVersion,
); err != nil {
return nil, err
}
@@ -613,6 +624,35 @@ func (q *Queries) SearchAlbums(ctx context.Context, arg SearchAlbumsParams) ([]A
return items, nil
}
const setAlbumCoverWithVersion = `-- name: SetAlbumCoverWithVersion :exec
UPDATE albums
SET cover_art_path = $2,
cover_art_source = $3,
cover_art_sources_version = $4,
updated_at = now()
WHERE id = $1
`
type SetAlbumCoverWithVersionParams struct {
ID pgtype.UUID
CoverArtPath *string
CoverArtSource *string
CoverArtSourcesVersion int32
}
// 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.
func (q *Queries) SetAlbumCoverWithVersion(ctx context.Context, arg SetAlbumCoverWithVersionParams) error {
_, err := q.db.Exec(ctx, setAlbumCoverWithVersion,
arg.ID,
arg.CoverArtPath,
arg.CoverArtSource,
arg.CoverArtSourcesVersion,
)
return err
}
const setAlbumMbidIfNull = `-- name: SetAlbumMbidIfNull :exec
UPDATE albums
SET mbid = $2,
@@ -643,7 +683,7 @@ DO UPDATE SET
release_date = EXCLUDED.release_date,
cover_art_path = EXCLUDED.cover_art_path,
updated_at = now()
RETURNING id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source
RETURNING id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version
`
type UpsertAlbumParams struct {
@@ -676,6 +716,7 @@ func (q *Queries) UpsertAlbum(ctx context.Context, arg UpsertAlbumParams) (Album
&i.CreatedAt,
&i.UpdatedAt,
&i.CoverArtSource,
&i.CoverArtSourcesVersion,
)
return i, err
}
+153 -13
View File
@@ -11,6 +11,21 @@ import (
"github.com/jackc/pgx/v5/pgtype"
)
const clearArtistArtNone = `-- name: ClearArtistArtNone :exec
UPDATE artists
SET artist_art_source = NULL,
updated_at = now()
WHERE id = $1
AND artist_art_source = 'none'
`
// M7 cover-sources: parallel of ClearAlbumCoverNone. Used by the
// eligibility loop to flip stale 'none' rows back to NULL.
func (q *Queries) ClearArtistArtNone(ctx context.Context, id pgtype.UUID) error {
_, err := q.db.Exec(ctx, clearArtistArtNone, id)
return err
}
const countArtists = `-- name: CountArtists :one
SELECT COUNT(*) FROM artists
`
@@ -52,25 +67,46 @@ func (q *Queries) DeleteArtistIfEmpty(ctx context.Context, id pgtype.UUID) (pgty
}
const getArtistByID = `-- name: GetArtistByID :one
SELECT id, name, sort_name, mbid, created_at, updated_at FROM artists WHERE id = $1
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
`
func (q *Queries) GetArtistByID(ctx context.Context, id pgtype.UUID) (Artist, error) {
type GetArtistByIDRow struct {
ID pgtype.UUID
Name string
SortName string
Mbid *string
ArtistThumbPath *string
ArtistFanartPath *string
ArtistArtSource *string
ArtistArtSourcesVersion int32
}
// 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.)
func (q *Queries) GetArtistByID(ctx context.Context, id pgtype.UUID) (GetArtistByIDRow, error) {
row := q.db.QueryRow(ctx, getArtistByID, id)
var i Artist
var i GetArtistByIDRow
err := row.Scan(
&i.ID,
&i.Name,
&i.SortName,
&i.Mbid,
&i.CreatedAt,
&i.UpdatedAt,
&i.ArtistThumbPath,
&i.ArtistFanartPath,
&i.ArtistArtSource,
&i.ArtistArtSourcesVersion,
)
return i, err
}
const getArtistByName = `-- name: GetArtistByName :one
SELECT id, name, sort_name, mbid, created_at, updated_at FROM artists WHERE name = $1 LIMIT 1
SELECT id, name, sort_name, mbid, created_at, updated_at, artist_thumb_path, artist_fanart_path, artist_art_source, artist_art_sources_version FROM artists WHERE name = $1 LIMIT 1
`
func (q *Queries) GetArtistByName(ctx context.Context, name string) (Artist, error) {
@@ -83,12 +119,16 @@ func (q *Queries) GetArtistByName(ctx context.Context, name string) (Artist, err
&i.Mbid,
&i.CreatedAt,
&i.UpdatedAt,
&i.ArtistThumbPath,
&i.ArtistFanartPath,
&i.ArtistArtSource,
&i.ArtistArtSourcesVersion,
)
return i, err
}
const getArtistsByIDs = `-- name: GetArtistsByIDs :many
SELECT id, name, sort_name, mbid, created_at, updated_at FROM artists WHERE id = ANY($1::uuid[])
SELECT id, name, sort_name, mbid, created_at, updated_at, artist_thumb_path, artist_fanart_path, artist_art_source, artist_art_sources_version FROM artists WHERE id = ANY($1::uuid[])
`
// Batched lookup used by M5c suggestion attribution to resolve top-3
@@ -109,6 +149,10 @@ func (q *Queries) GetArtistsByIDs(ctx context.Context, dollar_1 []pgtype.UUID) (
&i.Mbid,
&i.CreatedAt,
&i.UpdatedAt,
&i.ArtistThumbPath,
&i.ArtistFanartPath,
&i.ArtistArtSource,
&i.ArtistArtSourcesVersion,
); err != nil {
return nil, err
}
@@ -121,7 +165,7 @@ func (q *Queries) GetArtistsByIDs(ctx context.Context, dollar_1 []pgtype.UUID) (
}
const listArtists = `-- name: ListArtists :many
SELECT id, name, sort_name, mbid, created_at, updated_at FROM artists ORDER BY sort_name
SELECT id, name, sort_name, mbid, created_at, updated_at, artist_thumb_path, artist_fanart_path, artist_art_source, artist_art_sources_version FROM artists ORDER BY sort_name
`
func (q *Queries) ListArtists(ctx context.Context) ([]Artist, error) {
@@ -140,6 +184,10 @@ func (q *Queries) ListArtists(ctx context.Context) ([]Artist, error) {
&i.Mbid,
&i.CreatedAt,
&i.UpdatedAt,
&i.ArtistThumbPath,
&i.ArtistFanartPath,
&i.ArtistArtSource,
&i.ArtistArtSourcesVersion,
); err != nil {
return nil, err
}
@@ -152,7 +200,7 @@ func (q *Queries) ListArtists(ctx context.Context) ([]Artist, error) {
}
const listArtistsAlpha = `-- name: ListArtistsAlpha :many
SELECT id, name, sort_name, mbid, created_at, updated_at FROM artists ORDER BY sort_name, name, id LIMIT $1 OFFSET $2
SELECT id, name, sort_name, mbid, created_at, updated_at, artist_thumb_path, artist_fanart_path, artist_art_source, artist_art_sources_version FROM artists ORDER BY sort_name, name, id LIMIT $1 OFFSET $2
`
type ListArtistsAlphaParams struct {
@@ -176,6 +224,10 @@ func (q *Queries) ListArtistsAlpha(ctx context.Context, arg ListArtistsAlphaPara
&i.Mbid,
&i.CreatedAt,
&i.UpdatedAt,
&i.ArtistThumbPath,
&i.ArtistFanartPath,
&i.ArtistArtSource,
&i.ArtistArtSourcesVersion,
); err != nil {
return nil, err
}
@@ -188,7 +240,7 @@ func (q *Queries) ListArtistsAlpha(ctx context.Context, arg ListArtistsAlphaPara
}
const listArtistsAlphaWithCovers = `-- name: ListArtistsAlphaWithCovers :many
SELECT artists.id, artists.name, artists.sort_name, artists.mbid, artists.created_at, artists.updated_at,
SELECT artists.id, artists.name, artists.sort_name, artists.mbid, artists.created_at, artists.updated_at, artists.artist_thumb_path, artists.artist_fanart_path, artists.artist_art_source, artists.artist_art_sources_version,
cov.id AS cover_album_id,
cnt.album_count::bigint AS album_count
FROM artists
@@ -237,6 +289,10 @@ func (q *Queries) ListArtistsAlphaWithCovers(ctx context.Context, arg ListArtist
&i.Artist.Mbid,
&i.Artist.CreatedAt,
&i.Artist.UpdatedAt,
&i.Artist.ArtistThumbPath,
&i.Artist.ArtistFanartPath,
&i.Artist.ArtistArtSource,
&i.Artist.ArtistArtSourcesVersion,
&i.CoverAlbumID,
&i.AlbumCount,
); err != nil {
@@ -250,8 +306,46 @@ func (q *Queries) ListArtistsAlphaWithCovers(ctx context.Context, arg ListArtist
return items, nil
}
const listArtistsMissingArt = `-- name: ListArtistsMissingArt :many
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
`
type ListArtistsMissingArtParams struct {
ArtistArtSourcesVersion int32
Limit int32
}
// 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.
func (q *Queries) ListArtistsMissingArt(ctx context.Context, arg ListArtistsMissingArtParams) ([]pgtype.UUID, error) {
rows, err := q.db.Query(ctx, listArtistsMissingArt, arg.ArtistArtSourcesVersion, arg.Limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []pgtype.UUID
for rows.Next() {
var id pgtype.UUID
if err := rows.Scan(&id); err != nil {
return nil, err
}
items = append(items, id)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listArtistsNewest = `-- name: ListArtistsNewest :many
SELECT id, name, sort_name, mbid, created_at, updated_at FROM artists ORDER BY created_at DESC, id DESC LIMIT $1 OFFSET $2
SELECT id, name, sort_name, mbid, created_at, updated_at, artist_thumb_path, artist_fanart_path, artist_art_source, artist_art_sources_version FROM artists ORDER BY created_at DESC, id DESC LIMIT $1 OFFSET $2
`
type ListArtistsNewestParams struct {
@@ -276,6 +370,10 @@ func (q *Queries) ListArtistsNewest(ctx context.Context, arg ListArtistsNewestPa
&i.Mbid,
&i.CreatedAt,
&i.UpdatedAt,
&i.ArtistThumbPath,
&i.ArtistFanartPath,
&i.ArtistArtSource,
&i.ArtistArtSourcesVersion,
); err != nil {
return nil, err
}
@@ -288,7 +386,7 @@ func (q *Queries) ListArtistsNewest(ctx context.Context, arg ListArtistsNewestPa
}
const searchArtists = `-- name: SearchArtists :many
SELECT id, name, sort_name, mbid, created_at, updated_at FROM artists
SELECT id, name, sort_name, mbid, created_at, updated_at, artist_thumb_path, artist_fanart_path, artist_art_source, artist_art_sources_version FROM artists
WHERE name ILIKE '%' || $1 || '%'
ORDER BY sort_name
LIMIT $2 OFFSET $3
@@ -316,6 +414,10 @@ func (q *Queries) SearchArtists(ctx context.Context, arg SearchArtistsParams) ([
&i.Mbid,
&i.CreatedAt,
&i.UpdatedAt,
&i.ArtistThumbPath,
&i.ArtistFanartPath,
&i.ArtistArtSource,
&i.ArtistArtSourcesVersion,
); err != nil {
return nil, err
}
@@ -327,6 +429,40 @@ func (q *Queries) SearchArtists(ctx context.Context, arg SearchArtistsParams) ([
return items, nil
}
const setArtistArtWithVersion = `-- name: SetArtistArtWithVersion :exec
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
`
type SetArtistArtWithVersionParams struct {
ID pgtype.UUID
ArtistThumbPath *string
ArtistFanartPath *string
ArtistArtSource *string
ArtistArtSourcesVersion int32
}
// 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).
func (q *Queries) SetArtistArtWithVersion(ctx context.Context, arg SetArtistArtWithVersionParams) error {
_, err := q.db.Exec(ctx, setArtistArtWithVersion,
arg.ID,
arg.ArtistThumbPath,
arg.ArtistFanartPath,
arg.ArtistArtSource,
arg.ArtistArtSourcesVersion,
)
return err
}
const setArtistMbidIfNull = `-- name: SetArtistMbidIfNull :exec
UPDATE artists
SET mbid = $2,
@@ -355,7 +491,7 @@ DO UPDATE SET
name = EXCLUDED.name,
sort_name = EXCLUDED.sort_name,
updated_at = now()
RETURNING id, name, sort_name, mbid, created_at, updated_at
RETURNING id, name, sort_name, mbid, created_at, updated_at, artist_thumb_path, artist_fanart_path, artist_art_source, artist_art_sources_version
`
type UpsertArtistParams struct {
@@ -376,6 +512,10 @@ func (q *Queries) UpsertArtist(ctx context.Context, arg UpsertArtistParams) (Art
&i.Mbid,
&i.CreatedAt,
&i.UpdatedAt,
&i.ArtistThumbPath,
&i.ArtistFanartPath,
&i.ArtistArtSource,
&i.ArtistArtSourcesVersion,
)
return i, err
}
+131
View File
@@ -0,0 +1,131 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: coverart_settings.sql
package dbq
import (
"context"
)
const bumpSourcesVersion = `-- name: BumpSourcesVersion :one
UPDATE cover_art_sources_meta
SET current_version = current_version + 1
WHERE id = true
RETURNING current_version
`
// 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.
func (q *Queries) BumpSourcesVersion(ctx context.Context) (int32, error) {
row := q.db.QueryRow(ctx, bumpSourcesVersion)
var current_version int32
err := row.Scan(&current_version)
return current_version, err
}
const getCurrentSourcesVersion = `-- name: GetCurrentSourcesVersion :one
SELECT current_version FROM cover_art_sources_meta WHERE id = true
`
func (q *Queries) GetCurrentSourcesVersion(ctx context.Context) (int32, error) {
row := q.db.QueryRow(ctx, getCurrentSourcesVersion)
var current_version int32
err := row.Scan(&current_version)
return current_version, err
}
const listProviderSettings = `-- name: ListProviderSettings :many
SELECT provider_id, enabled, api_key, display_order, created_at, updated_at
FROM cover_art_provider_settings
ORDER BY display_order, provider_id
`
// M7 cover-sources: queries for the cover_art_provider_settings table
// and the cover_art_sources_meta singleton. The SettingsService
// consumes these.
// Returns all rows from cover_art_provider_settings. Used by the
// SettingsService at boot for reconciliation and by the admin GET
// handler.
func (q *Queries) ListProviderSettings(ctx context.Context) ([]CoverArtProviderSetting, error) {
rows, err := q.db.Query(ctx, listProviderSettings)
if err != nil {
return nil, err
}
defer rows.Close()
var items []CoverArtProviderSetting
for rows.Next() {
var i CoverArtProviderSetting
if err := rows.Scan(
&i.ProviderID,
&i.Enabled,
&i.ApiKey,
&i.DisplayOrder,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateProviderSettings = `-- name: UpdateProviderSettings :exec
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
`
type UpdateProviderSettingsParams struct {
ProviderID string
Enabled bool
Column3 bool
ApiKey *string
}
// 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.
func (q *Queries) UpdateProviderSettings(ctx context.Context, arg UpdateProviderSettingsParams) error {
_, err := q.db.Exec(ctx, updateProviderSettings,
arg.ProviderID,
arg.Enabled,
arg.Column3,
arg.ApiKey,
)
return err
}
const upsertProviderSettings = `-- name: UpsertProviderSettings :exec
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()
`
type UpsertProviderSettingsParams struct {
ProviderID string
Enabled bool
DisplayOrder int32
}
// 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.
func (q *Queries) UpsertProviderSettings(ctx context.Context, arg UpsertProviderSettingsParams) error {
_, err := q.db.Exec(ctx, upsertProviderSettings, arg.ProviderID, arg.Enabled, arg.DisplayOrder)
return err
}
+18 -21
View File
@@ -109,41 +109,38 @@ func (q *Queries) GetAlbumWithFirstTrackPath(ctx context.Context, id pgtype.UUID
const listAlbumsMissingCover = `-- name: ListAlbumsMissingCover :many
SELECT a.id, a.mbid, a.title, a.artist_id
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
`
type ListAlbumsMissingCoverRow struct {
ID pgtype.UUID
Mbid *string
Title string
ArtistID pgtype.UUID
type ListAlbumsMissingCoverParams struct {
CoverArtSourcesVersion int32
Limit int32
}
// M7 #353: album cover enrichment queries.
// Albums where cover_art_source has never been set. Used by the boot
// backfill and post-scan enrichment passes. LIMIT supplied by caller.
func (q *Queries) ListAlbumsMissingCover(ctx context.Context, limit int32) ([]ListAlbumsMissingCoverRow, error) {
rows, err := q.db.Query(ctx, listAlbumsMissingCover, limit)
// 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.
func (q *Queries) ListAlbumsMissingCover(ctx context.Context, arg ListAlbumsMissingCoverParams) ([]pgtype.UUID, error) {
rows, err := q.db.Query(ctx, listAlbumsMissingCover, arg.CoverArtSourcesVersion, arg.Limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ListAlbumsMissingCoverRow
var items []pgtype.UUID
for rows.Next() {
var i ListAlbumsMissingCoverRow
if err := rows.Scan(
&i.ID,
&i.Mbid,
&i.Title,
&i.ArtistID,
); err != nil {
var id pgtype.UUID
if err := rows.Scan(&id); err != nil {
return nil, err
}
items = append(items, i)
items = append(items, id)
}
if err := rows.Err(); err != nil {
return nil, err
+7 -2
View File
@@ -120,7 +120,7 @@ func (q *Queries) ListLikedAlbumIDs(ctx context.Context, userID pgtype.UUID) ([]
}
const listLikedAlbumRows = `-- name: ListLikedAlbumRows :many
SELECT a.id, a.title, a.sort_title, a.artist_id, a.release_date, a.mbid, a.cover_art_path, a.created_at, a.updated_at, a.cover_art_source FROM albums a
SELECT a.id, a.title, a.sort_title, a.artist_id, a.release_date, a.mbid, a.cover_art_path, a.created_at, a.updated_at, a.cover_art_source, a.cover_art_sources_version FROM albums a
JOIN general_likes_albums l ON l.album_id = a.id
WHERE l.user_id = $1
ORDER BY l.liked_at DESC
@@ -153,6 +153,7 @@ func (q *Queries) ListLikedAlbumRows(ctx context.Context, arg ListLikedAlbumRows
&i.CreatedAt,
&i.UpdatedAt,
&i.CoverArtSource,
&i.CoverArtSourcesVersion,
); err != nil {
return nil, err
}
@@ -189,7 +190,7 @@ func (q *Queries) ListLikedArtistIDs(ctx context.Context, userID pgtype.UUID) ([
}
const listLikedArtistRows = `-- name: ListLikedArtistRows :many
SELECT a.id, a.name, a.sort_name, a.mbid, a.created_at, a.updated_at FROM artists a
SELECT a.id, a.name, a.sort_name, a.mbid, a.created_at, a.updated_at, a.artist_thumb_path, a.artist_fanart_path, a.artist_art_source, a.artist_art_sources_version FROM artists a
JOIN general_likes_artists l ON l.artist_id = a.id
WHERE l.user_id = $1
ORDER BY l.liked_at DESC
@@ -218,6 +219,10 @@ func (q *Queries) ListLikedArtistRows(ctx context.Context, arg ListLikedArtistRo
&i.Mbid,
&i.CreatedAt,
&i.UpdatedAt,
&i.ArtistThumbPath,
&i.ArtistFanartPath,
&i.ArtistArtSource,
&i.ArtistArtSourcesVersion,
); err != nil {
return nil, err
}
+43 -23
View File
@@ -188,25 +188,30 @@ func (ns NullLidarrRequestStatus) Value() (driver.Value, error) {
}
type Album struct {
ID pgtype.UUID
Title string
SortTitle string
ArtistID pgtype.UUID
ReleaseDate pgtype.Date
Mbid *string
CoverArtPath *string
CreatedAt pgtype.Timestamptz
UpdatedAt pgtype.Timestamptz
CoverArtSource *string
ID pgtype.UUID
Title string
SortTitle string
ArtistID pgtype.UUID
ReleaseDate pgtype.Date
Mbid *string
CoverArtPath *string
CreatedAt pgtype.Timestamptz
UpdatedAt pgtype.Timestamptz
CoverArtSource *string
CoverArtSourcesVersion int32
}
type Artist struct {
ID pgtype.UUID
Name string
SortName string
Mbid *string
CreatedAt pgtype.Timestamptz
UpdatedAt pgtype.Timestamptz
ID pgtype.UUID
Name string
SortName string
Mbid *string
CreatedAt pgtype.Timestamptz
UpdatedAt pgtype.Timestamptz
ArtistThumbPath *string
ArtistFanartPath *string
ArtistArtSource *string
ArtistArtSourcesVersion int32
}
type ArtistSimilarity struct {
@@ -236,6 +241,20 @@ type ContextualLike struct {
SessionID pgtype.UUID
}
type CoverArtProviderSetting struct {
ProviderID string
Enabled bool
ApiKey *string
DisplayOrder int32
CreatedAt pgtype.Timestamptz
UpdatedAt pgtype.Timestamptz
}
type CoverArtSourcesMetum struct {
ID bool
CurrentVersion int32
}
type GeneralLike struct {
UserID pgtype.UUID
TrackID pgtype.UUID
@@ -364,13 +383,14 @@ type PlaylistTrack struct {
}
type ScanRun struct {
ID pgtype.UUID
StartedAt pgtype.Timestamptz
FinishedAt pgtype.Timestamptz
Library []byte
MbidBackfill []byte
CoverEnrich []byte
ErrorMessage *string
ID pgtype.UUID
StartedAt pgtype.Timestamptz
FinishedAt pgtype.Timestamptz
Library []byte
MbidBackfill []byte
CoverEnrich []byte
ErrorMessage *string
ArtistArtEnrich []byte
}
type ScrobbleQueue struct {
+19 -5
View File
@@ -12,7 +12,7 @@ import (
)
const listLastPlayedArtistsForUser = `-- name: ListLastPlayedArtistsForUser :many
SELECT a.id, a.name, a.sort_name, a.mbid, a.created_at, a.updated_at,
SELECT a.id, a.name, a.sort_name, a.mbid, a.created_at, a.updated_at, a.artist_thumb_path, a.artist_fanart_path, a.artist_art_source, a.artist_art_sources_version,
cov.id AS cover_album_id,
cnt.album_count::bigint AS album_count,
max_started.started_at::timestamptz AS last_played_at
@@ -68,6 +68,10 @@ func (q *Queries) ListLastPlayedArtistsForUser(ctx context.Context, arg ListLast
&i.Artist.Mbid,
&i.Artist.CreatedAt,
&i.Artist.UpdatedAt,
&i.Artist.ArtistThumbPath,
&i.Artist.ArtistFanartPath,
&i.Artist.ArtistArtSource,
&i.Artist.ArtistArtSourcesVersion,
&i.CoverAlbumID,
&i.AlbumCount,
&i.LastPlayedAt,
@@ -157,7 +161,7 @@ func (q *Queries) ListMostPlayedTracksForUser(ctx context.Context, arg ListMostP
}
const listRediscoverAlbumsFallbackForUser = `-- name: ListRediscoverAlbumsFallbackForUser :many
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, artists.name AS artist_name
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, albums.cover_art_sources_version, artists.name AS artist_name
FROM general_likes_albums gla
JOIN albums ON albums.id = gla.album_id
JOIN artists ON artists.id = albums.artist_id
@@ -199,6 +203,7 @@ func (q *Queries) ListRediscoverAlbumsFallbackForUser(ctx context.Context, arg L
&i.Album.CreatedAt,
&i.Album.UpdatedAt,
&i.Album.CoverArtSource,
&i.Album.CoverArtSourcesVersion,
&i.ArtistName,
); err != nil {
return nil, err
@@ -226,7 +231,7 @@ WITH eligible AS (
HAVING COALESCE(max(pe.started_at), '1970-01-01'::timestamptz)
< now() - interval '14 days'
)
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, artists.name AS artist_name
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, albums.cover_art_sources_version, artists.name AS artist_name
FROM eligible e
JOIN albums ON albums.id = e.album_id
JOIN artists ON artists.id = albums.artist_id
@@ -268,6 +273,7 @@ func (q *Queries) ListRediscoverAlbumsForUser(ctx context.Context, arg ListRedis
&i.Album.CreatedAt,
&i.Album.UpdatedAt,
&i.Album.CoverArtSource,
&i.Album.CoverArtSourcesVersion,
&i.ArtistName,
); err != nil {
return nil, err
@@ -281,7 +287,7 @@ func (q *Queries) ListRediscoverAlbumsForUser(ctx context.Context, arg ListRedis
}
const listRediscoverArtistsFallbackForUser = `-- name: ListRediscoverArtistsFallbackForUser :many
SELECT artists.id, artists.name, artists.sort_name, artists.mbid, artists.created_at, artists.updated_at,
SELECT artists.id, artists.name, artists.sort_name, artists.mbid, artists.created_at, artists.updated_at, artists.artist_thumb_path, artists.artist_fanart_path, artists.artist_art_source, artists.artist_art_sources_version,
cov.id AS cover_album_id,
cnt.album_count::bigint AS album_count
FROM general_likes_artists gla
@@ -327,6 +333,10 @@ func (q *Queries) ListRediscoverArtistsFallbackForUser(ctx context.Context, arg
&i.Artist.Mbid,
&i.Artist.CreatedAt,
&i.Artist.UpdatedAt,
&i.Artist.ArtistThumbPath,
&i.Artist.ArtistFanartPath,
&i.Artist.ArtistArtSource,
&i.Artist.ArtistArtSourcesVersion,
&i.CoverAlbumID,
&i.AlbumCount,
); err != nil {
@@ -355,7 +365,7 @@ WITH eligible AS (
HAVING COALESCE(max(pe.started_at), '1970-01-01'::timestamptz)
< now() - interval '14 days'
)
SELECT artists.id, artists.name, artists.sort_name, artists.mbid, artists.created_at, artists.updated_at,
SELECT artists.id, artists.name, artists.sort_name, artists.mbid, artists.created_at, artists.updated_at, artists.artist_thumb_path, artists.artist_fanart_path, artists.artist_art_source, artists.artist_art_sources_version,
cov.id AS cover_album_id,
cnt.album_count::bigint AS album_count
FROM eligible e
@@ -403,6 +413,10 @@ func (q *Queries) ListRediscoverArtistsForUser(ctx context.Context, arg ListRedi
&i.Artist.Mbid,
&i.Artist.CreatedAt,
&i.Artist.UpdatedAt,
&i.Artist.ArtistThumbPath,
&i.Artist.ArtistFanartPath,
&i.Artist.ArtistArtSource,
&i.Artist.ArtistArtSourcesVersion,
&i.CoverAlbumID,
&i.AlbumCount,
); err != nil {
+12 -2
View File
@@ -59,11 +59,21 @@ SELECT id, started_at, finished_at, library, mbid_backfill, cover_enrich, error_
LIMIT 1
`
type GetLatestScanRunRow struct {
ID pgtype.UUID
StartedAt pgtype.Timestamptz
FinishedAt pgtype.Timestamptz
Library []byte
MbidBackfill []byte
CoverEnrich []byte
ErrorMessage *string
}
// Powers GET /api/admin/scan/status. Returns NoRows when no scan has
// ever run; the API layer maps that to a 200 with empty payload.
func (q *Queries) GetLatestScanRun(ctx context.Context) (ScanRun, error) {
func (q *Queries) GetLatestScanRun(ctx context.Context) (GetLatestScanRunRow, error) {
row := q.db.QueryRow(ctx, getLatestScanRun)
var i ScanRun
var i GetLatestScanRunRow
err := row.Scan(
&i.ID,
&i.StartedAt,
+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).