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>
This commit is contained in:
@@ -347,6 +347,59 @@ func (q *Queries) ListAlbumsByGenre(ctx context.Context, arg ListAlbumsByGenrePa
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listAlbumsMissingMbidWithTrack = `-- name: ListAlbumsMissingMbidWithTrack :many
|
||||
SELECT a.id AS album_id,
|
||||
a.artist_id AS artist_id,
|
||||
a.title AS title,
|
||||
t.file_path AS track_file_path
|
||||
FROM albums a
|
||||
JOIN LATERAL (
|
||||
SELECT file_path
|
||||
FROM tracks
|
||||
WHERE album_id = a.id
|
||||
ORDER BY disc_number NULLS LAST, track_number NULLS LAST, id
|
||||
LIMIT 1
|
||||
) t ON true
|
||||
WHERE a.mbid IS NULL
|
||||
ORDER BY a.created_at ASC
|
||||
LIMIT $1
|
||||
`
|
||||
|
||||
type ListAlbumsMissingMbidWithTrackRow struct {
|
||||
AlbumID pgtype.UUID
|
||||
ArtistID pgtype.UUID
|
||||
Title string
|
||||
TrackFilePath string
|
||||
}
|
||||
|
||||
// One-shot MBID backfill: returns each album where mbid IS NULL alongside
|
||||
// one of its tracks' file_path so the worker can re-read tags. LIMIT
|
||||
// supplied by caller for batching/progress purposes.
|
||||
func (q *Queries) ListAlbumsMissingMbidWithTrack(ctx context.Context, limit int32) ([]ListAlbumsMissingMbidWithTrackRow, error) {
|
||||
rows, err := q.db.Query(ctx, listAlbumsMissingMbidWithTrack, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []ListAlbumsMissingMbidWithTrackRow
|
||||
for rows.Next() {
|
||||
var i ListAlbumsMissingMbidWithTrackRow
|
||||
if err := rows.Scan(
|
||||
&i.AlbumID,
|
||||
&i.ArtistID,
|
||||
&i.Title,
|
||||
&i.TrackFilePath,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
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
|
||||
`
|
||||
|
||||
@@ -25,6 +25,20 @@ func (q *Queries) ClearAlbumCover(ctx context.Context, id pgtype.UUID) error {
|
||||
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,
|
||||
|
||||
@@ -104,3 +104,23 @@ UPDATE albums
|
||||
SET mbid = $2,
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND mbid IS NULL;
|
||||
|
||||
-- name: ListAlbumsMissingMbidWithTrack :many
|
||||
-- One-shot MBID backfill: returns each album where mbid IS NULL alongside
|
||||
-- one of its tracks' file_path so the worker can re-read tags. LIMIT
|
||||
-- supplied by caller for batching/progress purposes.
|
||||
SELECT a.id AS album_id,
|
||||
a.artist_id AS artist_id,
|
||||
a.title AS title,
|
||||
t.file_path AS track_file_path
|
||||
FROM albums a
|
||||
JOIN LATERAL (
|
||||
SELECT file_path
|
||||
FROM tracks
|
||||
WHERE album_id = a.id
|
||||
ORDER BY disc_number NULLS LAST, track_number NULLS LAST, id
|
||||
LIMIT 1
|
||||
) t ON true
|
||||
WHERE a.mbid IS NULL
|
||||
ORDER BY a.created_at ASC
|
||||
LIMIT $1;
|
||||
|
||||
@@ -44,6 +44,14 @@ UPDATE albums
|
||||
cover_art_source = NULL
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: ClearAlbumCoverNone :exec
|
||||
-- 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'.
|
||||
UPDATE albums
|
||||
SET cover_art_source = NULL
|
||||
WHERE id = $1 AND cover_art_source = 'none';
|
||||
|
||||
-- name: CountAlbumCoverSources :one
|
||||
-- Diagnostic for the admin overview: counts by source, including NULL.
|
||||
-- Returns one row with named tallies.
|
||||
|
||||
Reference in New Issue
Block a user