feat(db/m7-353): sqlc queries for album cover enrichment
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 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 FROM albums WHERE artist_id = $1 AND title = $2 LIMIT 1
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetAlbumByArtistAndTitleParams struct {
|
type GetAlbumByArtistAndTitleParams struct {
|
||||||
@@ -94,12 +94,13 @@ func (q *Queries) GetAlbumByArtistAndTitle(ctx context.Context, arg GetAlbumByAr
|
|||||||
&i.CoverArtPath,
|
&i.CoverArtPath,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.CoverArtSource,
|
||||||
)
|
)
|
||||||
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 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 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) {
|
||||||
@@ -115,12 +116,13 @@ func (q *Queries) GetAlbumByID(ctx context.Context, id pgtype.UUID) (Album, erro
|
|||||||
&i.CoverArtPath,
|
&i.CoverArtPath,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.CoverArtSource,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
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, 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, 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
|
||||||
@@ -158,6 +160,7 @@ func (q *Queries) ListAlbumsAlphaByArtist(ctx context.Context, arg ListAlbumsAlp
|
|||||||
&i.Album.CoverArtPath,
|
&i.Album.CoverArtPath,
|
||||||
&i.Album.CreatedAt,
|
&i.Album.CreatedAt,
|
||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
|
&i.Album.CoverArtSource,
|
||||||
&i.ArtistSortName,
|
&i.ArtistSortName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -171,7 +174,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 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 FROM albums ORDER BY sort_title LIMIT $1 OFFSET $2
|
||||||
`
|
`
|
||||||
|
|
||||||
type ListAlbumsAlphaByNameParams struct {
|
type ListAlbumsAlphaByNameParams struct {
|
||||||
@@ -198,6 +201,7 @@ func (q *Queries) ListAlbumsAlphaByName(ctx context.Context, arg ListAlbumsAlpha
|
|||||||
&i.CoverArtPath,
|
&i.CoverArtPath,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.CoverArtSource,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -210,7 +214,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, 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, 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
|
||||||
@@ -248,6 +252,7 @@ func (q *Queries) ListAlbumsAlphaWithArtist(ctx context.Context, arg ListAlbumsA
|
|||||||
&i.Album.CoverArtPath,
|
&i.Album.CoverArtPath,
|
||||||
&i.Album.CreatedAt,
|
&i.Album.CreatedAt,
|
||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
|
&i.Album.CoverArtSource,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -261,7 +266,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 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 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) {
|
||||||
@@ -283,6 +288,7 @@ func (q *Queries) ListAlbumsByArtist(ctx context.Context, artistID pgtype.UUID)
|
|||||||
&i.CoverArtPath,
|
&i.CoverArtPath,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.CoverArtSource,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -295,7 +301,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
|
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
|
||||||
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
|
||||||
@@ -329,6 +335,7 @@ func (q *Queries) ListAlbumsByGenre(ctx context.Context, arg ListAlbumsByGenrePa
|
|||||||
&i.CoverArtPath,
|
&i.CoverArtPath,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.CoverArtSource,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -341,7 +348,7 @@ func (q *Queries) ListAlbumsByGenre(ctx context.Context, arg ListAlbumsByGenrePa
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 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 FROM albums ORDER BY created_at DESC LIMIT $1 OFFSET $2
|
||||||
`
|
`
|
||||||
|
|
||||||
type ListAlbumsNewestParams struct {
|
type ListAlbumsNewestParams struct {
|
||||||
@@ -368,6 +375,7 @@ func (q *Queries) ListAlbumsNewest(ctx context.Context, arg ListAlbumsNewestPara
|
|||||||
&i.CoverArtPath,
|
&i.CoverArtPath,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.CoverArtSource,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -380,7 +388,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 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 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) {
|
||||||
@@ -402,6 +410,7 @@ func (q *Queries) ListAlbumsRandom(ctx context.Context, limit int32) ([]Album, e
|
|||||||
&i.CoverArtPath,
|
&i.CoverArtPath,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.CoverArtSource,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -414,7 +423,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, 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, 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
|
||||||
@@ -449,6 +458,7 @@ func (q *Queries) ListRecentlyAddedAlbumsWithArtist(ctx context.Context, limit i
|
|||||||
&i.Album.CoverArtPath,
|
&i.Album.CoverArtPath,
|
||||||
&i.Album.CreatedAt,
|
&i.Album.CreatedAt,
|
||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
|
&i.Album.CoverArtSource,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -462,7 +472,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 FROM albums
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source 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
|
||||||
@@ -493,6 +503,7 @@ func (q *Queries) SearchAlbums(ctx context.Context, arg SearchAlbumsParams) ([]A
|
|||||||
&i.CoverArtPath,
|
&i.CoverArtPath,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.CoverArtSource,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -515,7 +526,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
|
RETURNING id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpsertAlbumParams struct {
|
type UpsertAlbumParams struct {
|
||||||
@@ -547,6 +558,7 @@ func (q *Queries) UpsertAlbum(ctx context.Context, arg UpsertAlbumParams) (Album
|
|||||||
&i.CoverArtPath,
|
&i.CoverArtPath,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.CoverArtSource,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.31.1
|
||||||
|
// source: covers.sql
|
||||||
|
|
||||||
|
package dbq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
const clearAlbumCover = `-- name: ClearAlbumCover :exec
|
||||||
|
UPDATE albums
|
||||||
|
SET cover_art_path = NULL,
|
||||||
|
cover_art_source = NULL
|
||||||
|
WHERE id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
// Used by the admin retry endpoint to wipe state before re-running the
|
||||||
|
// enricher. Sets both fields to NULL.
|
||||||
|
func (q *Queries) ClearAlbumCover(ctx context.Context, id pgtype.UUID) error {
|
||||||
|
_, err := q.db.Exec(ctx, clearAlbumCover, id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const countAlbumCoverSources = `-- name: CountAlbumCoverSources :one
|
||||||
|
SELECT
|
||||||
|
COUNT(*) FILTER (WHERE cover_art_source IS NULL) ::bigint AS untried,
|
||||||
|
COUNT(*) FILTER (WHERE cover_art_source = 'sidecar') ::bigint AS sidecar,
|
||||||
|
COUNT(*) FILTER (WHERE cover_art_source = 'embedded')::bigint AS embedded,
|
||||||
|
COUNT(*) FILTER (WHERE cover_art_source = 'mbcaa') ::bigint AS mbcaa,
|
||||||
|
COUNT(*) FILTER (WHERE cover_art_source = 'none') ::bigint AS none
|
||||||
|
FROM albums
|
||||||
|
`
|
||||||
|
|
||||||
|
type CountAlbumCoverSourcesRow struct {
|
||||||
|
Untried int64
|
||||||
|
Sidecar int64
|
||||||
|
Embedded int64
|
||||||
|
Mbcaa int64
|
||||||
|
None int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diagnostic for the admin overview: counts by source, including NULL.
|
||||||
|
// Returns one row with named tallies.
|
||||||
|
func (q *Queries) CountAlbumCoverSources(ctx context.Context) (CountAlbumCoverSourcesRow, error) {
|
||||||
|
row := q.db.QueryRow(ctx, countAlbumCoverSources)
|
||||||
|
var i CountAlbumCoverSourcesRow
|
||||||
|
err := row.Scan(
|
||||||
|
&i.Untried,
|
||||||
|
&i.Sidecar,
|
||||||
|
&i.Embedded,
|
||||||
|
&i.Mbcaa,
|
||||||
|
&i.None,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAlbumWithFirstTrackPath = `-- name: GetAlbumWithFirstTrackPath :one
|
||||||
|
SELECT a.id, a.mbid, a.title, a.cover_art_path, a.cover_art_source,
|
||||||
|
t.file_path AS track_file_path
|
||||||
|
FROM albums a
|
||||||
|
LEFT JOIN tracks t ON t.album_id = a.id
|
||||||
|
WHERE a.id = $1
|
||||||
|
ORDER BY t.disc_number NULLS LAST, t.track_number NULLS LAST, t.id
|
||||||
|
LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetAlbumWithFirstTrackPathRow struct {
|
||||||
|
ID pgtype.UUID
|
||||||
|
Mbid *string
|
||||||
|
Title string
|
||||||
|
CoverArtPath *string
|
||||||
|
CoverArtSource *string
|
||||||
|
TrackFilePath *string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the album row and one of its track file paths (used to derive
|
||||||
|
// the album directory). Pick lowest position track for determinism.
|
||||||
|
func (q *Queries) GetAlbumWithFirstTrackPath(ctx context.Context, id pgtype.UUID) (GetAlbumWithFirstTrackPathRow, error) {
|
||||||
|
row := q.db.QueryRow(ctx, getAlbumWithFirstTrackPath, id)
|
||||||
|
var i GetAlbumWithFirstTrackPathRow
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Mbid,
|
||||||
|
&i.Title,
|
||||||
|
&i.CoverArtPath,
|
||||||
|
&i.CoverArtSource,
|
||||||
|
&i.TrackFilePath,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const listAlbumsMissingCover = `-- name: ListAlbumsMissingCover :many
|
||||||
|
|
||||||
|
SELECT a.id, a.mbid, a.title, a.artist_id
|
||||||
|
FROM albums a
|
||||||
|
WHERE a.cover_art_source IS NULL
|
||||||
|
ORDER BY a.created_at ASC
|
||||||
|
LIMIT $1
|
||||||
|
`
|
||||||
|
|
||||||
|
type ListAlbumsMissingCoverRow struct {
|
||||||
|
ID pgtype.UUID
|
||||||
|
Mbid *string
|
||||||
|
Title string
|
||||||
|
ArtistID pgtype.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []ListAlbumsMissingCoverRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i ListAlbumsMissingCoverRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Mbid,
|
||||||
|
&i.Title,
|
||||||
|
&i.ArtistID,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const listAlbumsRetryMissing = `-- name: ListAlbumsRetryMissing :many
|
||||||
|
SELECT a.id, a.mbid, a.title, a.artist_id
|
||||||
|
FROM albums a
|
||||||
|
WHERE a.cover_art_source IS NULL OR a.cover_art_source = 'none'
|
||||||
|
ORDER BY a.created_at ASC
|
||||||
|
LIMIT $1
|
||||||
|
`
|
||||||
|
|
||||||
|
type ListAlbumsRetryMissingRow struct {
|
||||||
|
ID pgtype.UUID
|
||||||
|
Mbid *string
|
||||||
|
Title string
|
||||||
|
ArtistID pgtype.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bulk-retry candidates: NULL (never tried) plus 'none' (tried, failed).
|
||||||
|
// Used by the admin "refetch missing covers" endpoint. LIMIT supplied.
|
||||||
|
func (q *Queries) ListAlbumsRetryMissing(ctx context.Context, limit int32) ([]ListAlbumsRetryMissingRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, listAlbumsRetryMissing, limit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []ListAlbumsRetryMissingRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i ListAlbumsRetryMissingRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Mbid,
|
||||||
|
&i.Title,
|
||||||
|
&i.ArtistID,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const setAlbumCover = `-- name: SetAlbumCover :exec
|
||||||
|
UPDATE albums
|
||||||
|
SET cover_art_path = NULLIF($2::text, ''),
|
||||||
|
cover_art_source = $3
|
||||||
|
WHERE id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
type SetAlbumCoverParams struct {
|
||||||
|
ID pgtype.UUID
|
||||||
|
Column2 string
|
||||||
|
CoverArtSource *string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Persist enrichment result. Pass cover_art_path = ” to clear (turns into NULL).
|
||||||
|
func (q *Queries) SetAlbumCover(ctx context.Context, arg SetAlbumCoverParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, setAlbumCover, arg.ID, arg.Column2, arg.CoverArtSource)
|
||||||
|
return 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 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 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
|
||||||
@@ -152,6 +152,7 @@ func (q *Queries) ListLikedAlbumRows(ctx context.Context, arg ListLikedAlbumRows
|
|||||||
&i.CoverArtPath,
|
&i.CoverArtPath,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.CoverArtSource,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,15 +188,16 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
type Artist struct {
|
type Artist struct {
|
||||||
|
|||||||
@@ -157,7 +157,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, 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, 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
|
||||||
@@ -198,6 +198,7 @@ func (q *Queries) ListRediscoverAlbumsFallbackForUser(ctx context.Context, arg L
|
|||||||
&i.Album.CoverArtPath,
|
&i.Album.CoverArtPath,
|
||||||
&i.Album.CreatedAt,
|
&i.Album.CreatedAt,
|
||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
|
&i.Album.CoverArtSource,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -225,7 +226,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, 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, 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
|
||||||
@@ -266,6 +267,7 @@ func (q *Queries) ListRediscoverAlbumsForUser(ctx context.Context, arg ListRedis
|
|||||||
&i.Album.CoverArtPath,
|
&i.Album.CoverArtPath,
|
||||||
&i.Album.CreatedAt,
|
&i.Album.CreatedAt,
|
||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
|
&i.Album.CoverArtSource,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -201,7 +201,11 @@ type ListPlaylistsByUserAndKindRow struct {
|
|||||||
UpdatedAt pgtype.Timestamptz
|
UpdatedAt pgtype.Timestamptz
|
||||||
}
|
}
|
||||||
|
|
||||||
// Powers GET /api/me/playlists?kind=. Sorted by updated_at DESC.
|
// Used in integration tests; production filter applied in Go
|
||||||
|
// (api.handleListPlaylists fetches via Service.List and filters by Kind in
|
||||||
|
// memory so it can also surface other users' public playlists alongside
|
||||||
|
// the caller's own kind-filtered ones).
|
||||||
|
// Sorted by updated_at DESC.
|
||||||
func (q *Queries) ListPlaylistsByUserAndKind(ctx context.Context, arg ListPlaylistsByUserAndKindParams) ([]ListPlaylistsByUserAndKindRow, error) {
|
func (q *Queries) ListPlaylistsByUserAndKind(ctx context.Context, arg ListPlaylistsByUserAndKindParams) ([]ListPlaylistsByUserAndKindRow, error) {
|
||||||
rows, err := q.db.Query(ctx, listPlaylistsByUserAndKind, arg.UserID, arg.Column2)
|
rows, err := q.db.Query(ctx, listPlaylistsByUserAndKind, arg.UserID, arg.Column2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
-- 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
|
||||||
|
FROM albums a
|
||||||
|
WHERE a.cover_art_source IS NULL
|
||||||
|
ORDER BY a.created_at ASC
|
||||||
|
LIMIT $1;
|
||||||
|
|
||||||
|
-- name: ListAlbumsRetryMissing :many
|
||||||
|
-- Bulk-retry candidates: NULL (never tried) plus 'none' (tried, failed).
|
||||||
|
-- Used by the admin "refetch missing covers" endpoint. LIMIT supplied.
|
||||||
|
SELECT a.id, a.mbid, a.title, a.artist_id
|
||||||
|
FROM albums a
|
||||||
|
WHERE a.cover_art_source IS NULL OR a.cover_art_source = 'none'
|
||||||
|
ORDER BY a.created_at ASC
|
||||||
|
LIMIT $1;
|
||||||
|
|
||||||
|
-- name: GetAlbumWithFirstTrackPath :one
|
||||||
|
-- Returns the album row and one of its track file paths (used to derive
|
||||||
|
-- the album directory). Pick lowest position track for determinism.
|
||||||
|
SELECT a.id, a.mbid, a.title, a.cover_art_path, a.cover_art_source,
|
||||||
|
t.file_path AS track_file_path
|
||||||
|
FROM albums a
|
||||||
|
LEFT JOIN tracks t ON t.album_id = a.id
|
||||||
|
WHERE a.id = $1
|
||||||
|
ORDER BY t.disc_number NULLS LAST, t.track_number NULLS LAST, t.id
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- name: SetAlbumCover :exec
|
||||||
|
-- Persist enrichment result. Pass cover_art_path = '' to clear (turns into NULL).
|
||||||
|
UPDATE albums
|
||||||
|
SET cover_art_path = NULLIF($2::text, ''),
|
||||||
|
cover_art_source = $3
|
||||||
|
WHERE id = $1;
|
||||||
|
|
||||||
|
-- name: ClearAlbumCover :exec
|
||||||
|
-- Used by the admin retry endpoint to wipe state before re-running the
|
||||||
|
-- enricher. Sets both fields to NULL.
|
||||||
|
UPDATE albums
|
||||||
|
SET cover_art_path = NULL,
|
||||||
|
cover_art_source = NULL
|
||||||
|
WHERE id = $1;
|
||||||
|
|
||||||
|
-- name: CountAlbumCoverSources :one
|
||||||
|
-- Diagnostic for the admin overview: counts by source, including NULL.
|
||||||
|
-- Returns one row with named tallies.
|
||||||
|
SELECT
|
||||||
|
COUNT(*) FILTER (WHERE cover_art_source IS NULL) ::bigint AS untried,
|
||||||
|
COUNT(*) FILTER (WHERE cover_art_source = 'sidecar') ::bigint AS sidecar,
|
||||||
|
COUNT(*) FILTER (WHERE cover_art_source = 'embedded')::bigint AS embedded,
|
||||||
|
COUNT(*) FILTER (WHERE cover_art_source = 'mbcaa') ::bigint AS mbcaa,
|
||||||
|
COUNT(*) FILTER (WHERE cover_art_source = 'none') ::bigint AS none
|
||||||
|
FROM albums;
|
||||||
Reference in New Issue
Block a user