The video Library Scan tool only scanned 'all' — but movies and TV are
independent libraries (unlike music's single library). The scanner backend
already supported media_type='movie'|'show'|'all'; this just wires it up:
- /api/video/scan/request now reads media_type and threads it to request_scan
- the Tools card gains a target selector (All / Movies Only / TV Shows Only)
alongside the existing mode dropdown, matching the music scan's UX
- the live status detail reflects the target (no confusing '0 shows' on a
movies-only scan)
Seam test: the endpoint passes both mode and media_type through (default all/full,
explicit movie/deep, TV-only). Existing scanner media-type/scope tests unchanged.
The scan runs server-side, so on load video-scan.js now polls
/api/video/scan/status and, if a scan is in progress, restores the live UI
(Cancel button, moving progress bar, phase) and resumes polling — instead of
showing Idle after a refresh. Also re-checks when the Tools page is shown.
Because it re-emits the progress event, the Library/Dashboard scan affordances
rehydrate too.
Dashboard endpoint now returns the active media server; the Tools card title
becomes '<Server> Library Scan' (e.g. 'Plex Library Scan'), matching how music
prefixes 'Plex Database Updater'.
The scan tool now behaves like music's, not just looks like it:
- Card matches: help '?' button, 'Last Scan' line, and the Movies/Shows/
Episodes/Size stats grid (populated from /api/video/dashboard on show + after
a scan). Same .tool-card-stats markup.
- Real progress bar: scanner fetches item totals up front (Plex section.
totalSize / Jellyfin TotalRecordCount) and reports a true percent as it
processes; the bar actually moves (movies → shows) instead of sitting at 100%.
- Cancel: the Scan button toggles to 'Cancel' mid-scan and POSTs
/api/video/scan/stop; the scanner checks a cancel flag between items and ends
in a 'cancelled' state. Mirrors music's stop affordance.
Tests: percent reported, cancel stops midway + saves only processed items, stop
route registered, tool-card structure. 117 video/integrity tests green.
The visuals were off because I'd invented CSS/markup instead of reusing the
shared design system. Fixed to match music exactly:
- Dashboard Library card now uses music's full markup — header icon, Refresh/
Deep Scan buttons WITH their icons, and stat rows with icons (movies/shows/
episodes/disk). Same .library-status-* classes, no custom CSS.
- Tools 'Library Scan' card now mirrors the music Database Updater: a mode
dropdown (Incremental/Full Refresh/Deep Scan) + one Scan button inside
.tool-card-controls + the standard progress bar. Styling comes for free from
the generic music classes.
- Dropped bespoke .video-tool-btn/.video-scan-controls CSS and folded the
separate video-tools.js into the shared video-scan.js (one fewer file). JS
stays isolated only because it must hit /api/video + update video DOM.
110 tests green.
- New Tools page (video nav + subpage, mirrors music tools styling): a Library
Scan tool card with Incremental / Full Refresh / Deep Scan buttons + a live
status line. Room for more maintenance jobs later.
- Dashboard Library card now has Refresh (full) + Deep Scan buttons, like the
music dashboard.
- Shared video-scan.js controller: one place triggers + polls scans for all
surfaces (wires any [data-video-scan-mode]/[data-video-scan]); emits
soulsync:video-scan-progress/done. Library/Tools/Dashboard just listen — no
duplicated fetch/poll. video-library.js refactored onto it; dashboard reloads
stats on scan-done.
- All isolated IIFEs, data-attr wired (no inline onclick). video-tools added to
the nav (13 pages). 110 tests green.