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

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:
2026-10-10 16:13:46 -04:00
co-authored by Claude Opus 5.5
parent bc198a9389
commit 2c677b3184
19 changed files with 965 additions and 172 deletions
+21 -1
View File
@@ -86,6 +86,16 @@ type wantsPage struct {
Limit int `json:"limit"`
Offset int `json:"offset"`
Counts wantCounts `json:"counts"`
// Library is the one-time listing of the library's albums (#5567).
Library libraryListingView `json:"library"`
}
// libraryListingView: whether the library's albums are all on the list
// yet, and how many could not be listed (nothing names them to
// MusicBrainz).
type libraryListingView struct {
ListedAt *string `json:"listed_at"`
Unlisted int32 `json:"unlisted"`
}
const wantsPageSize = 50
@@ -116,11 +126,21 @@ func (h *handlers) handleListWants(w http.ResponseWriter, r *http.Request) {
writeErrWithLog(w, h.logger, "admin wanted: count", apierror.Internal(err))
return
}
listing, err := q.GetLibraryListing(r.Context())
if err != nil {
writeErrWithLog(w, h.logger, "admin wanted: library listing", apierror.Internal(err))
return
}
out := wantsPage{
Items: make([]wantView, 0, len(rows)), Limit: wantsPageSize, Offset: offset,
Library: libraryListingView{Unlisted: listing.LibraryUnlisted},
Items: make([]wantView, 0, len(rows)), Limit: wantsPageSize, Offset: offset,
Counts: wantCounts{Wanted: counts.Wanted, Missing: counts.Missing, Held: counts.Held, Following: counts.Following,
Activity: counts.Activity, Attention: counts.Attention, Blocked: counts.Blocked},
}
if listing.LibraryListedAt.Valid {
at := formatTimestamp(listing.LibraryListedAt)
out.Library.ListedAt = &at
}
switch state {
case "missing":
out.Total = counts.Missing