mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-14 14:23:14 -06:00
2 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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 |
||
|
|
8fd6eb75d5 |
BrightSign: cache content for offline, and let the package update itself
Two gaps that both end the same way — a panel nobody can fix without a van. OFFLINE. Content bytes were never persistently cached. The service worker skipped /uploads/content/ and leaned on the browser's HTTP cache, which is reasonable on a desktop and is not a documented-persistent store here: BrightSign guarantees survival across reboots for IndexedDB, localStorage and SQLite, and their own answer for offline video is to cache the bytes explicitly. A panel could come back from a power cut with its playlist intact — that lives in localStorage — and no media to play it with. The reason content was skipped is real, and player-cache-policy.js is what makes intercepting it safe. Seeking video issues range requests, and naive caching is worse than none: storing a 206 as the whole file means every later full request gets a fragment, and answering a range request with a 200 makes some media stacks fail outright. So only complete 200s are stored, and ranges are served by slicing the stored body into a correct 206. The content cache survives shell re-versioning, or every deploy would re-download the playlist over a link that may be exactly what is broken. SELF-UPDATE. The package can replace autorun.brs, so a truncated file is a dark panel with no app underneath. The safety is the ordering: download to .part, verify sha256 AND size, then delete the .done marker, rename, reboot. Marker first is not stylistic — leaving it makes the next boot skip the archive and the update silently never happens. A failed extract parks the zip as .bad instead of retrying every boot, which would be a loop indistinguishable from a hardware fault. sha256 because that is what roMessageDigest can compute; a checksum the player cannot verify is an unverifiable package. The decision lives on the server and is unit-tested, and the host only executes it — re-implementing the version comparison in BrightScript would put the prerelease trap somewhere untestable. That trap is honoured directly: a player on 1.9.29-rc1 is running something semver-OLDER than 1.9.29, so an opted-in player HOLDS a prerelease of its own core rather than being pulled off the build it was given to test. Narrowly — a newer core still lands, so opting in never means never updating again. Both loop conditions are closed by construction. The manifest and the download come from one buffer hashed once, so a checksum cannot describe bytes we are not serving. And the version is stamped into autorun.brs at build time by both builders, so the script reports the version it actually is — otherwise the player applies the update, still reports the old version, and is offered the same package forever. Failure always degrades to "keep running the old version": an unreachable manifest, a missing checksum, a failed verification, a full attempt counter and an unbuildable package all resolve to skip. 998 tests pass (was 954). |