feat: the library's albums go on the wanted list as held, once; after that the list is the truth (M502 #5567)
release / govulncheck (push) Successful in 16s
release / web (push) Successful in 1m24s
release / go (push) Successful in 1m44s
release / integration (push) Successful in 5m19s
release / android (push) Successful in 5m31s
release / Build signed APK (releases and dev) (push) Successful in 5m35s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 25s
release / Verify release artifacts (tag releases only) (push) Skipped
release / govulncheck (push) Successful in 16s
release / web (push) Successful in 1m24s
release / go (push) Successful in 1m44s
release / integration (push) Successful in 5m19s
release / android (push) Successful in 5m31s
release / Build signed APK (releases and dev) (push) Successful in 5m35s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 25s
release / Verify release artifacts (tag releases only) (push) Skipped
A one-time job in the wanted worker lists every album in the library as a held want, one per release group, with a record that it happened (acquisition_settings.library_listed_at). Albums whose tags name only a release are named on MusicBrainz first, 120 a pass; what nothing names is counted and shown on the Held tab. Re-acquisition now wants back only albums on the list. Taking a held album off the list leaves its files, and it is never fetched back; albums put in the library folder by hand afterwards are not added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -124,6 +124,33 @@ func (q *Queries) DeleteWant(ctx context.Context, id pgtype.UUID) (int64, error)
|
||||
return result.RowsAffected(), nil
|
||||
}
|
||||
|
||||
const finishLibraryListing = `-- name: FinishLibraryListing :one
|
||||
UPDATE acquisition_settings s
|
||||
SET library_listed_at = now(),
|
||||
library_unlisted = (
|
||||
SELECT COUNT(*) FROM albums a
|
||||
WHERE a.created_at <= s.library_listing_started_at
|
||||
AND NOT EXISTS (SELECT 1 FROM wants w
|
||||
WHERE w.album_id = a.id
|
||||
OR (a.release_group_mbid <> '' AND w.release_group_mbid = a.release_group_mbid)))
|
||||
WHERE s.id = true AND s.library_listed_at IS NULL
|
||||
RETURNING library_listed_at, library_unlisted
|
||||
`
|
||||
|
||||
type FinishLibraryListingRow struct {
|
||||
LibraryListedAt pgtype.Timestamptz
|
||||
LibraryUnlisted int32
|
||||
}
|
||||
|
||||
// The listing is done: count the albums from before the cutoff it could not
|
||||
// list, because nothing names them to MusicBrainz.
|
||||
func (q *Queries) FinishLibraryListing(ctx context.Context) (FinishLibraryListingRow, error) {
|
||||
row := q.db.QueryRow(ctx, finishLibraryListing)
|
||||
var i FinishLibraryListingRow
|
||||
err := row.Scan(&i.LibraryListedAt, &i.LibraryUnlisted)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const fulfillWant = `-- name: FulfillWant :exec
|
||||
UPDATE wants
|
||||
SET fulfilled_at = now(),
|
||||
@@ -199,6 +226,26 @@ func (q *Queries) GetFollow(ctx context.Context, artistMbid string) (GetFollowRo
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getLibraryListing = `-- name: GetLibraryListing :one
|
||||
|
||||
SELECT library_listing_started_at, library_listed_at, library_unlisted
|
||||
FROM acquisition_settings WHERE id = true
|
||||
`
|
||||
|
||||
type GetLibraryListingRow struct {
|
||||
LibraryListingStartedAt pgtype.Timestamptz
|
||||
LibraryListedAt pgtype.Timestamptz
|
||||
LibraryUnlisted int32
|
||||
}
|
||||
|
||||
// The one-time listing of the library (#5567). ---------------------------
|
||||
func (q *Queries) GetLibraryListing(ctx context.Context) (GetLibraryListingRow, error) {
|
||||
row := q.db.QueryRow(ctx, getLibraryListing)
|
||||
var i GetLibraryListingRow
|
||||
err := row.Scan(&i.LibraryListingStartedAt, &i.LibraryListedAt, &i.LibraryUnlisted)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getReleaseTypeProfile = `-- name: GetReleaseTypeProfile :one
|
||||
SELECT id, name, primary_types, secondary_types, is_default, created_at, updated_at FROM release_type_profiles WHERE id = $1
|
||||
`
|
||||
@@ -424,6 +471,83 @@ func (q *Queries) ListFollowsDue(ctx context.Context, arg ListFollowsDueParams)
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listLibraryAlbums = `-- name: ListLibraryAlbums :execrows
|
||||
INSERT INTO wants (release_group_mbid, artist_mbid, artist_name, title, reason, album_id, wanted_at, fulfilled_at)
|
||||
SELECT DISTINCT ON (a.release_group_mbid)
|
||||
a.release_group_mbid, ar.mbid, ar.name, a.title, 'library', a.id, now(),
|
||||
COALESCE((SELECT MIN(t.missing_since) FROM tracks t WHERE t.album_id = a.id) - interval '1 second', now())
|
||||
FROM albums a
|
||||
JOIN artists ar ON ar.id = a.artist_id
|
||||
WHERE a.release_group_mbid IS NOT NULL AND a.release_group_mbid <> ''
|
||||
AND ar.mbid IS NOT NULL AND ar.mbid <> ''
|
||||
AND a.created_at <= $1::timestamptz
|
||||
AND ($2::uuid IS NULL OR a.id = $2::uuid)
|
||||
AND EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = a.id)
|
||||
ORDER BY a.release_group_mbid,
|
||||
(SELECT COUNT(*) FROM tracks t WHERE t.album_id = a.id AND t.missing_since IS NULL) DESC, a.id
|
||||
ON CONFLICT (release_group_mbid) DO NOTHING
|
||||
`
|
||||
|
||||
type ListLibraryAlbumsParams struct {
|
||||
Cutoff pgtype.Timestamptz
|
||||
AlbumID pgtype.UUID
|
||||
}
|
||||
|
||||
// Puts library albums on the list as held: each release group once, by the
|
||||
// album holding the most tracks. Only albums the library had at the cutoff,
|
||||
// and with album_id given only that album. A group already on the list is
|
||||
// left as it is. An album with tracks already missing is held from just
|
||||
// before the first loss, so re-acquisition still fetches it back.
|
||||
func (q *Queries) ListLibraryAlbums(ctx context.Context, arg ListLibraryAlbumsParams) (int64, error) {
|
||||
result, err := q.db.Exec(ctx, listLibraryAlbums, arg.Cutoff, arg.AlbumID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.RowsAffected(), nil
|
||||
}
|
||||
|
||||
const listLibraryAlbumsToName = `-- name: ListLibraryAlbumsToName :many
|
||||
SELECT a.id, a.title, a.mbid FROM albums a
|
||||
WHERE a.release_group_mbid IS NULL AND a.mbid IS NOT NULL AND a.mbid <> ''
|
||||
AND a.created_at <= $1::timestamptz
|
||||
ORDER BY a.created_at, a.id
|
||||
LIMIT $2
|
||||
`
|
||||
|
||||
type ListLibraryAlbumsToNameParams struct {
|
||||
Cutoff pgtype.Timestamptz
|
||||
MaxRows int32
|
||||
}
|
||||
|
||||
type ListLibraryAlbumsToNameRow struct {
|
||||
ID pgtype.UUID
|
||||
Title string
|
||||
Mbid *string
|
||||
}
|
||||
|
||||
// Library albums from before the cutoff that carry a release id but no
|
||||
// release group yet: the listing asks MusicBrainz for each. ” marks one
|
||||
// MusicBrainz could not name, so it is asked once.
|
||||
func (q *Queries) ListLibraryAlbumsToName(ctx context.Context, arg ListLibraryAlbumsToNameParams) ([]ListLibraryAlbumsToNameRow, error) {
|
||||
rows, err := q.db.Query(ctx, listLibraryAlbumsToName, arg.Cutoff, arg.MaxRows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []ListLibraryAlbumsToNameRow
|
||||
for rows.Next() {
|
||||
var i ListLibraryAlbumsToNameRow
|
||||
if err := rows.Scan(&i.ID, &i.Title, &i.Mbid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listOpenWants = `-- name: ListOpenWants :many
|
||||
SELECT id, release_group_mbid, artist_mbid, artist_name, title, primary_type, first_release_date, reason, album_id, created_by, wanted_at, fulfilled_at, created_at, updated_at, searched_at, search_outcome, search_considered, best_release, best_verdict, search_error FROM wants
|
||||
WHERE fulfilled_at IS NULL
|
||||
@@ -697,6 +821,21 @@ func (q *Queries) RepointWant(ctx context.Context, arg RepointWantParams) error
|
||||
return err
|
||||
}
|
||||
|
||||
const setAlbumReleaseGroup = `-- name: SetAlbumReleaseGroup :exec
|
||||
UPDATE albums SET release_group_mbid = $1
|
||||
WHERE id = $2 AND release_group_mbid IS NULL
|
||||
`
|
||||
|
||||
type SetAlbumReleaseGroupParams struct {
|
||||
ReleaseGroupMbid *string
|
||||
ID pgtype.UUID
|
||||
}
|
||||
|
||||
func (q *Queries) SetAlbumReleaseGroup(ctx context.Context, arg SetAlbumReleaseGroupParams) error {
|
||||
_, err := q.db.Exec(ctx, setAlbumReleaseGroup, arg.ReleaseGroupMbid, arg.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
const setDefaultReleaseTypeProfile = `-- name: SetDefaultReleaseTypeProfile :execrows
|
||||
UPDATE release_type_profiles SET is_default = true, updated_at = now() WHERE id = $1
|
||||
`
|
||||
@@ -709,6 +848,22 @@ func (q *Queries) SetDefaultReleaseTypeProfile(ctx context.Context, id pgtype.UU
|
||||
return result.RowsAffected(), nil
|
||||
}
|
||||
|
||||
const startLibraryListing = `-- name: StartLibraryListing :one
|
||||
UPDATE acquisition_settings
|
||||
SET library_listing_started_at = COALESCE(library_listing_started_at, now())
|
||||
WHERE id = true
|
||||
RETURNING library_listing_started_at
|
||||
`
|
||||
|
||||
// Marks the listing begun, once; its time is the cutoff for which albums
|
||||
// count as the library's.
|
||||
func (q *Queries) StartLibraryListing(ctx context.Context) (pgtype.Timestamptz, error) {
|
||||
row := q.db.QueryRow(ctx, startLibraryListing)
|
||||
var library_listing_started_at pgtype.Timestamptz
|
||||
err := row.Scan(&library_listing_started_at)
|
||||
return library_listing_started_at, err
|
||||
}
|
||||
|
||||
const updateReleaseTypeProfile = `-- name: UpdateReleaseTypeProfile :one
|
||||
UPDATE release_type_profiles
|
||||
SET name = $2,
|
||||
|
||||
Reference in New Issue
Block a user