screentinker/server/ws
ScreenTinker 684e60fc55 Offline media on every player, and a revision so the cache can still be updated
Two halves of the same problem. A screen has to keep playing when the link is
gone, and it must not keep playing the wrong thing once the link is back.

CACHING FOR OFFLINE, on the players that could not:

- Tizen cached nothing but the playlist, so a panel came back from a reboot
  knowing exactly what to show and fetched every frame of it from a server that
  was not there. tizen/js/media-cache.js caches the media itself to wgt-private
  (the store Tizen documents as surviving reboots), resumable via Range and
  If-Range, with the transfer async so a stalled chunk cannot freeze the player.
  offline.cache moves from "absent" to a runtime claim: a build with no writable
  private storage still says nothing.

- The web player's worker stored only what a single fetch() happened to
  complete, which on a marginal link is nothing at all — a 200MB asset never
  finishes in one go and every retry starts from zero. It now accumulates in
  resumable chunks, driven by the player's playlist rather than by playback, so
  the prefetch is not competing with the video that is currently on screen for
  the same scarce bandwidth. BrightSign inherits this.

STILL UPDATING, which caching quietly breaks:

PUT /api/content/:id/replace changes an asset's bytes under a stable id. Every
cache keys on that id, so before this the new bytes could not reach a panel that
already held the old ones — not until the next refresh, but never. Content now
carries a revision, stamped onto each item at send time like widget revs, and
every player keys its cache on it. The same send-time refresh fixes a second
bug: a replace writes a new randomly-named file and unlinks the old one, so the
filepath in a published snapshot pointed at a deleted file and web panels 404'd
on the item until somebody republished the playlist. The route now also pushes
to affected devices, which it never did.

Bytes are kept only where they can be built upon: no validator means no safe
resume, so the partial is discarded and the attempt backs off as the failure it
is rather than re-fetching the same prefix forever.

Server needed no new transfer support — res.sendFile already does Range,
If-Range and 416. The Tizen cache and the service worker are both driven in Node
against fakes, because neither can be exercised without hardware and "the chunks
assemble correctly" is not something to discover from a panel showing a corrupt
video.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL
2026-08-05 15:27:36 -05:00
..
dashboardSocket.js Show only the controls a display can actually honour 2026-08-05 14:24:52 -05:00
deviceSocket.js Offline media on every player, and a revision so the cache can still be updated 2026-08-05 15:27:36 -05:00
index.js Initial open source release 2026-04-08 12:14:53 -05:00