Files
minstrel/internal/db/dbq/covers.sql.go
T
bvandeusen 34615fffbd feat(server/m7-380): one-shot MBID backfill worker for existing libraries
Adds a boot-time goroutine that walks albums with NULL mbid, re-reads
tags from one track per album via dhowden/tag, and persists album + artist
MBIDs. Healed albums also get their cover_art_source='none' cleared so the
enricher's next batch retries the MBCAA fetch. Caps at 5000 albums per
boot to avoid stalling startup on huge libraries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 19:40:27 -04:00

214 lines
5.6 KiB
Go

// 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 clearAlbumCoverNone = `-- name: ClearAlbumCoverNone :exec
UPDATE albums
SET cover_art_source = NULL
WHERE id = $1 AND cover_art_source = 'none'
`
// M7 #380: when MBID backfill heals an album, clear its 'none' cover_art_source
// back to NULL so the enricher's next batch retries the MBCAA fetch.
// Idempotent — only updates rows currently stamped 'none'.
func (q *Queries) ClearAlbumCoverNone(ctx context.Context, id pgtype.UUID) error {
_, err := q.db.Exec(ctx, clearAlbumCoverNone, 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
}