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:
@@ -72,7 +72,7 @@ func (q *Queries) DeleteAlbumIfEmpty(ctx context.Context, id pgtype.UUID) (Delet
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getAlbumByArtistAndTitle = `-- name: GetAlbumByArtistAndTitle :one
|
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 {
|
type GetAlbumByArtistAndTitleParams struct {
|
||||||
@@ -95,12 +95,13 @@ func (q *Queries) GetAlbumByArtistAndTitle(ctx context.Context, arg GetAlbumByAr
|
|||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
|
&i.CoverArtSourcesVersion,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAlbumByID = `-- name: GetAlbumByID :one
|
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) {
|
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.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
|
&i.CoverArtSourcesVersion,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
@@ -124,7 +126,7 @@ func (q *Queries) GetAlbumByID(ctx context.Context, id pgtype.UUID) (Album, erro
|
|||||||
const getAlbumCoverageRollup = `-- name: GetAlbumCoverageRollup :one
|
const getAlbumCoverageRollup = `-- name: GetAlbumCoverageRollup :one
|
||||||
SELECT
|
SELECT
|
||||||
COUNT(*) AS total,
|
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 IS NULL) AS pending,
|
||||||
COUNT(*) FILTER (WHERE cover_art_source = 'none') AS settled,
|
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
|
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
|
// Invariant: with_art + pending + settled = total. (pending_no_mbid is
|
||||||
// not part of the sum — it's a subset of pending, surfaced separately.)
|
// 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
|
// with the cover_art_source CHECK constraint in migration
|
||||||
// 0016_album_cover_source.up.sql. If a new source value is added there
|
// 0016_album_cover_source.up.sql. If a new source value is added there
|
||||||
// without updating this query, with_art will silently undercount.
|
// 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
|
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
|
FROM albums
|
||||||
JOIN artists ON artists.id = albums.artist_id
|
JOIN artists ON artists.id = albums.artist_id
|
||||||
ORDER BY artists.sort_name, albums.sort_title
|
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.CreatedAt,
|
||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
|
&i.Album.CoverArtSourcesVersion,
|
||||||
&i.ArtistSortName,
|
&i.ArtistSortName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -219,7 +222,7 @@ func (q *Queries) ListAlbumsAlphaByArtist(ctx context.Context, arg ListAlbumsAlp
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsAlphaByName = `-- name: ListAlbumsAlphaByName :many
|
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 {
|
type ListAlbumsAlphaByNameParams struct {
|
||||||
@@ -247,6 +250,7 @@ func (q *Queries) ListAlbumsAlphaByName(ctx context.Context, arg ListAlbumsAlpha
|
|||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
|
&i.CoverArtSourcesVersion,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -259,7 +263,7 @@ func (q *Queries) ListAlbumsAlphaByName(ctx context.Context, arg ListAlbumsAlpha
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsAlphaWithArtist = `-- name: ListAlbumsAlphaWithArtist :many
|
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
|
FROM albums
|
||||||
JOIN artists ON artists.id = albums.artist_id
|
JOIN artists ON artists.id = albums.artist_id
|
||||||
ORDER BY albums.sort_title, albums.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.CreatedAt,
|
||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
|
&i.Album.CoverArtSourcesVersion,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -311,7 +316,7 @@ func (q *Queries) ListAlbumsAlphaWithArtist(ctx context.Context, arg ListAlbumsA
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsByArtist = `-- name: ListAlbumsByArtist :many
|
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) {
|
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.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
|
&i.CoverArtSourcesVersion,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -346,7 +352,7 @@ func (q *Queries) ListAlbumsByArtist(ctx context.Context, artistID pgtype.UUID)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsByGenre = `-- name: ListAlbumsByGenre :many
|
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
|
FROM albums
|
||||||
JOIN tracks ON tracks.album_id = albums.id
|
JOIN tracks ON tracks.album_id = albums.id
|
||||||
WHERE tracks.genre = $1
|
WHERE tracks.genre = $1
|
||||||
@@ -381,6 +387,7 @@ func (q *Queries) ListAlbumsByGenre(ctx context.Context, arg ListAlbumsByGenrePa
|
|||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
|
&i.CoverArtSourcesVersion,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -446,7 +453,7 @@ func (q *Queries) ListAlbumsMissingMbidWithTrack(ctx context.Context, limit int3
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsNewest = `-- name: ListAlbumsNewest :many
|
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 {
|
type ListAlbumsNewestParams struct {
|
||||||
@@ -474,6 +481,7 @@ func (q *Queries) ListAlbumsNewest(ctx context.Context, arg ListAlbumsNewestPara
|
|||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
|
&i.CoverArtSourcesVersion,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -486,7 +494,7 @@ func (q *Queries) ListAlbumsNewest(ctx context.Context, arg ListAlbumsNewestPara
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsRandom = `-- name: ListAlbumsRandom :many
|
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) {
|
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.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
|
&i.CoverArtSourcesVersion,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -521,7 +530,7 @@ func (q *Queries) ListAlbumsRandom(ctx context.Context, limit int32) ([]Album, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listRecentlyAddedAlbumsWithArtist = `-- name: ListRecentlyAddedAlbumsWithArtist :many
|
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
|
FROM albums
|
||||||
JOIN artists ON artists.id = albums.artist_id
|
JOIN artists ON artists.id = albums.artist_id
|
||||||
ORDER BY albums.created_at DESC, albums.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.CreatedAt,
|
||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
|
&i.Album.CoverArtSourcesVersion,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -570,7 +580,7 @@ func (q *Queries) ListRecentlyAddedAlbumsWithArtist(ctx context.Context, limit i
|
|||||||
}
|
}
|
||||||
|
|
||||||
const searchAlbums = `-- name: SearchAlbums :many
|
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 || '%'
|
WHERE title ILIKE '%' || $1 || '%'
|
||||||
ORDER BY sort_title
|
ORDER BY sort_title
|
||||||
LIMIT $2 OFFSET $3
|
LIMIT $2 OFFSET $3
|
||||||
@@ -602,6 +612,7 @@ func (q *Queries) SearchAlbums(ctx context.Context, arg SearchAlbumsParams) ([]A
|
|||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
|
&i.CoverArtSourcesVersion,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -613,6 +624,35 @@ func (q *Queries) SearchAlbums(ctx context.Context, arg SearchAlbumsParams) ([]A
|
|||||||
return items, nil
|
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
|
const setAlbumMbidIfNull = `-- name: SetAlbumMbidIfNull :exec
|
||||||
UPDATE albums
|
UPDATE albums
|
||||||
SET mbid = $2,
|
SET mbid = $2,
|
||||||
@@ -643,7 +683,7 @@ DO UPDATE SET
|
|||||||
release_date = EXCLUDED.release_date,
|
release_date = EXCLUDED.release_date,
|
||||||
cover_art_path = EXCLUDED.cover_art_path,
|
cover_art_path = EXCLUDED.cover_art_path,
|
||||||
updated_at = now()
|
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 {
|
type UpsertAlbumParams struct {
|
||||||
@@ -676,6 +716,7 @@ func (q *Queries) UpsertAlbum(ctx context.Context, arg UpsertAlbumParams) (Album
|
|||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
|
&i.CoverArtSourcesVersion,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|||||||
+153
-13
@@ -11,6 +11,21 @@ import (
|
|||||||
"github.com/jackc/pgx/v5/pgtype"
|
"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
|
const countArtists = `-- name: CountArtists :one
|
||||||
SELECT COUNT(*) FROM artists
|
SELECT COUNT(*) FROM artists
|
||||||
`
|
`
|
||||||
@@ -52,25 +67,46 @@ func (q *Queries) DeleteArtistIfEmpty(ctx context.Context, id pgtype.UUID) (pgty
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getArtistByID = `-- name: GetArtistByID :one
|
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)
|
row := q.db.QueryRow(ctx, getArtistByID, id)
|
||||||
var i Artist
|
var i GetArtistByIDRow
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Name,
|
&i.Name,
|
||||||
&i.SortName,
|
&i.SortName,
|
||||||
&i.Mbid,
|
&i.Mbid,
|
||||||
&i.CreatedAt,
|
&i.ArtistThumbPath,
|
||||||
&i.UpdatedAt,
|
&i.ArtistFanartPath,
|
||||||
|
&i.ArtistArtSource,
|
||||||
|
&i.ArtistArtSourcesVersion,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const getArtistByName = `-- name: GetArtistByName :one
|
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) {
|
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.Mbid,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.ArtistThumbPath,
|
||||||
|
&i.ArtistFanartPath,
|
||||||
|
&i.ArtistArtSource,
|
||||||
|
&i.ArtistArtSourcesVersion,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const getArtistsByIDs = `-- name: GetArtistsByIDs :many
|
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
|
// 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.Mbid,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.ArtistThumbPath,
|
||||||
|
&i.ArtistFanartPath,
|
||||||
|
&i.ArtistArtSource,
|
||||||
|
&i.ArtistArtSourcesVersion,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -121,7 +165,7 @@ func (q *Queries) GetArtistsByIDs(ctx context.Context, dollar_1 []pgtype.UUID) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listArtists = `-- name: ListArtists :many
|
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) {
|
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.Mbid,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.ArtistThumbPath,
|
||||||
|
&i.ArtistFanartPath,
|
||||||
|
&i.ArtistArtSource,
|
||||||
|
&i.ArtistArtSourcesVersion,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -152,7 +200,7 @@ func (q *Queries) ListArtists(ctx context.Context) ([]Artist, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listArtistsAlpha = `-- name: ListArtistsAlpha :many
|
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 {
|
type ListArtistsAlphaParams struct {
|
||||||
@@ -176,6 +224,10 @@ func (q *Queries) ListArtistsAlpha(ctx context.Context, arg ListArtistsAlphaPara
|
|||||||
&i.Mbid,
|
&i.Mbid,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.ArtistThumbPath,
|
||||||
|
&i.ArtistFanartPath,
|
||||||
|
&i.ArtistArtSource,
|
||||||
|
&i.ArtistArtSourcesVersion,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -188,7 +240,7 @@ func (q *Queries) ListArtistsAlpha(ctx context.Context, arg ListArtistsAlphaPara
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listArtistsAlphaWithCovers = `-- name: ListArtistsAlphaWithCovers :many
|
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,
|
cov.id AS cover_album_id,
|
||||||
cnt.album_count::bigint AS album_count
|
cnt.album_count::bigint AS album_count
|
||||||
FROM artists
|
FROM artists
|
||||||
@@ -237,6 +289,10 @@ func (q *Queries) ListArtistsAlphaWithCovers(ctx context.Context, arg ListArtist
|
|||||||
&i.Artist.Mbid,
|
&i.Artist.Mbid,
|
||||||
&i.Artist.CreatedAt,
|
&i.Artist.CreatedAt,
|
||||||
&i.Artist.UpdatedAt,
|
&i.Artist.UpdatedAt,
|
||||||
|
&i.Artist.ArtistThumbPath,
|
||||||
|
&i.Artist.ArtistFanartPath,
|
||||||
|
&i.Artist.ArtistArtSource,
|
||||||
|
&i.Artist.ArtistArtSourcesVersion,
|
||||||
&i.CoverAlbumID,
|
&i.CoverAlbumID,
|
||||||
&i.AlbumCount,
|
&i.AlbumCount,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@@ -250,8 +306,46 @@ func (q *Queries) ListArtistsAlphaWithCovers(ctx context.Context, arg ListArtist
|
|||||||
return items, nil
|
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
|
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 {
|
type ListArtistsNewestParams struct {
|
||||||
@@ -276,6 +370,10 @@ func (q *Queries) ListArtistsNewest(ctx context.Context, arg ListArtistsNewestPa
|
|||||||
&i.Mbid,
|
&i.Mbid,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.ArtistThumbPath,
|
||||||
|
&i.ArtistFanartPath,
|
||||||
|
&i.ArtistArtSource,
|
||||||
|
&i.ArtistArtSourcesVersion,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -288,7 +386,7 @@ func (q *Queries) ListArtistsNewest(ctx context.Context, arg ListArtistsNewestPa
|
|||||||
}
|
}
|
||||||
|
|
||||||
const searchArtists = `-- name: SearchArtists :many
|
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 || '%'
|
WHERE name ILIKE '%' || $1 || '%'
|
||||||
ORDER BY sort_name
|
ORDER BY sort_name
|
||||||
LIMIT $2 OFFSET $3
|
LIMIT $2 OFFSET $3
|
||||||
@@ -316,6 +414,10 @@ func (q *Queries) SearchArtists(ctx context.Context, arg SearchArtistsParams) ([
|
|||||||
&i.Mbid,
|
&i.Mbid,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.ArtistThumbPath,
|
||||||
|
&i.ArtistFanartPath,
|
||||||
|
&i.ArtistArtSource,
|
||||||
|
&i.ArtistArtSourcesVersion,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -327,6 +429,40 @@ func (q *Queries) SearchArtists(ctx context.Context, arg SearchArtistsParams) ([
|
|||||||
return items, nil
|
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
|
const setArtistMbidIfNull = `-- name: SetArtistMbidIfNull :exec
|
||||||
UPDATE artists
|
UPDATE artists
|
||||||
SET mbid = $2,
|
SET mbid = $2,
|
||||||
@@ -355,7 +491,7 @@ DO UPDATE SET
|
|||||||
name = EXCLUDED.name,
|
name = EXCLUDED.name,
|
||||||
sort_name = EXCLUDED.sort_name,
|
sort_name = EXCLUDED.sort_name,
|
||||||
updated_at = now()
|
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 {
|
type UpsertArtistParams struct {
|
||||||
@@ -376,6 +512,10 @@ func (q *Queries) UpsertArtist(ctx context.Context, arg UpsertArtistParams) (Art
|
|||||||
&i.Mbid,
|
&i.Mbid,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.ArtistThumbPath,
|
||||||
|
&i.ArtistFanartPath,
|
||||||
|
&i.ArtistArtSource,
|
||||||
|
&i.ArtistArtSourcesVersion,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(¤t_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(¤t_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
|
||||||
|
}
|
||||||
@@ -109,41 +109,38 @@ func (q *Queries) GetAlbumWithFirstTrackPath(ctx context.Context, id pgtype.UUID
|
|||||||
|
|
||||||
const listAlbumsMissingCover = `-- name: ListAlbumsMissingCover :many
|
const listAlbumsMissingCover = `-- name: ListAlbumsMissingCover :many
|
||||||
|
|
||||||
SELECT a.id, a.mbid, a.title, a.artist_id
|
SELECT a.id
|
||||||
FROM albums a
|
FROM albums a
|
||||||
WHERE a.cover_art_source IS NULL
|
WHERE a.cover_art_source IS NULL
|
||||||
ORDER BY a.created_at ASC
|
OR (a.cover_art_source = 'none' AND a.cover_art_sources_version != $1)
|
||||||
LIMIT $1
|
ORDER BY a.created_at
|
||||||
|
LIMIT $2
|
||||||
`
|
`
|
||||||
|
|
||||||
type ListAlbumsMissingCoverRow struct {
|
type ListAlbumsMissingCoverParams struct {
|
||||||
ID pgtype.UUID
|
CoverArtSourcesVersion int32
|
||||||
Mbid *string
|
Limit int32
|
||||||
Title string
|
|
||||||
ArtistID pgtype.UUID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// M7 #353: album cover enrichment queries.
|
// M7 #353: album cover enrichment queries.
|
||||||
// Albums where cover_art_source has never been set. Used by the boot
|
// M7 cover-sources: returns rows the enricher should attempt. Eligible:
|
||||||
// backfill and post-scan enrichment passes. LIMIT supplied by caller.
|
// cover_art_source IS NULL (never tried), OR cover_art_source = 'none'
|
||||||
func (q *Queries) ListAlbumsMissingCover(ctx context.Context, limit int32) ([]ListAlbumsMissingCoverRow, error) {
|
// AND cover_art_sources_version != current (settled under an older
|
||||||
rows, err := q.db.Query(ctx, listAlbumsMissingCover, limit)
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
var items []ListAlbumsMissingCoverRow
|
var items []pgtype.UUID
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var i ListAlbumsMissingCoverRow
|
var id pgtype.UUID
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(&id); err != nil {
|
||||||
&i.ID,
|
|
||||||
&i.Mbid,
|
|
||||||
&i.Title,
|
|
||||||
&i.ArtistID,
|
|
||||||
); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
items = append(items, i)
|
items = append(items, id)
|
||||||
}
|
}
|
||||||
if err := rows.Err(); err != nil {
|
if err := rows.Err(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ func (q *Queries) ListLikedAlbumIDs(ctx context.Context, userID pgtype.UUID) ([]
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listLikedAlbumRows = `-- name: ListLikedAlbumRows :many
|
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
|
JOIN general_likes_albums l ON l.album_id = a.id
|
||||||
WHERE l.user_id = $1
|
WHERE l.user_id = $1
|
||||||
ORDER BY l.liked_at DESC
|
ORDER BY l.liked_at DESC
|
||||||
@@ -153,6 +153,7 @@ func (q *Queries) ListLikedAlbumRows(ctx context.Context, arg ListLikedAlbumRows
|
|||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
|
&i.CoverArtSourcesVersion,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -189,7 +190,7 @@ func (q *Queries) ListLikedArtistIDs(ctx context.Context, userID pgtype.UUID) ([
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listLikedArtistRows = `-- name: ListLikedArtistRows :many
|
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
|
JOIN general_likes_artists l ON l.artist_id = a.id
|
||||||
WHERE l.user_id = $1
|
WHERE l.user_id = $1
|
||||||
ORDER BY l.liked_at DESC
|
ORDER BY l.liked_at DESC
|
||||||
@@ -218,6 +219,10 @@ func (q *Queries) ListLikedArtistRows(ctx context.Context, arg ListLikedArtistRo
|
|||||||
&i.Mbid,
|
&i.Mbid,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.ArtistThumbPath,
|
||||||
|
&i.ArtistFanartPath,
|
||||||
|
&i.ArtistArtSource,
|
||||||
|
&i.ArtistArtSourcesVersion,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-23
@@ -188,25 +188,30 @@ func (ns NullLidarrRequestStatus) Value() (driver.Value, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Album struct {
|
type Album struct {
|
||||||
ID pgtype.UUID
|
ID pgtype.UUID
|
||||||
Title string
|
Title string
|
||||||
SortTitle string
|
SortTitle string
|
||||||
ArtistID pgtype.UUID
|
ArtistID pgtype.UUID
|
||||||
ReleaseDate pgtype.Date
|
ReleaseDate pgtype.Date
|
||||||
Mbid *string
|
Mbid *string
|
||||||
CoverArtPath *string
|
CoverArtPath *string
|
||||||
CreatedAt pgtype.Timestamptz
|
CreatedAt pgtype.Timestamptz
|
||||||
UpdatedAt pgtype.Timestamptz
|
UpdatedAt pgtype.Timestamptz
|
||||||
CoverArtSource *string
|
CoverArtSource *string
|
||||||
|
CoverArtSourcesVersion int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type Artist struct {
|
type Artist struct {
|
||||||
ID pgtype.UUID
|
ID pgtype.UUID
|
||||||
Name string
|
Name string
|
||||||
SortName string
|
SortName string
|
||||||
Mbid *string
|
Mbid *string
|
||||||
CreatedAt pgtype.Timestamptz
|
CreatedAt pgtype.Timestamptz
|
||||||
UpdatedAt pgtype.Timestamptz
|
UpdatedAt pgtype.Timestamptz
|
||||||
|
ArtistThumbPath *string
|
||||||
|
ArtistFanartPath *string
|
||||||
|
ArtistArtSource *string
|
||||||
|
ArtistArtSourcesVersion int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type ArtistSimilarity struct {
|
type ArtistSimilarity struct {
|
||||||
@@ -236,6 +241,20 @@ type ContextualLike struct {
|
|||||||
SessionID pgtype.UUID
|
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 {
|
type GeneralLike struct {
|
||||||
UserID pgtype.UUID
|
UserID pgtype.UUID
|
||||||
TrackID pgtype.UUID
|
TrackID pgtype.UUID
|
||||||
@@ -364,13 +383,14 @@ type PlaylistTrack struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ScanRun struct {
|
type ScanRun struct {
|
||||||
ID pgtype.UUID
|
ID pgtype.UUID
|
||||||
StartedAt pgtype.Timestamptz
|
StartedAt pgtype.Timestamptz
|
||||||
FinishedAt pgtype.Timestamptz
|
FinishedAt pgtype.Timestamptz
|
||||||
Library []byte
|
Library []byte
|
||||||
MbidBackfill []byte
|
MbidBackfill []byte
|
||||||
CoverEnrich []byte
|
CoverEnrich []byte
|
||||||
ErrorMessage *string
|
ErrorMessage *string
|
||||||
|
ArtistArtEnrich []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type ScrobbleQueue struct {
|
type ScrobbleQueue struct {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const listLastPlayedArtistsForUser = `-- name: ListLastPlayedArtistsForUser :many
|
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,
|
cov.id AS cover_album_id,
|
||||||
cnt.album_count::bigint AS album_count,
|
cnt.album_count::bigint AS album_count,
|
||||||
max_started.started_at::timestamptz AS last_played_at
|
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.Mbid,
|
||||||
&i.Artist.CreatedAt,
|
&i.Artist.CreatedAt,
|
||||||
&i.Artist.UpdatedAt,
|
&i.Artist.UpdatedAt,
|
||||||
|
&i.Artist.ArtistThumbPath,
|
||||||
|
&i.Artist.ArtistFanartPath,
|
||||||
|
&i.Artist.ArtistArtSource,
|
||||||
|
&i.Artist.ArtistArtSourcesVersion,
|
||||||
&i.CoverAlbumID,
|
&i.CoverAlbumID,
|
||||||
&i.AlbumCount,
|
&i.AlbumCount,
|
||||||
&i.LastPlayedAt,
|
&i.LastPlayedAt,
|
||||||
@@ -157,7 +161,7 @@ func (q *Queries) ListMostPlayedTracksForUser(ctx context.Context, arg ListMostP
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listRediscoverAlbumsFallbackForUser = `-- name: ListRediscoverAlbumsFallbackForUser :many
|
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
|
FROM general_likes_albums gla
|
||||||
JOIN albums ON albums.id = gla.album_id
|
JOIN albums ON albums.id = gla.album_id
|
||||||
JOIN artists ON artists.id = albums.artist_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.CreatedAt,
|
||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
|
&i.Album.CoverArtSourcesVersion,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -226,7 +231,7 @@ WITH eligible AS (
|
|||||||
HAVING COALESCE(max(pe.started_at), '1970-01-01'::timestamptz)
|
HAVING COALESCE(max(pe.started_at), '1970-01-01'::timestamptz)
|
||||||
< now() - interval '14 days'
|
< 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
|
FROM eligible e
|
||||||
JOIN albums ON albums.id = e.album_id
|
JOIN albums ON albums.id = e.album_id
|
||||||
JOIN artists ON artists.id = albums.artist_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.CreatedAt,
|
||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
|
&i.Album.CoverArtSourcesVersion,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -281,7 +287,7 @@ func (q *Queries) ListRediscoverAlbumsForUser(ctx context.Context, arg ListRedis
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listRediscoverArtistsFallbackForUser = `-- name: ListRediscoverArtistsFallbackForUser :many
|
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,
|
cov.id AS cover_album_id,
|
||||||
cnt.album_count::bigint AS album_count
|
cnt.album_count::bigint AS album_count
|
||||||
FROM general_likes_artists gla
|
FROM general_likes_artists gla
|
||||||
@@ -327,6 +333,10 @@ func (q *Queries) ListRediscoverArtistsFallbackForUser(ctx context.Context, arg
|
|||||||
&i.Artist.Mbid,
|
&i.Artist.Mbid,
|
||||||
&i.Artist.CreatedAt,
|
&i.Artist.CreatedAt,
|
||||||
&i.Artist.UpdatedAt,
|
&i.Artist.UpdatedAt,
|
||||||
|
&i.Artist.ArtistThumbPath,
|
||||||
|
&i.Artist.ArtistFanartPath,
|
||||||
|
&i.Artist.ArtistArtSource,
|
||||||
|
&i.Artist.ArtistArtSourcesVersion,
|
||||||
&i.CoverAlbumID,
|
&i.CoverAlbumID,
|
||||||
&i.AlbumCount,
|
&i.AlbumCount,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@@ -355,7 +365,7 @@ WITH eligible AS (
|
|||||||
HAVING COALESCE(max(pe.started_at), '1970-01-01'::timestamptz)
|
HAVING COALESCE(max(pe.started_at), '1970-01-01'::timestamptz)
|
||||||
< now() - interval '14 days'
|
< 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,
|
cov.id AS cover_album_id,
|
||||||
cnt.album_count::bigint AS album_count
|
cnt.album_count::bigint AS album_count
|
||||||
FROM eligible e
|
FROM eligible e
|
||||||
@@ -403,6 +413,10 @@ func (q *Queries) ListRediscoverArtistsForUser(ctx context.Context, arg ListRedi
|
|||||||
&i.Artist.Mbid,
|
&i.Artist.Mbid,
|
||||||
&i.Artist.CreatedAt,
|
&i.Artist.CreatedAt,
|
||||||
&i.Artist.UpdatedAt,
|
&i.Artist.UpdatedAt,
|
||||||
|
&i.Artist.ArtistThumbPath,
|
||||||
|
&i.Artist.ArtistFanartPath,
|
||||||
|
&i.Artist.ArtistArtSource,
|
||||||
|
&i.Artist.ArtistArtSourcesVersion,
|
||||||
&i.CoverAlbumID,
|
&i.CoverAlbumID,
|
||||||
&i.AlbumCount,
|
&i.AlbumCount,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
|
|||||||
@@ -59,11 +59,21 @@ SELECT id, started_at, finished_at, library, mbid_backfill, cover_enrich, error_
|
|||||||
LIMIT 1
|
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
|
// 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.
|
// 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)
|
row := q.db.QueryRow(ctx, getLatestScanRun)
|
||||||
var i ScanRun
|
var i GetLatestScanRunRow
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.StartedAt,
|
&i.StartedAt,
|
||||||
|
|||||||
@@ -136,15 +136,26 @@ SELECT a.id AS album_id,
|
|||||||
-- Invariant: with_art + pending + settled = total. (pending_no_mbid is
|
-- Invariant: with_art + pending + settled = total. (pending_no_mbid is
|
||||||
-- not part of the sum — it's a subset of pending, surfaced separately.)
|
-- 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
|
-- with the cover_art_source CHECK constraint in migration
|
||||||
-- 0016_album_cover_source.up.sql. If a new source value is added there
|
-- 0016_album_cover_source.up.sql. If a new source value is added there
|
||||||
-- without updating this query, with_art will silently undercount.
|
-- without updating this query, with_art will silently undercount.
|
||||||
SELECT
|
SELECT
|
||||||
COUNT(*) AS total,
|
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 IS NULL) AS pending,
|
||||||
COUNT(*) FILTER (WHERE cover_art_source = 'none') AS settled,
|
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
|
COUNT(*) FILTER (WHERE cover_art_source IS NULL AND mbid IS NULL) AS pending_no_mbid
|
||||||
FROM albums;
|
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;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,15 @@ DO UPDATE SET
|
|||||||
RETURNING *;
|
RETURNING *;
|
||||||
|
|
||||||
-- name: GetArtistByID :one
|
-- 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
|
-- name: GetArtistByName :one
|
||||||
SELECT * FROM artists WHERE name = $1 LIMIT 1;
|
SELECT * FROM artists WHERE name = $1 LIMIT 1;
|
||||||
@@ -83,3 +91,38 @@ UPDATE artists
|
|||||||
SET mbid = $2,
|
SET mbid = $2,
|
||||||
updated_at = now()
|
updated_at = now()
|
||||||
WHERE id = $1 AND mbid IS NULL;
|
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';
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -1,13 +1,17 @@
|
|||||||
-- M7 #353: album cover enrichment queries.
|
-- M7 #353: album cover enrichment queries.
|
||||||
|
|
||||||
-- name: ListAlbumsMissingCover :many
|
-- name: ListAlbumsMissingCover :many
|
||||||
-- Albums where cover_art_source has never been set. Used by the boot
|
-- M7 cover-sources: returns rows the enricher should attempt. Eligible:
|
||||||
-- backfill and post-scan enrichment passes. LIMIT supplied by caller.
|
-- cover_art_source IS NULL (never tried), OR cover_art_source = 'none'
|
||||||
SELECT a.id, a.mbid, a.title, a.artist_id
|
-- 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
|
FROM albums a
|
||||||
WHERE a.cover_art_source IS NULL
|
WHERE a.cover_art_source IS NULL
|
||||||
ORDER BY a.created_at ASC
|
OR (a.cover_art_source = 'none' AND a.cover_art_sources_version != $1)
|
||||||
LIMIT $1;
|
ORDER BY a.created_at
|
||||||
|
LIMIT $2;
|
||||||
|
|
||||||
-- name: ListAlbumsRetryMissing :many
|
-- name: ListAlbumsRetryMissing :many
|
||||||
-- Bulk-retry candidates: NULL (never tried) plus 'none' (tried, failed).
|
-- Bulk-retry candidates: NULL (never tried) plus 'none' (tried, failed).
|
||||||
|
|||||||
Reference in New Issue
Block a user