feat(library): store each album's MusicBrainz release-group id (M483 #5242)
release / govulncheck (push) Successful in 22s
release / web (push) Successful in 1m14s
release / go (push) Successful in 1m33s
release / integration (push) Successful in 4m36s
release / Attach APK to the Release (tag releases only) (push) Canceled after 0s
release / Build + push container image (push) Canceled after 0s
release / Verify release artifacts (tag releases only) (push) Canceled after 0s
release / android (push) Canceled after 5m24s
release / Build signed APK (releases and dev) (push) Canceled after 5m29s

albums.mbid is the release id (Picard's musicbrainz_albumid, one edition).
Lidarr names albums by release group, so re-acquisition and request
completion need that id too (#5241).

Migration 0070 adds albums.release_group_mbid (nullable, non-unique index:
several releases share a group). The scanner reads musicbrainz_releasegroupid
through extractReleaseGroupMBID, writes it on insert and heals it onto
existing rows when NULL. tagReadVersion goes to 3 so the next scan fills
it for the library already indexed, bound by tag reads (no ffprobe, no
decode).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 11:33:26 -04:00
co-authored by Claude Opus 5.5
parent bf6364b709
commit 5c19a916ba
13 changed files with 250 additions and 35 deletions
+26 -3
View File
@@ -56,7 +56,11 @@ var audioExtensions = map[string]bool{
// "EDM", "Children'S Music" -> "Children's Music". Bumped rather than left
// to new files only because taste_profile.sql reads tracks.genre directly,
// so a half-repaired library would carry both spellings as separate tags.
const tagReadVersion int16 = 2
// 3: the MusicBrainz release-group id is read into albums.release_group_mbid
// (#5241). Lidarr names albums by release group, not by the release id
// albums.mbid holds, so re-acquisition and request completion need it for
// the albums already in the library, not only for files added from now on.
const tagReadVersion int16 = 3
type Stats struct {
Scanned int `json:"scanned"`
@@ -258,6 +262,7 @@ func (s *Scanner) scanFile(
}
albumMBID, artistMBID := extractMBIDs(meta)
recordingMBID := extractRecordingMBID(meta)
releaseGroupMBID := extractReleaseGroupMBID(meta)
artistName := meta.Artist()
if artistName == "" {
@@ -276,7 +281,7 @@ func (s *Scanner) scanFile(
if err != nil {
return pgtype.UUID{}, false, fmt.Errorf("artist: %w", err)
}
album, err := s.resolveAlbum(ctx, q, artist.ID, albumTitle, meta.Year(), albumMBID)
album, err := s.resolveAlbum(ctx, q, artist.ID, albumTitle, meta.Year(), albumMBID, releaseGroupMBID)
if err != nil {
return pgtype.UUID{}, false, fmt.Errorf("album: %w", err)
}
@@ -518,9 +523,23 @@ func (s *Scanner) resolveArtist(ctx context.Context, q *dbq.Queries, name, mbid
return artist, nil
}
func (s *Scanner) resolveAlbum(ctx context.Context, q *dbq.Queries, artistID pgtype.UUID, title string, year int, mbid string) (dbq.Album, error) {
func (s *Scanner) resolveAlbum(ctx context.Context, q *dbq.Queries, artistID pgtype.UUID, title string, year int, mbid, releaseGroupMBID string) (dbq.Album, error) {
existing, err := q.GetAlbumByArtistAndTitle(ctx, dbq.GetAlbumByArtistAndTitleParams{ArtistID: artistID, Title: title})
if err == nil {
// Heal the release group the same way. Not unique, so no conflict to
// handle: every release of a group may carry it.
if releaseGroupMBID != "" && existing.ReleaseGroupMbid == nil {
rg := releaseGroupMBID
if uerr := q.SetAlbumReleaseGroupMbidIfNull(ctx, dbq.SetAlbumReleaseGroupMbidIfNullParams{
ID: existing.ID,
ReleaseGroupMbid: &rg,
}); uerr != nil {
s.logger.Warn("library scan: heal album release group failed",
"album_id", existing.ID, "err", uerr)
} else {
existing.ReleaseGroupMbid = &rg
}
}
// Heal: backfill mbid on a previously-imported row if we have one now.
if mbid != "" && (existing.Mbid == nil || *existing.Mbid == "") {
m := mbid
@@ -564,6 +583,10 @@ func (s *Scanner) resolveAlbum(ctx context.Context, q *dbq.Queries, artistID pgt
m := mbid
params.Mbid = &m
}
if releaseGroupMBID != "" {
rg := releaseGroupMBID
params.ReleaseGroupMbid = &rg
}
album, err := q.UpsertAlbum(ctx, params)
if err != nil {
return dbq.Album{}, err