feat(lidarr): ask Lidarr for release groups; repair stored release-id requests (M483 #5244)
release / go (push) Successful in 2m33s
release / web (push) Successful in 1m41s
release / govulncheck (push) Successful in 22s
release / integration (push) Successful in 6m0s
release / android (push) Successful in 6m7s
release / Build signed APK (releases and dev) (push) Successful in 6m8s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 1m43s
release / Verify release artifacts (tag releases only) (push) Skipped

Lidarr's metadata is keyed by MusicBrainz release group, but re-acquisition
requested albums by their release id, so every add came back "not found".

- Sweeper requests an album by its tag-supplied release group, else the one
  MusicBrainz names (cached onto the album). An album MusicBrainz cannot name
  is skipped and counted, with no attempt spent.
- Reconciler: an add refused as not found re-reads the request's album id as
  a release (library first, then MusicBrainz), rewrites the request to the
  group and adds again. This repairs the requests already stored.
- Completion matches an album by release id or release group, and only once a
  track of it is on disk, so a re-acquisition request no longer completes
  against the row of the album it is trying to bring back.

Closes #5241.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 11:38:45 -04:00
co-authored by Claude Opus 5.5
parent 2b4e274b12
commit e509d7d5a9
11 changed files with 596 additions and 27 deletions
+35
View File
@@ -248,6 +248,22 @@ func (q *Queries) GetAlbumsByIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([
return items, nil
}
const getReleaseGroupForReleaseMbid = `-- name: GetReleaseGroupForReleaseMbid :one
SELECT release_group_mbid::text
FROM albums
WHERE mbid = $1 AND release_group_mbid IS NOT NULL
LIMIT 1
`
// #5241: the release group the library already knows for a release id, so a
// request carrying a release id can be repaired without asking MusicBrainz.
func (q *Queries) GetReleaseGroupForReleaseMbid(ctx context.Context, mbid *string) (string, error) {
row := q.db.QueryRow(ctx, getReleaseGroupForReleaseMbid, mbid)
var release_group_mbid string
err := row.Scan(&release_group_mbid)
return release_group_mbid, err
}
const listAlbumsAlphaByArtist = `-- name: ListAlbumsAlphaByArtist :many
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, albums.cover_art_sources_version, albums.release_group_mbid, artists.sort_name AS artist_sort_name
FROM albums
@@ -852,6 +868,25 @@ func (q *Queries) SetAlbumReleaseGroupMbidIfNull(ctx context.Context, arg SetAlb
return err
}
const setReleaseGroupForReleaseMbidIfNull = `-- name: SetReleaseGroupForReleaseMbidIfNull :exec
UPDATE albums
SET release_group_mbid = $1::text,
updated_at = now()
WHERE mbid = $2::text AND release_group_mbid IS NULL
`
type SetReleaseGroupForReleaseMbidIfNullParams struct {
ReleaseGroupMbid string
ReleaseMbid string
}
// #5241: cache a MusicBrainz-resolved release group onto the album row that
// holds the release, filling only a NULL.
func (q *Queries) SetReleaseGroupForReleaseMbidIfNull(ctx context.Context, arg SetReleaseGroupForReleaseMbidIfNullParams) error {
_, err := q.db.Exec(ctx, setReleaseGroupForReleaseMbidIfNull, arg.ReleaseGroupMbid, arg.ReleaseMbid)
return err
}
const upsertAlbum = `-- name: UpsertAlbum :one
INSERT INTO albums (title, sort_title, artist_id, release_date, mbid, cover_art_path, release_group_mbid)
VALUES ($1, $2, $3, $4, $5, $6, $7)
+20
View File
@@ -560,3 +560,23 @@ func (q *Queries) RejectLidarrRequest(ctx context.Context, arg RejectLidarrReque
)
return i, err
}
const setLidarrRequestAlbumMbid = `-- name: SetLidarrRequestAlbumMbid :exec
UPDATE lidarr_requests
SET lidarr_album_mbid = $2,
updated_at = now()
WHERE id = $1
`
type SetLidarrRequestAlbumMbidParams struct {
ID pgtype.UUID
LidarrAlbumMbid *string
}
// #5241: repoint a request at the album's release group. Requests made before
// the release-group read named albums by MusicBrainz release id, which Lidarr
// does not index; the reconciler rewrites them once it has resolved the group.
func (q *Queries) SetLidarrRequestAlbumMbid(ctx context.Context, arg SetLidarrRequestAlbumMbidParams) error {
_, err := q.db.Exec(ctx, setLidarrRequestAlbumMbid, arg.ID, arg.LidarrAlbumMbid)
return err
}
+12 -9
View File
@@ -114,6 +114,7 @@ const listAlbumsDueReacquisition = `-- name: ListAlbumsDueReacquisition :many
SELECT albums.id AS album_id,
albums.title AS album_title,
albums.mbid AS album_mbid,
albums.release_group_mbid AS album_release_group_mbid,
artists.id AS artist_id,
artists.name AS artist_name,
artists.mbid AS artist_mbid,
@@ -135,7 +136,7 @@ SELECT albums.id AS album_id,
* POWER(2, GREATEST(COALESCE(r.attempts, 0) - 1, 0)))::int,
$3::int))
)
GROUP BY albums.id, albums.title, albums.mbid,
GROUP BY albums.id, albums.title, albums.mbid, albums.release_group_mbid,
artists.id, artists.name, artists.mbid, r.attempts, r.last_attempt_at
ORDER BY r.last_attempt_at NULLS FIRST, albums.sort_title
LIMIT $4
@@ -149,14 +150,15 @@ type ListAlbumsDueReacquisitionParams struct {
}
type ListAlbumsDueReacquisitionRow struct {
AlbumID pgtype.UUID
AlbumTitle string
AlbumMbid *string
ArtistID pgtype.UUID
ArtistName string
ArtistMbid *string
MissingTrackCount int64
Attempts int32
AlbumID pgtype.UUID
AlbumTitle string
AlbumMbid *string
AlbumReleaseGroupMbid *string
ArtistID pgtype.UUID
ArtistName string
ArtistMbid *string
MissingTrackCount int64
Attempts int32
}
// The sweeper's selection. An album qualifies when:
@@ -194,6 +196,7 @@ func (q *Queries) ListAlbumsDueReacquisition(ctx context.Context, arg ListAlbums
&i.AlbumID,
&i.AlbumTitle,
&i.AlbumMbid,
&i.AlbumReleaseGroupMbid,
&i.ArtistID,
&i.ArtistName,
&i.ArtistMbid,