TV library fix: the unfiltered Doom Scroll path was calling
/api/browse which explicitly rejects non-mixed libraries with a 400
error, leaving the item list empty. Replace it with the same TV API
hierarchy fetch already used by the filtered path (series → seasons →
episodes), matching how the rest of the TV library is loaded.
Autoplay fix (all library types): two interacting bugs caused videos
to silently stall on navigation. First, the play/pause effect had
current?.url in its deps, so navigating while paused would call
pause() on the freshly-mounted video element before the isPaused reset
could take effect. Second, browser autoplay policy blocks unmuted
play() calls and the rejection was silently swallowed with no recovery.
Fix by merging the isPaused reset and the play() call into one
navigation effect, and adding a muted fallback on rejection so playback
always starts — the user can unmute manually afterward.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove fire-and-forget thumbnail pre-warming from scanMixed(): firing
48k+ simultaneous unresolved getThumbnailPath() promises was saturating
sharp and ffmpeg after scan completion, keeping CPU pegged. Mixed-library
thumbnails are now generated on-demand by /api/thumbnail as before.
- Add incremental fingerprinting: load existing (item_key → fingerprint)
map from DB before each walk; reuse stored fingerprint for unchanged paths
instead of re-reading 64 KB per file. Stable re-scans now do ~0 bytes of
fingerprint I/O.
- Wrap all bulk DB upsert and delete loops in db.transaction() in
scanMovies(), scanTv(), scanMixed(), and reconcileAndPrune(). Reduces
N auto-committed WAL writes to a single batch commit per scan phase.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>