Part 2 of the faceted plan, Phase 5 (steps 5-6): the scan that used to
print ~4 lines per unchanged file now prints ONE summary line, and a
poll tick landing mid-scan no longer stacks a second full scan.
- Quiet scans (§4): the walk loop is silent — inserts, changes, and
errors log inside processMediaFile; the duplicate
'Successfully processed' branch (both branches printed it) and the
per-file 'processing / already exists / same size, skipping' chatter
are gone. Summary carries the trigger and a changed counter:
'Scan of <folders> (trigger: poll): N files, N changed, N new, Xs,
N errors'. Triggers threaded through every enqueue site: poll
(startBackupScan), watcher (dirty-dirs flush), admin (scan API),
initial (startup scan).
- Enqueue coalescing (§5): Worker.HasActiveScanForLibrary (JobResult
gains additive job_type so only SCAN jobs coalesce — an import
sharing the library id never blocks one). enqueueLibraryScan skips
silently; the admin scan API answers 409 with the active job id.
Enforces the one-scan-per-library invariant the bulk scan-state map
leans on.
- Watcher capability probe (§4): ONE Add on a root folder; failure
disables the watcher with a single startup line (replaces the
per-directory warning spam on inotify-broken deployments). The
watcher stays an optional latency reducer over scheduled scans.
- Per-directory sibling cache (§6): hasSiblingBookFileCached caches
the ReadDir per directory for one scan — loose-image manga re-stats
thousands of images sharing a handful of directories.
Tests: TestHasActiveScanForLibrary (unit — running/pending coalesce,
terminal jobs don't, non-scan jobs don't, empty library never, nil
worker safe). Full scanner suite green in the container.
Bug: the admin 'Scanning Libraries' widget fed the server's 0-1
progress straight into percent strings and bar widths, so the overall
and per-library percentages capped at '1%' and the bars never filled.
Both now scale by 100 (matching the setup page, which was already
correct).
Bug: scan state only reached clients via websocket events, so a page
opened (or refreshed) mid-scan showed nothing until the next tick —
and the admin widget never re-attached at all. The worker now exposes
active jobs (with their library_id so rows keep their names), served
as GET /api/scanner/active (admin, re-attaches the widget + polling)
and GET /api/scanner/active-summary (any user, restores the sidebar
indicator on load). Idle hides both, exactly as before.
Annoyance: the sidebar's 'Scanning… N%' was a block element between
the logo and the nav, shoving every menu entry down while a scan ran.
The percent now lives inside the fixed-height logo row next to the
spinner (spinner last, so growing digits extend leftward); nothing
below it moves.