Fix library filter, compact dashboard/credentials UI, add fuzzy audio dedup

- beets_service: filter params now use truthy checks instead of `is not
  None`, since a real <select> left on "(any)" submits an empty string,
  not an absent param -- the filter form silently matched zero rows for
  any real submission. Also match grouping tokens within "; "-joined
  multi-playlist values instead of requiring an exact string match.
- dashboard: credential status renders as compact dot indicators instead
  of badge+text chips, so long scope names (telegram) stay on one line.
- credentials page: two-column grid layout instead of one long column.
- find-fuzzy-dupes.py: add --json/--only-paths flags matching
  dedup-library.sh's convention, so it can plug into the same review
  queue.
- dedup_review_service: add scan_fuzzy() and route confirm_and_apply()
  to the correct underlying script (dedup-library.sh vs
  find-fuzzy-dupes.py) per candidate's pass_name, since tag-based passes
  miss duplicates whose tags differ even when the audio is identical
  (e.g. a remix credited to different artists between two copies).
- dedup page: add a "Scan for audio duplicates" trigger alongside the
  existing tag-based scan.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
andrew
2026-07-08 16:25:52 -06:00
parent c87ddc2649
commit 02744f6654
8 changed files with 217 additions and 85 deletions
+36
View File
@@ -491,6 +491,42 @@ tbody td.actions-cell { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.scope-header { display: flex; align-items: center; gap: 0.6rem; }
/* Compact variant for the dashboard's credential-status row -- a dot
indicator instead of a full badge pill so all 7 scopes fit on one line
at normal desktop widths instead of the last one wrapping to its own row. */
.auth-chip-list li {
padding: 0.3rem 0.7rem;
display: inline-flex;
align-items: center;
gap: 0.4rem;
white-space: nowrap;
}
.status-dot {
display: inline-block;
width: 7px;
height: 7px;
border-radius: 50%;
flex-shrink: 0;
background: var(--status-muted);
}
.status-dot-success { background: var(--status-success); box-shadow: 0 0 6px var(--status-success); }
.status-dot-muted { background: var(--status-muted); }
.status-dot-warning { background: var(--status-warning); box-shadow: 0 0 6px var(--status-warning); }
/* Credentials page: two columns on wide viewports instead of one long
single-column stack of 7 scopes. align-items: start so a taller form
(bandcamp's cookie textarea) doesn't stretch its row-mate. */
.cred-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0 2rem;
align-items: start;
}
.cred-scope .panel { max-width: none; }
@media (max-width: 720px) {
.cred-grid { grid-template-columns: 1fr; }
}
@media (max-width: 1040px) {
.topbar-inner { flex-wrap: wrap; }
.nav-pills { order: 3; width: 100%; flex-wrap: wrap; overflow: visible; row-gap: 0.3rem; }