feat: one Missing files page under Collection: folders gone, each with its album's want, last search or download, and Search, Releases and Remove; an album held before its files went is no longer shown as held again (M504 #5626)
release / govulncheck (push) Successful in 18s
release / web (push) Successful in 1m30s
release / go (push) Successful in 1m46s
release / integration (push) Successful in 5m23s
release / android (push) Successful in 5m43s
release / Build signed APK (releases and dev) (push) Successful in 6m2s
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 18s
release / web (push) Successful in 1m30s
release / go (push) Successful in 1m46s
release / integration (push) Successful in 5m23s
release / android (push) Successful in 5m43s
release / Build signed APK (releases and dev) (push) Successful in 6m2s
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
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
|
||||
@@ -31,10 +32,13 @@ type missingTrackView struct {
|
||||
// reacquisitionStateView is what the wanted list says about one album
|
||||
// (milestone #290, M502 #5520): when it was wanted back, and when the library
|
||||
// held it again. A missing album with no want yet is still inside the grace
|
||||
// window, or MusicBrainz cannot name it.
|
||||
// window, or MusicBrainz cannot name it. Want is that want as the wanted list
|
||||
// sends it, so the Missing page can show its search and download and act on
|
||||
// it (M504).
|
||||
type reacquisitionStateView struct {
|
||||
WantedAt *string `json:"wanted_at"`
|
||||
HeldAt *string `json:"held_at"`
|
||||
WantedAt *string `json:"wanted_at"`
|
||||
HeldAt *string `json:"held_at"`
|
||||
Want *wantView `json:"want"`
|
||||
}
|
||||
|
||||
// missingGroupView is a directory's worth of missing tracks.
|
||||
@@ -174,7 +178,13 @@ func (h *handlers) attachReacquisitionState(
|
||||
// groupMissingByDirectory folded them in.
|
||||
dirAlbum := make(map[string]pgtype.UUID, len(groups))
|
||||
ids := make([]pgtype.UUID, 0, len(groups))
|
||||
// The latest loss in each folder: a want held before it is the album
|
||||
// as it was before its files went, not the library holding it again.
|
||||
dirLost := make(map[string]time.Time, len(groups))
|
||||
for _, row := range rows {
|
||||
if row.MissingSince.Valid && row.MissingSince.Time.After(dirLost[row.Directory]) {
|
||||
dirLost[row.Directory] = row.MissingSince.Time
|
||||
}
|
||||
if _, seen := dirAlbum[row.Directory]; seen {
|
||||
continue
|
||||
}
|
||||
@@ -194,6 +204,7 @@ func (h *handlers) attachReacquisitionState(
|
||||
byAlbum[uuidToString(w.AlbumID)] = w
|
||||
}
|
||||
}
|
||||
full := h.wantViewsByID(r, q, byAlbum)
|
||||
for i := range groups {
|
||||
albumID, ok := dirAlbum[groups[i].Directory]
|
||||
if !ok {
|
||||
@@ -203,6 +214,12 @@ func (h *handlers) attachReacquisitionState(
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
// Held before its files went (every library album is on the list,
|
||||
// held, since #5567): not wanted back yet, which the sweep does once
|
||||
// the grace window has passed.
|
||||
if w.FulfilledAt.Valid && !w.FulfilledAt.Time.After(dirLost[groups[i].Directory]) {
|
||||
continue
|
||||
}
|
||||
view := &reacquisitionStateView{}
|
||||
wantedAt := formatTimestamp(w.WantedAt)
|
||||
view.WantedAt = &wantedAt
|
||||
@@ -210,6 +227,36 @@ func (h *handlers) attachReacquisitionState(
|
||||
heldAt := formatTimestamp(w.FulfilledAt)
|
||||
view.HeldAt = &heldAt
|
||||
}
|
||||
if v, ok := full[uuidToString(w.WantID)]; ok {
|
||||
view.Want = &v
|
||||
}
|
||||
groups[i].Reacquisition = view
|
||||
}
|
||||
}
|
||||
|
||||
// wantViewsByID reads each album's current want in full, keyed by want id.
|
||||
// Best-effort like the state it decorates: a failure leaves the groups with
|
||||
// their dates and no want.
|
||||
func (h *handlers) wantViewsByID(
|
||||
r *http.Request,
|
||||
q *dbq.Queries,
|
||||
byAlbum map[string]dbq.GetWantsForAlbumsRow,
|
||||
) map[string]wantView {
|
||||
ids := make([]pgtype.UUID, 0, len(byAlbum))
|
||||
for _, w := range byAlbum {
|
||||
ids = append(ids, w.WantID)
|
||||
}
|
||||
out := make(map[string]wantView, len(ids))
|
||||
if len(ids) == 0 {
|
||||
return out
|
||||
}
|
||||
rows, err := q.ListWants(r.Context(), dbq.ListWantsParams{State: "all", WantIds: ids, MaxRows: int32(len(ids))})
|
||||
if err != nil {
|
||||
h.logger.Warn("admin missing: want detail lookup failed", "err", err)
|
||||
return out
|
||||
}
|
||||
for _, row := range rows {
|
||||
out[uuidToString(row.ID)] = wantViewOf(row)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user