screentinker/server
ScreenTinker 75c1940821 Fix the worker scope that made web offline playback silently inert, and prune superseded assets
Found by QA against a real browser, not by any test in the suite: the bug lived
entirely in the relationship between a URL and a header.

A service worker's default scope is its own directory, so /player/sw.js could
only ever control /player/ and below — which does not include /player itself.
The player is served at all three of /player, /player/ and /player/index.html,
and /player is the one that gets used: it is what the dashboard shows and what
gets typed into a panel. On that URL registration SUCCEEDED, logged "Service
Worker registered", and then controlled nothing. No shell cache, no content
cache, no offline playback, no error. Every web and BrightSign panel served at
/player has been running with its offline story switched off.

Registration now asks for scope '/' and the server sends Service-Worker-Allowed
to permit it. Both halves are required — without the header the registration
does not narrow, it fails outright.

Also: revision-keyed sweeping could not reclaim a replaced asset's predecessor.
A replace writes a NEW randomly-named file, so the superseded copy lives at a
different path entirely and nothing keyed on the asset path can find it; it
would sit there until the quota evicted it. The player now declares the complete
set of media it needs — the raw assignments, so multi-zone items are included
and a prune cannot delete something a zone is still playing — and the worker
drops everything else.

QA results this pass: web player 18/18 against a real browser (cold start with
no network renders a cached video at readyState 4); Android 12/12 on a device
including a replace round-trip that re-fetched 6MB and then dropped it for the
new bytes, and a cold start with the server stopped that played from disk;
Tizen 11/11 for the no-storage path, which must degrade to streaming and must
not claim a capability it cannot honour.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL
2026-08-05 16:30:12 -05:00
..
config fix(logging): gate CF-Connecting-IP on a Cloudflare peer, not any trusted proxy 2026-07-26 10:23:04 -05:00
db Offline media on every player, and a revision so the cache can still be updated 2026-08-05 15:27:36 -05:00
lib Offline media on every player, and a revision so the cache can still be updated 2026-08-05 15:27:36 -05:00
middleware Merge branch 'fix/recovery-grants' into release/auth-campaign 2026-07-26 16:22:59 -05:00
player Fix the worker scope that made web offline playback silently inert, and prune superseded assets 2026-08-05 16:30:12 -05:00
routes Offline media on every player, and a revision so the cache can still be updated 2026-08-05 15:27:36 -05:00
scripts fix(content+android): rotation-aware media — portrait upright on dashboard AND player (#170) (#172) 2026-07-12 22:05:11 -05:00
services Show only the controls a display can actually honour 2026-08-05 14:24:52 -05:00
test Fix the worker scope that made web offline playback silently inert, and prune superseded assets 2026-08-05 16:30:12 -05:00
ws Offline media on every player, and a revision so the cache can still be updated 2026-08-05 15:27:36 -05:00
.gitignore feat(email): Microsoft Graph send + alert spam protection + preferences UI 2026-05-12 18:16:40 -05:00
config.js Add an operator override for self-update on MDM-managed panels 2026-07-28 23:07:30 -05:00
package-lock.json chore(release): v1.9.29-rc3 2026-08-05 11:37:53 -05:00
package.json chore(release): v1.9.29-rc3 2026-08-05 11:37:53 -05:00
server.js Fix the worker scope that made web offline playback silently inert, and prune superseded assets 2026-08-05 16:30:12 -05:00
smoke-ui.js Keep the smoke test out of npm test, and update the lockfile 2026-07-28 20:34:34 -05:00
version.js chore(version): single-source VERSION, env-configurable data paths, bump tooling 2026-06-10 12:56:03 -05:00