screentinker/tizen/config.xml
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

47 lines
2.8 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<widget xmlns="http://www.w3.org/ns/widgets" xmlns:tizen="http://tizen.org/ns/widgets"
id="http://screentinker.com/player" version="1.9.29" viewmodes="maximized">
<tizen:application id="ScrnTinkr1.ScreenTinker" package="ScrnTinkr1" required_version="2.4"/>
<tizen:profile name="tv"/>
<name>ScreenTinker</name>
<author email="support@screentinker.net">ScreenTinker</author>
<description>ScreenTinker digital signage player</description>
<icon src="icon.png"/>
<content src="index.html"/>
<!-- Landscape signage, no context menu, allow background, keep app full-screen -->
<tizen:setting screen-orientation="landscape" context-menu="disable"
background-support="enable" encryption="disable"
install-location="auto" hwkey-event="enable"/>
<feature name="http://tizen.org/feature/screen.size.all"/>
<!-- Allow the player to reach any signage server + load remote media / YouTube -->
<access origin="*" subdomains="true"/>
<tizen:allow-navigation>*</tizen:allow-navigation>
<tizen:privilege name="http://tizen.org/privilege/internet"/>
<tizen:privilege name="http://tizen.org/privilege/application.launch"/>
<tizen:privilege name="http://tizen.org/privilege/display"/>
<!-- tizen.tvaudiocontrol: sets the TV's OWN volume, which is what reaches AVPlay video on the
hardware plane. Without this privilege the API throws SecurityError and the player falls
back to per-element media volume, which cannot touch that plane. -->
<tizen:privilege name="http://tizen.org/privilege/tv.audio"/>
<!-- Offline media cache (js/media-cache.js). Content is written to wgt-private, which is the
store Tizen documents as surviving reboots and app updates — without these the player can
hold a playlist through an outage but none of the media it points at, which from the floor
is the same as holding nothing. -->
<tizen:privilege name="http://tizen.org/privilege/filesystem.read"/>
<tizen:privilege name="http://tizen.org/privilege/filesystem.write"/>
<tizen:privilege name="http://developer.samsung.com/privilege/network.public"/>
<!-- #125: Samsung B2B fleet control (reboot / panel power via b2bcontrol /
systemcontrol). These are PARTNER-level privileges: they only take effect
when the .wgt is signed with a Samsung Partner distributor certificate on a
real SSSP panel. On unsigned / URL-Launcher / web / consumer-TV builds they
are ignored (not fatal) — the APIs are simply absent and device-control.js
reports "unsupported". -->
<tizen:privilege name="http://developer.samsung.com/privilege/b2bcontrol"/>
<tizen:privilege name="http://developer.samsung.com/privilege/systemcontrol"/>
</widget>