M483: release-group ids for Lidarr #139
@@ -72,7 +72,7 @@ func (q *Queries) DeleteAlbumIfEmpty(ctx context.Context, id pgtype.UUID) (Delet
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getAlbumByArtistAndTitle = `-- name: GetAlbumByArtistAndTitle :one
|
const getAlbumByArtistAndTitle = `-- name: GetAlbumByArtistAndTitle :one
|
||||||
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version FROM albums WHERE artist_id = $1 AND title = $2 LIMIT 1
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version, release_group_mbid FROM albums WHERE artist_id = $1 AND title = $2 LIMIT 1
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetAlbumByArtistAndTitleParams struct {
|
type GetAlbumByArtistAndTitleParams struct {
|
||||||
@@ -96,12 +96,13 @@ func (q *Queries) GetAlbumByArtistAndTitle(ctx context.Context, arg GetAlbumByAr
|
|||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
&i.CoverArtSourcesVersion,
|
&i.CoverArtSourcesVersion,
|
||||||
|
&i.ReleaseGroupMbid,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAlbumByID = `-- name: GetAlbumByID :one
|
const getAlbumByID = `-- name: GetAlbumByID :one
|
||||||
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version FROM albums WHERE id = $1
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version, release_group_mbid FROM albums WHERE id = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetAlbumByID(ctx context.Context, id pgtype.UUID) (Album, error) {
|
func (q *Queries) GetAlbumByID(ctx context.Context, id pgtype.UUID) (Album, error) {
|
||||||
@@ -119,6 +120,7 @@ func (q *Queries) GetAlbumByID(ctx context.Context, id pgtype.UUID) (Album, erro
|
|||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
&i.CoverArtSourcesVersion,
|
&i.CoverArtSourcesVersion,
|
||||||
|
&i.ReleaseGroupMbid,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
@@ -172,7 +174,7 @@ func (q *Queries) GetAlbumCoverageRollup(ctx context.Context) (GetAlbumCoverageR
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getAlbumWithArtist = `-- name: GetAlbumWithArtist :one
|
const getAlbumWithArtist = `-- name: GetAlbumWithArtist :one
|
||||||
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, artists.name AS artist_name
|
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.name AS artist_name
|
||||||
FROM albums
|
FROM albums
|
||||||
JOIN artists ON artists.id = albums.artist_id
|
JOIN artists ON artists.id = albums.artist_id
|
||||||
WHERE albums.id = $1
|
WHERE albums.id = $1
|
||||||
@@ -201,13 +203,14 @@ func (q *Queries) GetAlbumWithArtist(ctx context.Context, id pgtype.UUID) (GetAl
|
|||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
&i.Album.CoverArtSourcesVersion,
|
&i.Album.CoverArtSourcesVersion,
|
||||||
|
&i.Album.ReleaseGroupMbid,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAlbumsByIDs = `-- name: GetAlbumsByIDs :many
|
const getAlbumsByIDs = `-- name: GetAlbumsByIDs :many
|
||||||
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version FROM albums WHERE id = ANY($1::uuid[])
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version, release_group_mbid FROM albums WHERE id = ANY($1::uuid[])
|
||||||
`
|
`
|
||||||
|
|
||||||
// Batched lookup used by /api/library/sync to hydrate upsert payloads
|
// Batched lookup used by /api/library/sync to hydrate upsert payloads
|
||||||
@@ -233,6 +236,7 @@ func (q *Queries) GetAlbumsByIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([
|
|||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
&i.CoverArtSourcesVersion,
|
&i.CoverArtSourcesVersion,
|
||||||
|
&i.ReleaseGroupMbid,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -245,7 +249,7 @@ func (q *Queries) GetAlbumsByIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsAlphaByArtist = `-- name: ListAlbumsAlphaByArtist :many
|
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, artists.sort_name AS artist_sort_name
|
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
|
FROM albums
|
||||||
JOIN artists ON artists.id = albums.artist_id
|
JOIN artists ON artists.id = albums.artist_id
|
||||||
ORDER BY artists.sort_name, albums.sort_title
|
ORDER BY artists.sort_name, albums.sort_title
|
||||||
@@ -285,6 +289,7 @@ func (q *Queries) ListAlbumsAlphaByArtist(ctx context.Context, arg ListAlbumsAlp
|
|||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
&i.Album.CoverArtSourcesVersion,
|
&i.Album.CoverArtSourcesVersion,
|
||||||
|
&i.Album.ReleaseGroupMbid,
|
||||||
&i.ArtistSortName,
|
&i.ArtistSortName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -298,7 +303,7 @@ func (q *Queries) ListAlbumsAlphaByArtist(ctx context.Context, arg ListAlbumsAlp
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsAlphaByName = `-- name: ListAlbumsAlphaByName :many
|
const listAlbumsAlphaByName = `-- name: ListAlbumsAlphaByName :many
|
||||||
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version FROM albums ORDER BY sort_title LIMIT $1 OFFSET $2
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version, release_group_mbid FROM albums ORDER BY sort_title LIMIT $1 OFFSET $2
|
||||||
`
|
`
|
||||||
|
|
||||||
type ListAlbumsAlphaByNameParams struct {
|
type ListAlbumsAlphaByNameParams struct {
|
||||||
@@ -327,6 +332,7 @@ func (q *Queries) ListAlbumsAlphaByName(ctx context.Context, arg ListAlbumsAlpha
|
|||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
&i.CoverArtSourcesVersion,
|
&i.CoverArtSourcesVersion,
|
||||||
|
&i.ReleaseGroupMbid,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -339,7 +345,7 @@ func (q *Queries) ListAlbumsAlphaByName(ctx context.Context, arg ListAlbumsAlpha
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsAlphaWithArtist = `-- name: ListAlbumsAlphaWithArtist :many
|
const listAlbumsAlphaWithArtist = `-- name: ListAlbumsAlphaWithArtist :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, artists.name AS artist_name
|
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.name AS artist_name
|
||||||
FROM albums
|
FROM albums
|
||||||
JOIN artists ON artists.id = albums.artist_id
|
JOIN artists ON artists.id = albums.artist_id
|
||||||
ORDER BY albums.sort_title, albums.id
|
ORDER BY albums.sort_title, albums.id
|
||||||
@@ -379,6 +385,7 @@ func (q *Queries) ListAlbumsAlphaWithArtist(ctx context.Context, arg ListAlbumsA
|
|||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
&i.Album.CoverArtSourcesVersion,
|
&i.Album.CoverArtSourcesVersion,
|
||||||
|
&i.Album.ReleaseGroupMbid,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -392,7 +399,7 @@ func (q *Queries) ListAlbumsAlphaWithArtist(ctx context.Context, arg ListAlbumsA
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsByArtist = `-- name: ListAlbumsByArtist :many
|
const listAlbumsByArtist = `-- name: ListAlbumsByArtist :many
|
||||||
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version FROM albums WHERE artist_id = $1 ORDER BY release_date NULLS LAST, sort_title
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version, release_group_mbid FROM albums WHERE artist_id = $1 ORDER BY release_date NULLS LAST, sort_title
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) ListAlbumsByArtist(ctx context.Context, artistID pgtype.UUID) ([]Album, error) {
|
func (q *Queries) ListAlbumsByArtist(ctx context.Context, artistID pgtype.UUID) ([]Album, error) {
|
||||||
@@ -416,6 +423,7 @@ func (q *Queries) ListAlbumsByArtist(ctx context.Context, artistID pgtype.UUID)
|
|||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
&i.CoverArtSourcesVersion,
|
&i.CoverArtSourcesVersion,
|
||||||
|
&i.ReleaseGroupMbid,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -428,7 +436,7 @@ func (q *Queries) ListAlbumsByArtist(ctx context.Context, artistID pgtype.UUID)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsByArtistWithTrackCount = `-- name: ListAlbumsByArtistWithTrackCount :many
|
const listAlbumsByArtistWithTrackCount = `-- name: ListAlbumsByArtistWithTrackCount :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,
|
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,
|
||||||
(SELECT count(*) FROM tracks t WHERE t.album_id = albums.id)::bigint
|
(SELECT count(*) FROM tracks t WHERE t.album_id = albums.id)::bigint
|
||||||
AS track_count
|
AS track_count
|
||||||
FROM albums
|
FROM albums
|
||||||
@@ -465,6 +473,7 @@ func (q *Queries) ListAlbumsByArtistWithTrackCount(ctx context.Context, artistID
|
|||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
&i.Album.CoverArtSourcesVersion,
|
&i.Album.CoverArtSourcesVersion,
|
||||||
|
&i.Album.ReleaseGroupMbid,
|
||||||
&i.TrackCount,
|
&i.TrackCount,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -478,7 +487,7 @@ func (q *Queries) ListAlbumsByArtistWithTrackCount(ctx context.Context, artistID
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsByGenre = `-- name: ListAlbumsByGenre :many
|
const listAlbumsByGenre = `-- name: ListAlbumsByGenre :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
|
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
|
||||||
FROM albums
|
FROM albums
|
||||||
WHERE EXISTS (
|
WHERE EXISTS (
|
||||||
SELECT 1
|
SELECT 1
|
||||||
@@ -531,6 +540,7 @@ func (q *Queries) ListAlbumsByGenre(ctx context.Context, arg ListAlbumsByGenrePa
|
|||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
&i.CoverArtSourcesVersion,
|
&i.CoverArtSourcesVersion,
|
||||||
|
&i.ReleaseGroupMbid,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -599,7 +609,7 @@ func (q *Queries) ListAlbumsMissingMbidWithTrack(ctx context.Context, limit int3
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsNewest = `-- name: ListAlbumsNewest :many
|
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, cover_art_sources_version FROM albums ORDER BY created_at DESC LIMIT $1 OFFSET $2
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version, release_group_mbid FROM albums ORDER BY created_at DESC LIMIT $1 OFFSET $2
|
||||||
`
|
`
|
||||||
|
|
||||||
type ListAlbumsNewestParams struct {
|
type ListAlbumsNewestParams struct {
|
||||||
@@ -628,6 +638,7 @@ func (q *Queries) ListAlbumsNewest(ctx context.Context, arg ListAlbumsNewestPara
|
|||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
&i.CoverArtSourcesVersion,
|
&i.CoverArtSourcesVersion,
|
||||||
|
&i.ReleaseGroupMbid,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -640,7 +651,7 @@ func (q *Queries) ListAlbumsNewest(ctx context.Context, arg ListAlbumsNewestPara
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsRandom = `-- name: ListAlbumsRandom :many
|
const listAlbumsRandom = `-- name: ListAlbumsRandom :many
|
||||||
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version FROM albums ORDER BY random() LIMIT $1
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version, release_group_mbid FROM albums ORDER BY random() LIMIT $1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) ListAlbumsRandom(ctx context.Context, limit int32) ([]Album, error) {
|
func (q *Queries) ListAlbumsRandom(ctx context.Context, limit int32) ([]Album, error) {
|
||||||
@@ -664,6 +675,7 @@ func (q *Queries) ListAlbumsRandom(ctx context.Context, limit int32) ([]Album, e
|
|||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
&i.CoverArtSourcesVersion,
|
&i.CoverArtSourcesVersion,
|
||||||
|
&i.ReleaseGroupMbid,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -676,7 +688,7 @@ func (q *Queries) ListAlbumsRandom(ctx context.Context, limit int32) ([]Album, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listRecentlyAddedAlbumsWithArtist = `-- name: ListRecentlyAddedAlbumsWithArtist :many
|
const listRecentlyAddedAlbumsWithArtist = `-- name: ListRecentlyAddedAlbumsWithArtist :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, artists.name AS artist_name
|
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.name AS artist_name
|
||||||
FROM albums
|
FROM albums
|
||||||
JOIN artists ON artists.id = albums.artist_id
|
JOIN artists ON artists.id = albums.artist_id
|
||||||
ORDER BY albums.created_at DESC, albums.id
|
ORDER BY albums.created_at DESC, albums.id
|
||||||
@@ -713,6 +725,7 @@ func (q *Queries) ListRecentlyAddedAlbumsWithArtist(ctx context.Context, limit i
|
|||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
&i.Album.CoverArtSourcesVersion,
|
&i.Album.CoverArtSourcesVersion,
|
||||||
|
&i.Album.ReleaseGroupMbid,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -726,7 +739,7 @@ func (q *Queries) ListRecentlyAddedAlbumsWithArtist(ctx context.Context, limit i
|
|||||||
}
|
}
|
||||||
|
|
||||||
const searchAlbums = `-- name: SearchAlbums :many
|
const searchAlbums = `-- name: SearchAlbums :many
|
||||||
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version FROM albums
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version, release_group_mbid FROM albums
|
||||||
WHERE title ILIKE '%' || $1 || '%'
|
WHERE title ILIKE '%' || $1 || '%'
|
||||||
ORDER BY sort_title
|
ORDER BY sort_title
|
||||||
LIMIT $2 OFFSET $3
|
LIMIT $2 OFFSET $3
|
||||||
@@ -759,6 +772,7 @@ func (q *Queries) SearchAlbums(ctx context.Context, arg SearchAlbumsParams) ([]A
|
|||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
&i.CoverArtSourcesVersion,
|
&i.CoverArtSourcesVersion,
|
||||||
|
&i.ReleaseGroupMbid,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -818,9 +832,29 @@ func (q *Queries) SetAlbumMbidIfNull(ctx context.Context, arg SetAlbumMbidIfNull
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setAlbumReleaseGroupMbidIfNull = `-- name: SetAlbumReleaseGroupMbidIfNull :exec
|
||||||
|
UPDATE albums
|
||||||
|
SET release_group_mbid = $2,
|
||||||
|
updated_at = now()
|
||||||
|
WHERE id = $1 AND release_group_mbid IS NULL
|
||||||
|
`
|
||||||
|
|
||||||
|
type SetAlbumReleaseGroupMbidIfNullParams struct {
|
||||||
|
ID pgtype.UUID
|
||||||
|
ReleaseGroupMbid *string
|
||||||
|
}
|
||||||
|
|
||||||
|
// #5241: heal the release-group id on an existing album row, from a rescan's
|
||||||
|
// tags or from the re-acquisition sweeper's MusicBrainz lookup. Only fills a
|
||||||
|
// NULL, so a tag never gets overwritten by a later guess or vice versa.
|
||||||
|
func (q *Queries) SetAlbumReleaseGroupMbidIfNull(ctx context.Context, arg SetAlbumReleaseGroupMbidIfNullParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, setAlbumReleaseGroupMbidIfNull, arg.ID, arg.ReleaseGroupMbid)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const upsertAlbum = `-- name: UpsertAlbum :one
|
const upsertAlbum = `-- name: UpsertAlbum :one
|
||||||
INSERT INTO albums (title, sort_title, artist_id, release_date, mbid, cover_art_path)
|
INSERT INTO albums (title, sort_title, artist_id, release_date, mbid, cover_art_path, release_group_mbid)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
ON CONFLICT (mbid) WHERE mbid IS NOT NULL
|
ON CONFLICT (mbid) WHERE mbid IS NOT NULL
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
title = EXCLUDED.title,
|
title = EXCLUDED.title,
|
||||||
@@ -828,8 +862,9 @@ DO UPDATE SET
|
|||||||
artist_id = EXCLUDED.artist_id,
|
artist_id = EXCLUDED.artist_id,
|
||||||
release_date = EXCLUDED.release_date,
|
release_date = EXCLUDED.release_date,
|
||||||
cover_art_path = EXCLUDED.cover_art_path,
|
cover_art_path = EXCLUDED.cover_art_path,
|
||||||
|
release_group_mbid = COALESCE(EXCLUDED.release_group_mbid, albums.release_group_mbid),
|
||||||
updated_at = now()
|
updated_at = now()
|
||||||
RETURNING id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version
|
RETURNING id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source, cover_art_sources_version, release_group_mbid
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpsertAlbumParams struct {
|
type UpsertAlbumParams struct {
|
||||||
@@ -839,6 +874,7 @@ type UpsertAlbumParams struct {
|
|||||||
ReleaseDate pgtype.Date
|
ReleaseDate pgtype.Date
|
||||||
Mbid *string
|
Mbid *string
|
||||||
CoverArtPath *string
|
CoverArtPath *string
|
||||||
|
ReleaseGroupMbid *string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) UpsertAlbum(ctx context.Context, arg UpsertAlbumParams) (Album, error) {
|
func (q *Queries) UpsertAlbum(ctx context.Context, arg UpsertAlbumParams) (Album, error) {
|
||||||
@@ -849,6 +885,7 @@ func (q *Queries) UpsertAlbum(ctx context.Context, arg UpsertAlbumParams) (Album
|
|||||||
arg.ReleaseDate,
|
arg.ReleaseDate,
|
||||||
arg.Mbid,
|
arg.Mbid,
|
||||||
arg.CoverArtPath,
|
arg.CoverArtPath,
|
||||||
|
arg.ReleaseGroupMbid,
|
||||||
)
|
)
|
||||||
var i Album
|
var i Album
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
@@ -863,6 +900,7 @@ func (q *Queries) UpsertAlbum(ctx context.Context, arg UpsertAlbumParams) (Album
|
|||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
&i.CoverArtSourcesVersion,
|
&i.CoverArtSourcesVersion,
|
||||||
|
&i.ReleaseGroupMbid,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ func (q *Queries) ListAlbumYearsWithCount(ctx context.Context) ([]ListAlbumYears
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsByGenreWithArtist = `-- name: ListAlbumsByGenreWithArtist :many
|
const listAlbumsByGenreWithArtist = `-- name: ListAlbumsByGenreWithArtist :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, artists.name AS artist_name
|
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.name AS artist_name
|
||||||
FROM albums
|
FROM albums
|
||||||
JOIN artists ON artists.id = albums.artist_id
|
JOIN artists ON artists.id = albums.artist_id
|
||||||
WHERE EXISTS (
|
WHERE EXISTS (
|
||||||
@@ -152,6 +152,7 @@ func (q *Queries) ListAlbumsByGenreWithArtist(ctx context.Context, arg ListAlbum
|
|||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
&i.Album.CoverArtSourcesVersion,
|
&i.Album.CoverArtSourcesVersion,
|
||||||
|
&i.Album.ReleaseGroupMbid,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -165,7 +166,7 @@ func (q *Queries) ListAlbumsByGenreWithArtist(ctx context.Context, arg ListAlbum
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listAlbumsByYearRangeWithArtist = `-- name: ListAlbumsByYearRangeWithArtist :many
|
const listAlbumsByYearRangeWithArtist = `-- name: ListAlbumsByYearRangeWithArtist :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, artists.name AS artist_name
|
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.name AS artist_name
|
||||||
FROM albums
|
FROM albums
|
||||||
JOIN artists ON artists.id = albums.artist_id
|
JOIN artists ON artists.id = albums.artist_id
|
||||||
WHERE albums.release_date IS NOT NULL
|
WHERE albums.release_date IS NOT NULL
|
||||||
@@ -219,6 +220,7 @@ func (q *Queries) ListAlbumsByYearRangeWithArtist(ctx context.Context, arg ListA
|
|||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
&i.Album.CoverArtSourcesVersion,
|
&i.Album.CoverArtSourcesVersion,
|
||||||
|
&i.Album.ReleaseGroupMbid,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ func (q *Queries) ListLikedAlbumIDs(ctx context.Context, userID pgtype.UUID) ([]
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listLikedAlbumRows = `-- name: ListLikedAlbumRows :many
|
const listLikedAlbumRows = `-- name: ListLikedAlbumRows :many
|
||||||
SELECT a.id, a.title, a.sort_title, a.artist_id, a.release_date, a.mbid, a.cover_art_path, a.created_at, a.updated_at, a.cover_art_source, a.cover_art_sources_version FROM albums a
|
SELECT a.id, a.title, a.sort_title, a.artist_id, a.release_date, a.mbid, a.cover_art_path, a.created_at, a.updated_at, a.cover_art_source, a.cover_art_sources_version, a.release_group_mbid FROM albums a
|
||||||
JOIN general_likes_albums l ON l.album_id = a.id
|
JOIN general_likes_albums l ON l.album_id = a.id
|
||||||
WHERE l.user_id = $1
|
WHERE l.user_id = $1
|
||||||
ORDER BY l.liked_at DESC
|
ORDER BY l.liked_at DESC
|
||||||
@@ -154,6 +154,7 @@ func (q *Queries) ListLikedAlbumRows(ctx context.Context, arg ListLikedAlbumRows
|
|||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.CoverArtSource,
|
&i.CoverArtSource,
|
||||||
&i.CoverArtSourcesVersion,
|
&i.CoverArtSourcesVersion,
|
||||||
|
&i.ReleaseGroupMbid,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,6 +207,7 @@ type Album struct {
|
|||||||
UpdatedAt pgtype.Timestamptz
|
UpdatedAt pgtype.Timestamptz
|
||||||
CoverArtSource *string
|
CoverArtSource *string
|
||||||
CoverArtSourcesVersion int32
|
CoverArtSourcesVersion int32
|
||||||
|
ReleaseGroupMbid *string
|
||||||
}
|
}
|
||||||
|
|
||||||
type AlbumLoudness struct {
|
type AlbumLoudness struct {
|
||||||
|
|||||||
@@ -370,7 +370,7 @@ func (q *Queries) ListMostPlayedTracksForUser(ctx context.Context, arg ListMostP
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listRediscoverAlbumsFallbackForUser = `-- name: ListRediscoverAlbumsFallbackForUser :many
|
const listRediscoverAlbumsFallbackForUser = `-- name: ListRediscoverAlbumsFallbackForUser :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, artists.name AS artist_name
|
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.name AS artist_name
|
||||||
FROM general_likes_albums gla
|
FROM general_likes_albums gla
|
||||||
JOIN albums ON albums.id = gla.album_id
|
JOIN albums ON albums.id = gla.album_id
|
||||||
JOIN artists ON artists.id = albums.artist_id
|
JOIN artists ON artists.id = albums.artist_id
|
||||||
@@ -418,6 +418,7 @@ func (q *Queries) ListRediscoverAlbumsFallbackForUser(ctx context.Context, arg L
|
|||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
&i.Album.CoverArtSourcesVersion,
|
&i.Album.CoverArtSourcesVersion,
|
||||||
|
&i.Album.ReleaseGroupMbid,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -459,7 +460,7 @@ eligible AS (
|
|||||||
HAVING COALESCE(max(pe.started_at), '1970-01-01'::timestamptz)
|
HAVING COALESCE(max(pe.started_at), '1970-01-01'::timestamptz)
|
||||||
< now() - interval '14 days'
|
< now() - interval '14 days'
|
||||||
)
|
)
|
||||||
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, artists.name AS artist_name
|
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.name AS artist_name
|
||||||
FROM eligible e
|
FROM eligible e
|
||||||
JOIN albums ON albums.id = e.album_id
|
JOIN albums ON albums.id = e.album_id
|
||||||
JOIN artists ON artists.id = albums.artist_id
|
JOIN artists ON artists.id = albums.artist_id
|
||||||
@@ -510,6 +511,7 @@ func (q *Queries) ListRediscoverAlbumsForUser(ctx context.Context, arg ListRedis
|
|||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
&i.Album.CoverArtSourcesVersion,
|
&i.Album.CoverArtSourcesVersion,
|
||||||
|
&i.Album.ReleaseGroupMbid,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ WITH liked_album_ids AS (
|
|||||||
JOIN tracks trk ON trk.id = likt.track_id
|
JOIN tracks trk ON trk.id = likt.track_id
|
||||||
WHERE likt.user_id = $1
|
WHERE likt.user_id = $1
|
||||||
)
|
)
|
||||||
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, artists.name AS artist_name
|
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.name AS artist_name
|
||||||
FROM liked_album_ids lai
|
FROM liked_album_ids lai
|
||||||
JOIN albums ON albums.id = lai.album_id
|
JOIN albums ON albums.id = lai.album_id
|
||||||
JOIN artists ON artists.id = albums.artist_id
|
JOIN artists ON artists.id = albums.artist_id
|
||||||
@@ -143,6 +143,7 @@ func (q *Queries) ListYouMightLikeAlbumFallbackForUser(ctx context.Context, arg
|
|||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
&i.Album.CoverArtSourcesVersion,
|
&i.Album.CoverArtSourcesVersion,
|
||||||
|
&i.Album.ReleaseGroupMbid,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -156,7 +157,7 @@ func (q *Queries) ListYouMightLikeAlbumFallbackForUser(ctx context.Context, arg
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listYouMightLikeAlbumsForUser = `-- name: ListYouMightLikeAlbumsForUser :many
|
const listYouMightLikeAlbumsForUser = `-- name: ListYouMightLikeAlbumsForUser :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, artists.name AS artist_name
|
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.name AS artist_name
|
||||||
FROM you_might_like_albums yml
|
FROM you_might_like_albums yml
|
||||||
JOIN albums ON albums.id = yml.album_id
|
JOIN albums ON albums.id = yml.album_id
|
||||||
JOIN artists ON artists.id = albums.artist_id
|
JOIN artists ON artists.id = albums.artist_id
|
||||||
@@ -201,6 +202,7 @@ func (q *Queries) ListYouMightLikeAlbumsForUser(ctx context.Context, arg ListYou
|
|||||||
&i.Album.UpdatedAt,
|
&i.Album.UpdatedAt,
|
||||||
&i.Album.CoverArtSource,
|
&i.Album.CoverArtSource,
|
||||||
&i.Album.CoverArtSourcesVersion,
|
&i.Album.CoverArtSourcesVersion,
|
||||||
|
&i.Album.ReleaseGroupMbid,
|
||||||
&i.ArtistName,
|
&i.ArtistName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
DROP INDEX IF EXISTS albums_release_group_mbid_idx;
|
||||||
|
ALTER TABLE albums DROP COLUMN IF EXISTS release_group_mbid;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
-- MusicBrainz release-group id for an album (#5241). albums.mbid holds the
|
||||||
|
-- *release* id (Picard's musicbrainz_albumid: one edition), but Lidarr names
|
||||||
|
-- albums by release group, so re-acquisition and request completion need
|
||||||
|
-- this one. Not unique: several releases of one group can each be an album
|
||||||
|
-- row. Filled by the scanner from musicbrainz_releasegroupid, or by the
|
||||||
|
-- re-acquisition sweeper resolving the release through MusicBrainz.
|
||||||
|
ALTER TABLE albums ADD COLUMN release_group_mbid text;
|
||||||
|
|
||||||
|
CREATE INDEX albums_release_group_mbid_idx ON albums (release_group_mbid)
|
||||||
|
WHERE release_group_mbid IS NOT NULL;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
-- name: UpsertAlbum :one
|
-- name: UpsertAlbum :one
|
||||||
INSERT INTO albums (title, sort_title, artist_id, release_date, mbid, cover_art_path)
|
INSERT INTO albums (title, sort_title, artist_id, release_date, mbid, cover_art_path, release_group_mbid)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
ON CONFLICT (mbid) WHERE mbid IS NOT NULL
|
ON CONFLICT (mbid) WHERE mbid IS NOT NULL
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
title = EXCLUDED.title,
|
title = EXCLUDED.title,
|
||||||
@@ -8,6 +8,7 @@ DO UPDATE SET
|
|||||||
artist_id = EXCLUDED.artist_id,
|
artist_id = EXCLUDED.artist_id,
|
||||||
release_date = EXCLUDED.release_date,
|
release_date = EXCLUDED.release_date,
|
||||||
cover_art_path = EXCLUDED.cover_art_path,
|
cover_art_path = EXCLUDED.cover_art_path,
|
||||||
|
release_group_mbid = COALESCE(EXCLUDED.release_group_mbid, albums.release_group_mbid),
|
||||||
updated_at = now()
|
updated_at = now()
|
||||||
RETURNING *;
|
RETURNING *;
|
||||||
|
|
||||||
@@ -142,6 +143,15 @@ UPDATE albums
|
|||||||
updated_at = now()
|
updated_at = now()
|
||||||
WHERE id = $1 AND mbid IS NULL;
|
WHERE id = $1 AND mbid IS NULL;
|
||||||
|
|
||||||
|
-- name: SetAlbumReleaseGroupMbidIfNull :exec
|
||||||
|
-- #5241: heal the release-group id on an existing album row, from a rescan's
|
||||||
|
-- tags or from the re-acquisition sweeper's MusicBrainz lookup. Only fills a
|
||||||
|
-- NULL, so a tag never gets overwritten by a later guess or vice versa.
|
||||||
|
UPDATE albums
|
||||||
|
SET release_group_mbid = $2,
|
||||||
|
updated_at = now()
|
||||||
|
WHERE id = $1 AND release_group_mbid IS NULL;
|
||||||
|
|
||||||
-- name: ListAlbumsMissingMbidWithTrack :many
|
-- name: ListAlbumsMissingMbidWithTrack :many
|
||||||
-- One-shot MBID backfill: returns each album where mbid IS NULL alongside
|
-- 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
|
-- one of its tracks' file_path so the worker can re-read tags. LIMIT
|
||||||
|
|||||||
@@ -41,6 +41,16 @@ func extractRecordingMBID(m tag.Metadata) string {
|
|||||||
return cleanMBID(mbz.Extract(m).Get(mbz.Recording))
|
return cleanMBID(mbz.Extract(m).Get(mbz.Recording))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractReleaseGroupMBID reads the MusicBrainz *release-group* ID —
|
||||||
|
// Picard's musicbrainz_releasegroupid, surfaced by dhowden/tag as
|
||||||
|
// mbz.ReleaseGroup. mbz.Album (what albums.mbid stores) is the release: one
|
||||||
|
// edition. Lidarr names albums by release group, so re-acquisition and
|
||||||
|
// request completion key on this one (#5241). Separate from extractMBIDs for
|
||||||
|
// the same reason extractRecordingMBID is.
|
||||||
|
func extractReleaseGroupMBID(m tag.Metadata) string {
|
||||||
|
return cleanMBID(mbz.Extract(m).Get(mbz.ReleaseGroup))
|
||||||
|
}
|
||||||
|
|
||||||
func cleanMBID(s string) string {
|
func cleanMBID(s string) string {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -163,3 +163,40 @@ func TestCleanMBID_OnlyNULReturnsEmpty(t *testing.T) {
|
|||||||
t.Errorf("got %q, want empty", got)
|
t.Errorf("got %q, want empty", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #5241: the release-group id is read beside the release id, from each
|
||||||
|
// format's own key, and the two are never confused.
|
||||||
|
func TestExtractReleaseGroupMBID(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
meta stubMeta
|
||||||
|
}{
|
||||||
|
{"vorbis", stubMeta{format: tag.VORBIS, raw: map[string]interface{}{
|
||||||
|
"musicbrainz_albumid": "release-1",
|
||||||
|
"musicbrainz_releasegroupid": "group-1",
|
||||||
|
}}},
|
||||||
|
{"id3v2", stubMeta{format: tag.ID3v2_4, raw: map[string]interface{}{
|
||||||
|
"TXXX": &tag.Comm{Description: "MusicBrainz Album Id", Text: "release-1"},
|
||||||
|
"TXXX_0": &tag.Comm{Description: "MusicBrainz Release Group Id", Text: "group-1\x00"},
|
||||||
|
}}},
|
||||||
|
{"mp4", stubMeta{format: tag.MP4, raw: map[string]interface{}{
|
||||||
|
"MusicBrainz Album Id": "release-1",
|
||||||
|
"MusicBrainz Release Group Id": "group-1",
|
||||||
|
}}},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
if got := extractReleaseGroupMBID(c.meta); got != "group-1" {
|
||||||
|
t.Errorf("release group = %q, want group-1", got)
|
||||||
|
}
|
||||||
|
if album, _ := extractMBIDs(c.meta); album != "release-1" {
|
||||||
|
t.Errorf("album (release) = %q, want release-1", album)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
none := stubMeta{format: tag.VORBIS, raw: map[string]interface{}{"musicbrainz_albumid": "release-1"}}
|
||||||
|
if got := extractReleaseGroupMBID(none); got != "" {
|
||||||
|
t.Errorf("untagged release group = %q, want empty", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -56,7 +56,11 @@ var audioExtensions = map[string]bool{
|
|||||||
// "EDM", "Children'S Music" -> "Children's Music". Bumped rather than left
|
// "EDM", "Children'S Music" -> "Children's Music". Bumped rather than left
|
||||||
// to new files only because taste_profile.sql reads tracks.genre directly,
|
// to new files only because taste_profile.sql reads tracks.genre directly,
|
||||||
// so a half-repaired library would carry both spellings as separate tags.
|
// 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 {
|
type Stats struct {
|
||||||
Scanned int `json:"scanned"`
|
Scanned int `json:"scanned"`
|
||||||
@@ -258,6 +262,7 @@ func (s *Scanner) scanFile(
|
|||||||
}
|
}
|
||||||
albumMBID, artistMBID := extractMBIDs(meta)
|
albumMBID, artistMBID := extractMBIDs(meta)
|
||||||
recordingMBID := extractRecordingMBID(meta)
|
recordingMBID := extractRecordingMBID(meta)
|
||||||
|
releaseGroupMBID := extractReleaseGroupMBID(meta)
|
||||||
|
|
||||||
artistName := meta.Artist()
|
artistName := meta.Artist()
|
||||||
if artistName == "" {
|
if artistName == "" {
|
||||||
@@ -276,7 +281,7 @@ func (s *Scanner) scanFile(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return pgtype.UUID{}, false, fmt.Errorf("artist: %w", err)
|
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 {
|
if err != nil {
|
||||||
return pgtype.UUID{}, false, fmt.Errorf("album: %w", err)
|
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
|
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})
|
existing, err := q.GetAlbumByArtistAndTitle(ctx, dbq.GetAlbumByArtistAndTitleParams{ArtistID: artistID, Title: title})
|
||||||
if err == nil {
|
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.
|
// Heal: backfill mbid on a previously-imported row if we have one now.
|
||||||
if mbid != "" && (existing.Mbid == nil || *existing.Mbid == "") {
|
if mbid != "" && (existing.Mbid == nil || *existing.Mbid == "") {
|
||||||
m := mbid
|
m := mbid
|
||||||
@@ -564,6 +583,10 @@ func (s *Scanner) resolveAlbum(ctx context.Context, q *dbq.Queries, artistID pgt
|
|||||||
m := mbid
|
m := mbid
|
||||||
params.Mbid = &m
|
params.Mbid = &m
|
||||||
}
|
}
|
||||||
|
if releaseGroupMBID != "" {
|
||||||
|
rg := releaseGroupMBID
|
||||||
|
params.ReleaseGroupMbid = &rg
|
||||||
|
}
|
||||||
album, err := q.UpsertAlbum(ctx, params)
|
album, err := q.UpsertAlbum(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dbq.Album{}, err
|
return dbq.Album{}, err
|
||||||
|
|||||||
@@ -308,3 +308,80 @@ func TestScanner_AdoptsMovedFile_Integration(t *testing.T) {
|
|||||||
t.Errorf("tracks = %d, want 8 — a rename must not add a row", total)
|
t.Errorf("tracks = %d, want 8 — a rename must not add a row", total)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestScanner_ReleaseGroupMBID_Integration is #5241's scan half: a new album
|
||||||
|
// takes its release-group id from the tag, and an album indexed before this
|
||||||
|
// read existed gets it on the next scan, through the tagReadVersion bump,
|
||||||
|
// without its file changing.
|
||||||
|
func TestScanner_ReleaseGroupMBID_Integration(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping scanner integration in -short mode")
|
||||||
|
}
|
||||||
|
dsn := os.Getenv("MINSTREL_TEST_DATABASE_URL")
|
||||||
|
if dsn == "" {
|
||||||
|
t.Skip("MINSTREL_TEST_DATABASE_URL not set")
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||||
|
|
||||||
|
if err := db.Migrate(dsn, logger); err != nil {
|
||||||
|
t.Fatalf("migrate: %v", err)
|
||||||
|
}
|
||||||
|
pool, err := pgxpool.New(ctx, dsn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("pool: %v", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(pool.Close)
|
||||||
|
if _, err := pool.Exec(ctx, "TRUNCATE tracks, albums, artists RESTART IDENTITY CASCADE"); err != nil {
|
||||||
|
t.Fatalf("truncate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
groupA = "aaaaaaaa-1111-2222-3333-444444444444"
|
||||||
|
groupB = "bbbbbbbb-1111-2222-3333-444444444444"
|
||||||
|
)
|
||||||
|
root := t.TempDir()
|
||||||
|
writeTestMP3(t, filepath.Join(root, "rg/A/01.mp3"), map[string]string{
|
||||||
|
"TIT2": "One", "TPE1": "Artist RG", "TALB": "Album A",
|
||||||
|
"TXXX": "MusicBrainz Release Group Id\x00" + groupA,
|
||||||
|
})
|
||||||
|
pathB := filepath.Join(root, "rg/B/01.mp3")
|
||||||
|
writeTestMP3(t, pathB, map[string]string{
|
||||||
|
"TIT2": "Two", "TPE1": "Artist RG", "TALB": "Album B",
|
||||||
|
"TXXX": "MusicBrainz Release Group Id\x00" + groupB,
|
||||||
|
})
|
||||||
|
|
||||||
|
scanner := New(pool, logger, []string{root}, nil)
|
||||||
|
if _, err := scanner.Scan(ctx, nil); err != nil {
|
||||||
|
t.Fatalf("first scan: %v", err)
|
||||||
|
}
|
||||||
|
groupOf := func(title string) *string {
|
||||||
|
t.Helper()
|
||||||
|
var rg *string
|
||||||
|
if err := pool.QueryRow(ctx, "SELECT release_group_mbid FROM albums WHERE title = $1", title).Scan(&rg); err != nil {
|
||||||
|
t.Fatalf("album %q: %v", title, err)
|
||||||
|
}
|
||||||
|
return rg
|
||||||
|
}
|
||||||
|
if rg := groupOf("Album A"); rg == nil || *rg != groupA {
|
||||||
|
t.Fatalf("new album release group = %v, want %s", rg, groupA)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Album B as a library indexed under tag-read version 2 left it: no
|
||||||
|
// release group, file untouched since.
|
||||||
|
if _, err := pool.Exec(ctx, "UPDATE albums SET release_group_mbid = NULL WHERE title = 'Album B'"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := pool.Exec(ctx, "UPDATE tracks SET tag_read_version = 2 WHERE file_path = $1", pathB); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := scanner.Scan(ctx, nil); err != nil {
|
||||||
|
t.Fatalf("second scan: %v", err)
|
||||||
|
}
|
||||||
|
if rg := groupOf("Album B"); rg == nil || *rg != groupB {
|
||||||
|
t.Errorf("existing album release group after rescan = %v, want %s", rg, groupB)
|
||||||
|
}
|
||||||
|
if rg := groupOf("Album A"); rg == nil || *rg != groupA {
|
||||||
|
t.Errorf("album A release group changed to %v", rg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user