Both add-section paths on Settings > Home Screen called crypto.randomUUID()
unguarded. Browsers only expose randomUUID in secure contexts, so on
self-hosted servers accessed over plain HTTP the click handler threw
synchronously and the Add section button appeared dead while Cancel still
worked. api/client.ts and plexAuth.ts already carried ad-hoc fallbacks for
the same problem; extract a shared lib/uuid helper (UUIDv4 via
crypto.getRandomValues, available in insecure contexts) and use it at all
four call sites.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review follow-up: normalizePath strips a trailing slash, but the slash is
semantic for legacy-scope changes — filepath.Dir("/x/Show/") is the
directory itself while filepath.Dir("/x/Show") is its parent, so dropping
it widened targeted directory notifications into parent/library scans.
applyRewrites now records whether the incoming path ended with a separator
(either form) and restores it on the returned path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Incoming webhook paths were only separator-swapped (normalizeSeparators),
while rewrite From values went through normalizePath, which also collapses
duplicate slashes. A Windows UNC root from a Windows-hosted arr
(\\NAS\Media\TV -> //NAS/Media/TV) therefore never prefix-matched its
rewrite rule (/NAS/Media/TV), so every import logged "webhook paths
matched no library folder" and nothing scanned.
applyRewrites now normalizes the incoming path with the same normalizePath
used for the stored From, and normalizes the joined result so a
trailing-slash To cannot produce a doubled separator.
Reported via internal Discord thread (Sonarr on Windows with a UNC TV
root posting to the autoscan webhook).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Scanner re-enqueues (priority 100) could silently promote pending legacy
backlog rows (priority -100) into the incremental lane via the enqueue
upsert's GREATEST, and the fail/release requeue branches hardcoded 100
regardless of the row's lane. A mass mtime shift or group-key-version
bump would have moved the entire legacy backlog out from under the
backfill task's pacing controls into the scheduled sync task.
Lane changes now happen only through terminal outcomes (complete or
discard); enqueue, fail, and release preserve a negative priority.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Forward start, pause, resume, and stop events with stable media identities
- Persist and retry terminal scrobbles across teardown and restart paths
- Reject ambiguous playback-report route matches
- Add a client uploads toggle with status refresh and feedback
- Replace native date filters with calendar and time pickers
- Improve responsive filter layout and test upload settings
Review feedback: window is shared across vitest tests, so repeated
installs accumulated listeners. The installer now returns a remover and
the tests detach in afterEach.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
After a deploy, an open tab still references the previous build's
content-hashed chunks and the first lazy navigation dies with "Failed to
fetch dynamically imported module". Handle Vite's vite:preloadError by
reloading onto the current build, guarded to at most one reload per
minute so a persistently missing chunk cannot reload-loop.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A /assets/ chunk from a previous build no longer exists after a deploy;
serving the SPA shell at that URL makes the browser fail dynamic imports
on a text/html module. Exclude /assets/ from the SPA fallback so the
miss surfaces as a 404 the client can react to.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Give the backfill task a default 15-minute interval trigger. With the
rate-limit cooldown floor each run meets a fresh ready-set, a saturated
batch trips the zero-progress breaker, and an empty lane exits in
milliseconds, so the backlog drains at provider speed unattended. The
canary claim cap and batch delay keep their semantics, and operators can
retune or disable the trigger through the admin task UI.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The ebook-metadata plugin attaches ~1s RetryInfo to ResourceExhausted
errors as request-pacing advice for its internal token bucket. Adopting
that hint verbatim as the queue horizon made rate-limited rows claimable
again immediately, so every backfill run re-claimed the same saturated
tail. Clamp rate-limited requeues to a 15m floor (SILO_EBOOK_RATE_LIMIT_COOLDOWN
to tune); hints above the floor are honored up to the existing 24h cap.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A claim whose enrichment surfaced context.Canceled while the sweep was
still live was released uncounted, leaving it immediately reclaimable and
invisible to the no-progress circuit breaker. Let it flow through the
failure path as a transient error so it backs off and is counted; genuine
sweep shutdown still releases via the existing ctx check.
Claims discarded because the item vanished are terminal, not retried, so
report them in a new discarded counter instead of inflating deferred, and
surface the count in task progress output.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Close the symlink-swap window in the sidecar reader: os.Open follows
symlinks, so a leaf swapped between the Lstat gate and the open could pull
metadata from outside the library root. Reject unless the opened handle is
the exact file Lstat inspected, matching the image cache processor guard.
Replace a stale ISBN provider id on rescan instead of silently dropping it,
so sidecar ISBN corrections actually take effect; tolerate the ISBN already
belonging to another item so duplicate copies keep scanning.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>