refactor(server/coverart): provider interface takes ArtistRef/AlbumRef

Changes the AlbumCoverProvider and ArtistArtProvider interfaces from
MBID-only string parameters to ref-struct signatures so name-based
providers (Deezer, Last.fm in upcoming tasks) can act on rows
without MBIDs.

Today, 53 of 213 artists in the dev DB have NULL MBIDs — featured
artists, remixers, and compilation contributors created from
track-tag splits where the primary-artist MBID is in the file but
collaborator IDs aren't. These rows are unreachable by the current
MBID-only chain. Refactoring the interface unblocks future
name-based providers from acting on them.

TheAudioDB and MBCAA keep MBID-only behavior internally: they return
ErrNotFound when ref.MBID is empty rather than attempting a
name-based fallback. The MBID guard inside EnrichAlbum/EnrichArtist
is removed; providers receive the ref unconditionally and decide
whether they can act on it.

GetAlbumWithFirstTrackPath now JOINs artists to return artist_name
alongside the existing fields, supporting AlbumRef construction.

No behavioral change today (only TheAudioDB + MBCAA registered, both
keep MBID-only behavior). The change unblocks T3 (Deezer) and T4
(Last.fm) which can now register and act on every artist/album
regardless of MBID presence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-07 07:44:34 -04:00
parent 9a705d0ba4
commit 425f12db38
13 changed files with 99 additions and 58 deletions
+8 -2
View File
@@ -74,8 +74,10 @@ func (q *Queries) CountAlbumCoverSources(ctx context.Context) (CountAlbumCoverSo
const getAlbumWithFirstTrackPath = `-- name: GetAlbumWithFirstTrackPath :one
SELECT a.id, a.mbid, a.title, a.cover_art_path, a.cover_art_source,
ar.name AS artist_name,
t.file_path AS track_file_path
FROM albums a
JOIN artists ar ON ar.id = a.artist_id
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
@@ -88,11 +90,14 @@ type GetAlbumWithFirstTrackPathRow struct {
Title string
CoverArtPath *string
CoverArtSource *string
ArtistName 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.
// Returns the album row, the artist's name (used by name-based art
// providers like Deezer/Last.fm), and one of its track file paths
// (used to derive the album directory for sidecar writes). 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
@@ -102,6 +107,7 @@ func (q *Queries) GetAlbumWithFirstTrackPath(ctx context.Context, id pgtype.UUID
&i.Title,
&i.CoverArtPath,
&i.CoverArtSource,
&i.ArtistName,
&i.TrackFilePath,
)
return i, err
+6 -2
View File
@@ -23,11 +23,15 @@ SELECT a.id, a.mbid, a.title, a.artist_id
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.
-- Returns the album row, the artist's name (used by name-based art
-- providers like Deezer/Last.fm), and one of its track file paths
-- (used to derive the album directory for sidecar writes). Pick
-- lowest position track for determinism.
SELECT a.id, a.mbid, a.title, a.cover_art_path, a.cover_art_source,
ar.name AS artist_name,
t.file_path AS track_file_path
FROM albums a
JOIN artists ar ON ar.id = a.artist_id
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