Files
bvandeusenandClaude Opus 5.5 0f58ebcd6c
release / govulncheck (push) Successful in 14s
release / web (push) Failing after 1m26s
release / go (push) Successful in 1m35s
release / integration (push) Successful in 5m8s
release / android (push) Successful in 5m12s
release / Build signed APK (releases and dev) (push) Successful in 5m26s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped
feat: duplicates settle themselves: retitled copies across releases link, alike versions on one album are dismissed, and a file in another song's slot is removed so the album is fetched again (M498 #6159-#6162)
The operator's library had 536 groups waiting for review, 2026-10-19. A sample
showed most were one recording under two titles on two releases.

- The headline and the Duplicates count leave out cross-release groups, which
  the resolver keeps and links: the headline read 3,479 when 536 needed review.
- Any group spanning albums is cross_release and is linked as one song,
  whatever its titles say. Nothing is deleted.
- One album, two versions named as such ("Lovesick (Instrumental)") and not
  byte-identical: a new class, distinct, dismissed by the resolver.
- One album, one recording under different song titles (mismatch): the
  album's cached MusicBrainz track list decides. When exactly one slot's song
  is as long as the file and every other slot's song is clearly not, the other
  copies are removed and their tracks marked missing, so the reacquisition
  sweeper wants the album back. Otherwise the group stays for review.
- The duplicates_found notice moves from the sweep to the resolver: it names
  only groups a pass first left for review.
- Resolved automatically lists dismissals and wrong-slot removals beside
  merges.

Migration 0094 adds 'distinct' to the class CHECK (rule 36).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 23:22:38 -04:00

33 lines
2.2 KiB
SQL

-- name: AdminSummary :one
-- What the admin rail and the Overview's verdict read (#5605): each queue
-- that waits on a person, with how many and since when. One round trip, so
-- the rail costs one cheap read on every admin page. The "since" is the
-- oldest item still waiting: how long the queue has been left, which is what
-- tells the operator a count of 2 is fine today and wrong after a month.
SELECT
(SELECT count(*) FROM requests WHERE status = 'pending')::bigint AS requests,
(SELECT min(requested_at) FROM requests WHERE status = 'pending')::timestamptz AS requests_since,
-- A broken track is one report however it was found: a listener flagged
-- it, or it failed to play (#5606). Counted by track, as the Reports
-- page lists them.
(SELECT count(*) FROM (
SELECT track_id FROM quarantine
UNION
SELECT track_id FROM playback_errors WHERE resolved_at IS NULL) r)::bigint AS reports,
(SELECT min(at) FROM (
SELECT created_at AS at FROM quarantine
UNION ALL
SELECT occurred_at FROM playback_errors WHERE resolved_at IS NULL) r)::timestamptz AS reports_since,
-- Duplicates needing review: a cross-release group is kept and linked by
-- the resolver and waits on nobody (the report's review view).
(SELECT count(*) FROM duplicate_groups g
WHERE g.status = 'pending' AND g.class IS DISTINCT FROM 'cross_release'
AND (SELECT count(*) FROM duplicate_group_members m WHERE m.group_id = g.id) >= 2)::bigint AS duplicates,
(SELECT min(g.detected_at) FROM duplicate_groups g
WHERE g.status = 'pending' AND g.class IS DISTINCT FROM 'cross_release'
AND (SELECT count(*) FROM duplicate_group_members m WHERE m.group_id = g.id) >= 2)::timestamptz AS duplicates_since,
(SELECT count(*) FROM tracks WHERE source_verdict = 'suspect' AND missing_since IS NULL)::bigint AS suspect,
(SELECT min(source_verdict_at) FROM tracks WHERE source_verdict = 'suspect' AND missing_since IS NULL)::timestamptz AS suspect_since,
(SELECT count(*) FROM tracks WHERE missing_since IS NOT NULL)::bigint AS missing,
(SELECT min(missing_since) FROM tracks WHERE missing_since IS NOT NULL)::timestamptz AS missing_since;