diff --git a/internal/db/dbq/albums.sql.go b/internal/db/dbq/albums.sql.go index 0c72f61b..2bb5a100 100644 --- a/internal/db/dbq/albums.sql.go +++ b/internal/db/dbq/albums.sql.go @@ -515,6 +515,25 @@ func (q *Queries) SearchAlbums(ctx context.Context, arg SearchAlbumsParams) ([]A return items, nil } +const setAlbumMbidIfNull = `-- name: SetAlbumMbidIfNull :exec +UPDATE albums + SET mbid = $2, + updated_at = now() + WHERE id = $1 AND mbid IS NULL +` + +type SetAlbumMbidIfNullParams struct { + ID pgtype.UUID + Mbid *string +} + +// M7 #379: heal MBID on existing album rows during rescan. Idempotent — +// only updates when the existing mbid is NULL. +func (q *Queries) SetAlbumMbidIfNull(ctx context.Context, arg SetAlbumMbidIfNullParams) error { + _, err := q.db.Exec(ctx, setAlbumMbidIfNull, arg.ID, arg.Mbid) + return err +} + const upsertAlbum = `-- name: UpsertAlbum :one INSERT INTO albums (title, sort_title, artist_id, release_date, mbid, cover_art_path) VALUES ($1, $2, $3, $4, $5, $6) diff --git a/internal/db/dbq/artists.sql.go b/internal/db/dbq/artists.sql.go index 98e6a5df..2df9bf12 100644 --- a/internal/db/dbq/artists.sql.go +++ b/internal/db/dbq/artists.sql.go @@ -327,6 +327,26 @@ func (q *Queries) SearchArtists(ctx context.Context, arg SearchArtistsParams) ([ return items, nil } +const setArtistMbidIfNull = `-- name: SetArtistMbidIfNull :exec +UPDATE artists + SET mbid = $2, + updated_at = now() + WHERE id = $1 AND mbid IS NULL +` + +type SetArtistMbidIfNullParams struct { + ID pgtype.UUID + Mbid *string +} + +// M7 #379: heal MBID on existing artist rows during rescan. Idempotent — +// only updates when the existing mbid is NULL, so concurrent scans don't +// overwrite an existing value with a stale one. +func (q *Queries) SetArtistMbidIfNull(ctx context.Context, arg SetArtistMbidIfNullParams) error { + _, err := q.db.Exec(ctx, setArtistMbidIfNull, arg.ID, arg.Mbid) + return err +} + const upsertArtist = `-- name: UpsertArtist :one INSERT INTO artists (name, sort_name, mbid) VALUES ($1, $2, $3) diff --git a/internal/db/queries/albums.sql b/internal/db/queries/albums.sql index 68c4bed4..640b8e16 100644 --- a/internal/db/queries/albums.sql +++ b/internal/db/queries/albums.sql @@ -96,3 +96,11 @@ DELETE FROM albums a WHERE a.id = $1 AND NOT EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = a.id) RETURNING a.id, a.artist_id; + +-- name: SetAlbumMbidIfNull :exec +-- M7 #379: heal MBID on existing album rows during rescan. Idempotent — +-- only updates when the existing mbid is NULL. +UPDATE albums + SET mbid = $2, + updated_at = now() + WHERE id = $1 AND mbid IS NULL; diff --git a/internal/db/queries/artists.sql b/internal/db/queries/artists.sql index a9cf20c5..b0efef49 100644 --- a/internal/db/queries/artists.sql +++ b/internal/db/queries/artists.sql @@ -74,3 +74,12 @@ WHERE a.id = $1 AND NOT EXISTS (SELECT 1 FROM albums al WHERE al.artist_id = a.id) AND NOT EXISTS (SELECT 1 FROM tracks t WHERE t.artist_id = a.id) RETURNING a.id; + +-- name: SetArtistMbidIfNull :exec +-- M7 #379: heal MBID on existing artist rows during rescan. Idempotent — +-- only updates when the existing mbid is NULL, so concurrent scans don't +-- overwrite an existing value with a stale one. +UPDATE artists + SET mbid = $2, + updated_at = now() + WHERE id = $1 AND mbid IS NULL; diff --git a/internal/library/scanner.go b/internal/library/scanner.go index 42b06485..c44f5277 100644 --- a/internal/library/scanner.go +++ b/internal/library/scanner.go @@ -201,6 +201,19 @@ func (s *Scanner) scanFile(ctx context.Context, q *dbq.Queries, path string, sta func (s *Scanner) resolveArtist(ctx context.Context, q *dbq.Queries, name, mbid string) (dbq.Artist, error) { existing, err := q.GetArtistByName(ctx, name) if err == nil { + // Heal: backfill mbid on a previously-imported row if we have one now. + if mbid != "" && (existing.Mbid == nil || *existing.Mbid == "") { + m := mbid + if uerr := q.SetArtistMbidIfNull(ctx, dbq.SetArtistMbidIfNullParams{ + ID: existing.ID, + Mbid: &m, + }); uerr != nil { + s.logger.Warn("library scan: heal artist mbid failed", + "artist_id", existing.ID, "err", uerr) + } else { + existing.Mbid = &m + } + } return existing, nil } if !errors.Is(err, pgx.ErrNoRows) { @@ -220,6 +233,19 @@ func (s *Scanner) resolveArtist(ctx context.Context, q *dbq.Queries, name, mbid func (s *Scanner) resolveAlbum(ctx context.Context, q *dbq.Queries, artistID pgtype.UUID, title string, year int, mbid string) (dbq.Album, error) { existing, err := q.GetAlbumByArtistAndTitle(ctx, dbq.GetAlbumByArtistAndTitleParams{ArtistID: artistID, Title: title}) if err == nil { + // Heal: backfill mbid on a previously-imported row if we have one now. + if mbid != "" && (existing.Mbid == nil || *existing.Mbid == "") { + m := mbid + if uerr := q.SetAlbumMbidIfNull(ctx, dbq.SetAlbumMbidIfNullParams{ + ID: existing.ID, + Mbid: &m, + }); uerr != nil { + s.logger.Warn("library scan: heal album mbid failed", + "album_id", existing.ID, "err", uerr) + } else { + existing.Mbid = &m + } + } return existing, nil } if !errors.Is(err, pgx.ErrNoRows) {