Skip to content

scan progress: core API + UI poller (extracted from #558)#571

Merged
IrosTheBeggar merged 3 commits intomasterfrom
claude/scan-progress-extract
Apr 25, 2026
Merged

scan progress: core API + UI poller (extracted from #558)#571
IrosTheBeggar merged 3 commits intomasterfrom
claude/scan-progress-extract

Conversation

@IrosTheBeggar
Copy link
Copy Markdown
Owner

Summary

Extracts the scan-progress refactor and a few small modernizations out of PR #558 (Syncthing + Electron Desktop Player), so they can land independent of the Desktop Player work. The Syncthing/Electron features stay on #558.

Three commits, each independently revertable:

update actions

Workflow version bumps that hadn't already been merged to master. Honors master's deletion of test-ffmpeg-bootstrap.yml.

drop unused deps: axios, winston-daily-rotate-file, make-dir

  • axiosPOST /api/v1/lastfm/test-login was the last caller; swapped for native fetch (Node ≥22).
  • winston-daily-rotate-file — replaced with a small in-process rotator in src/logger.js (opens a fresh file every hour, prunes anything older than 14 days).
  • make-dirmakeDirectorySync was the only caller; native fs.mkdirSync(p, { recursive: true }) covers it. Also tightens the playlist-resolver loop in src/api/file-explorer.js and adds a skipped count to the response.

scan progress: core API + UI poller

The actual feature this PR is about. Three problems with the current scan-status flow:

  1. The scan_progress table was readable only via GET /api/v1/admin/db/scan/progress, mounted in velvet-stubs.js. On default-UI installs the endpoint didn't exist (velvet-stubs only loaded when ui='velvet'), and it was admin-gated everywhere.
  2. The default UI's player polled /api/v1/db/status (coarse locked boolean + total file count) and only set up its 2s interval after seeing locked: true once. A scan triggered after page load went invisible until refresh.
  3. The renderer only knew how to write 'Scan In Progress' + a count to two divs. No per-vpath progress, no current-file display.

Server

  • src/api/scan.js (NEW) — GET /api/v1/scan/progress. Authenticated but not admin-only. Filters rows to vpaths the caller can see; admins also see pre-vpath 'counting' rows. currentFile is stripped to basename so absolute server paths don't leak.
  • src/api/velvet-stubs.js — drops the old /api/v1/admin/db/scan/progress; one comment in its place.
  • src/server.js — mounts scanApi alongside the other core APIs (always loaded).

Webapp

  • webapp/alpha/scan-progress.js (NEW) — polls every 3s unconditionally. Empty response → empty wrap → no visible UI when idle. Renders a per-vpath card with vpath name, progress bar, percent, scanned/expected counts, current-file as the title attribute.
  • webapp/alpha/spa.css.spc-* rules for the cards (palette adapted to the main UI's colors).
  • webapp/index.html — replaces <div id='scan-status'> + <div id='scan-status-files'> with <div id='scan-progress-wrap'>; loads the new script.
  • webapp/alpha/m.js — removes the old async dbStatus() + its caller + the unused startInterval. The function wrote to the now-removed scan-status DOM ids and would have thrown on every page load.

MSTREAMAPI.dbStatus and GET /api/v1/db/status are left in place — both are public surface and may have external callers.

Verification

  • Lint: no new errors vs. master on any touched file. Fixed two pre-existing prefer-const / no-useless-assignment issues in src/logger.js along the way.
  • node --check passes on every changed .js file.
  • Behavior (manual smoke): kick a scan from the admin panel after the player is already loaded — cards now appear within 3s and update through the scan; when the scan finishes the cards disappear cleanly. Scrobble flow round-trips a track via the new fetch-based test-login. Logs rotate correctly without winston-daily-rotate-file.

Compatibility

  • The new endpoint is at /api/v1/scan/progress — does not collide with any existing route.
  • The old admin endpoint is removed; the only known caller (which lived in this same UI) is replaced. Subsonic's separate getScanStatus handler is unchanged.
  • MSTREAMAPI.dbStatus and GET /api/v1/db/status are unchanged.

Test plan

  • Open the player UI, then trigger a scan from /admin. Within ~3s, a per-vpath card should appear in the navbar with progress.
  • Let the scan complete; the card should disappear when scan_progress is cleared by task-queue.js:onScanClose.
  • Hit /api/v1/scan/progress directly as a non-admin user — should return rows for that user's vpaths only, with currentFile containing only basenames.
  • Configure a Last.fm account via /api/v1/lastfm/test-login — should round-trip without axios installed.
  • Boot with writeLogs: true, run for an hour, confirm a new mstream-YYYY-MM-DD-HH.log file rolls over.

🤖 Generated with Claude Code

(cherry picked from commit b25b40d)
Three small modernizations from PR #558 (the syncthing/electron PR), pulled
out so they can land independent of the Electron Desktop Player work.

* src/api/scrobbler.js — POST /api/v1/lastfm/test-login was the last
  caller of axios. Switched to native fetch (Node ≥22), drop the dep.
* src/logger.js — was using winston-daily-rotate-file for log rotation.
  Replaced with a small in-process rotator that opens a fresh file every
  hour and prunes anything older than 14 days.
* src/api/file-explorer.js — `makeDirectorySync` from `make-dir` was the
  only caller; native `fs.mkdirSync(p, { recursive: true })` covers the
  same use. Also tightened the playlist-resolver loop and added a
  `skipped` count to the response so clients can see when entries were
  filtered for traversal.
Lifts the scan-progress refactor out of PR #558 (syncthing/electron) so
the player UI can show live scan progress on a default-UI install today,
without waiting for the Desktop Player feature to ship.

Three problems with the old flow:

  1. The scan_progress table was readable only via /api/v1/admin/db/scan/
     progress, mounted in velvet-stubs.js and gated under /admin/. On a
     default-UI install the endpoint didn't exist (velvet-stubs only loaded
     when ui='velvet'), and even on velvet, non-admin users couldn't reach it.
  2. The default UI's player polled /api/v1/db/status (coarse boolean lock +
     total file count) and only set up its 2s interval AFTER seeing
     locked=true once. A scan triggered after page-load went invisible until
     the next refresh.
  3. The renderer only knew how to write 'Scan In Progress' + a count to a
     pair of divs. No per-vpath progress, no current-file display.

Changes:

* src/api/scan.js (NEW) — GET /api/v1/scan/progress. Authenticated but
  not admin-only. Filters rows to vpaths the caller can see; admins also
  see pre-vpath 'counting' rows where the scanner hasn't assigned a
  library yet. currentFile is stripped to basename so absolute server
  paths don't leak.
* src/api/velvet-stubs.js — drop the old /api/v1/admin/db/scan/progress;
  one comment in its place pointing at the new endpoint.
* src/server.js — mount scanApi alongside the other core APIs (always
  loaded, regardless of ui setting).
* webapp/alpha/scan-progress.js (NEW) — polls every 3s unconditionally.
  Empty response → empty wrap → no visible UI when idle. Renders a
  per-vpath card with vpath name, progress bar, percent, scanned/expected
  counts, and current-file as the title attribute.
* webapp/alpha/spa.css — .spc-* rules for the cards (palette adapted to
  the main UI's colors; the velvet variables aren't defined here).
* webapp/index.html — replaces the legacy <div id='scan-status'> +
  <div id='scan-status-files'> with <div id='scan-progress-wrap'>; loads
  the new scan-progress.js script.
* webapp/alpha/m.js — removes the old async dbStatus() + its caller +
  the unused startInterval. The function wrote to the now-removed
  scan-status DOM ids and would have thrown on every page load.

MSTREAMAPI.dbStatus and the GET /api/v1/db/status endpoint are left in
place — both are public surface and may have external callers.
@IrosTheBeggar IrosTheBeggar force-pushed the claude/scan-progress-extract branch from ec8ae11 to c5eb2f2 Compare April 25, 2026 19:02
@IrosTheBeggar IrosTheBeggar merged commit f7e5072 into master Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant