Commit graph

40 commits

Author SHA1 Message Date
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
ScreenTinker c07a56b47d Tizen declares what it can do, and volume and blanking now work
The dashboard offered every control to every display, so on a Tizen panel the volume
slider and screen_off did nothing and read as bugs. Two of them were genuinely dead:

  - set_volume fell through STDeviceControl.run()'s default case and was answered
    "unknown command". It is not a Samsung fleet action and must work on every build,
    so it is handled in app.js instead: tizen.tvaudiocontrol where the TV profile
    provides it (that is the TV's own volume, the only thing that reaches AVPlay video
    on the hardware plane), otherwise the media elements. The level is remembered and
    re-applied on 'play' — media elements are created per item, so a one-shot set
    lasted only until the playlist advanced.

  - screen_off was a z-index overlay, which covers the web layer only. Portrait and
    flipped video runs through AVPlay on a separate hardware plane the DOM cannot draw
    over, so the overlay went up and the video played straight through it. It now tears
    the AVPlay session down as well; screen_on re-mounts via playCurrent(), because a
    torn-down session cannot be resumed and gotoIndex() early-returns on an unchanged
    index.

js/capabilities.js declares the rest at runtime rather than from a static table,
because on Tizen the answer varies by build: reboot exists only through the B2B
surface injected on a partner-signed .wgt, and tizen.tvaudiocontrol is absent in a
browser context. Against the server baseline this adds display.power, remote.screenshot
and remote.stream (all backed by real handlers) and drops offline.cache — the payload
is cached, but media bytes are still fetched from the network, so content does not
survive an outage and claiming it would overstate.

Adds the tv.audio privilege; without it tvaudiocontrol throws SecurityError.
2026-08-05 14:11:48 -05:00
ScreenTinker b15b17f5dd chore(release): v1.9.29-rc1 2026-08-04 23:29:37 -05:00
ScreenTinker ff7bfb2ded chore(release): v1.9.28
Some checks failed
CI / Unit tests (node --test) (push) Has been cancelled
CI / OpenAPI spec lint (push) Has been cancelled
CI / Android unit tests (Kotlin schedule evaluator vectors) (push) Has been cancelled
CI / Boot smoke + version check (push) Has been cancelled
2026-07-30 23:02:18 -05:00
ScreenTinker 752f39ea43 chore(release): v1.9.27 2026-07-30 19:16:55 -05:00
ScreenTinker d70764991e chore(release): v1.9.26 2026-07-30 18:43:35 -05:00
ScreenTinker 3f0db335d2 chore(release): v1.9.25 2026-07-29 20:38:47 -05:00
ScreenTinker c115ad5e62 chore(release): v1.9.24
Some checks are pending
CI / Unit tests (node --test) (push) Waiting to run
CI / OpenAPI spec lint (push) Waiting to run
CI / Android unit tests (Kotlin schedule evaluator vectors) (push) Waiting to run
CI / Boot smoke + version check (push) Waiting to run
2026-07-28 23:37:59 -05:00
ScreenTinker bcb1b5c7a3 chore(release): v1.9.23 2026-07-28 20:43:34 -05:00
ScreenTinker 19d1e3e19f chore(release): v1.9.22 2026-07-28 14:58:22 -05:00
ScreenTinker 2d4ebb800f chore(release): v1.9.21 2026-07-28 14:01:09 -05:00
ScreenTinker 00c294cb3b chore(release): v1.9.20-beta1 2026-07-28 12:53:08 -05:00
ScreenTinker 6e0b2464d7 chore(release): v1.9.19 2026-07-27 21:18:47 -05:00
ScreenTinker 90fd73b38c chore(release): v1.9.18 2026-07-27 20:40:51 -05:00
ScreenTinker d6f81171c2 chore(release): v1.9.17 2026-07-27 11:43:23 -05:00
ScreenTinker 1036333982 chore(release): v1.9.16 2026-07-27 10:37:11 -05:00
ScreenTinker 593458d519 chore(release): v1.9.15 2026-07-24 21:12:22 -05:00
ScreenTinker 7b4e5bf416 chore(release): v1.9.14-beta1 2026-07-23 22:10:05 -05:00
ScreenTinker ea99ae5e8d chore(release): v1.9.13-beta1 2026-07-23 12:39:30 -05:00
ScreenTinker 79ab849641 chore(release): v1.9.12
Some checks are pending
CI / Unit tests (node --test) (push) Waiting to run
CI / OpenAPI spec lint (push) Waiting to run
CI / Android unit tests (Kotlin schedule evaluator vectors) (push) Waiting to run
CI / Boot smoke + version check (push) Waiting to run
2026-07-22 21:09:09 -05:00
ScreenTinker 6b5b401f7c chore(release): v1.9.11 2026-07-20 16:59:56 -05:00
ScreenTinker af89eaa75b chore(release): v1.9.10
Some checks failed
CI / Unit tests (node --test) (push) Has been cancelled
CI / OpenAPI spec lint (push) Has been cancelled
CI / Android unit tests (Kotlin schedule evaluator vectors) (push) Has been cancelled
CI / Boot smoke + version check (push) Has been cancelled
2026-07-17 20:24:39 -05:00
ScreenTinker 79cf4cac2b chore(release): v1.9.9
Some checks are pending
CI / Unit tests (node --test) (push) Waiting to run
CI / OpenAPI spec lint (push) Waiting to run
CI / Android unit tests (Kotlin schedule evaluator vectors) (push) Waiting to run
CI / Boot smoke + version check (push) Waiting to run
2026-07-15 16:06:18 -05:00
ScreenTinker 87d5c5e1d7 chore(release): v1.9.8 2026-07-15 10:06:11 -05:00
ScreenTinker f10373551f chore(release): v1.9.7
Some checks are pending
CI / Unit tests (node --test) (push) Waiting to run
CI / OpenAPI spec lint (push) Waiting to run
CI / Android unit tests (Kotlin schedule evaluator vectors) (push) Waiting to run
CI / Boot smoke + version check (push) Waiting to run
2026-07-13 23:10:57 -05:00
ScreenTinker 3c59f647ac chore(release): v1.9.6 2026-07-13 12:12:03 -05:00
ScreenTinker 40efaa1fed chore(release): v1.9.5
Some checks are pending
CI / Unit tests (node --test) (push) Waiting to run
CI / OpenAPI spec lint (push) Waiting to run
CI / Android unit tests (Kotlin schedule evaluator vectors) (push) Waiting to run
CI / Boot smoke + version check (push) Waiting to run
2026-07-12 23:32:37 -05:00
ScreenTinker f7e1b4dc69 chore(release): v1.9.4 2026-07-10 11:57:43 -05:00
ScreenTinker dfd954d2ad chore(release): v1.9.3 2026-07-08 17:23:08 -05:00
ScreenTinker d9fb914b9e chore(release): v1.9.2-beta1
Some checks failed
CI / Unit tests (node --test) (push) Has been cancelled
CI / OpenAPI spec lint (push) Has been cancelled
CI / Android unit tests (Kotlin schedule evaluator vectors) (push) Has been cancelled
CI / Boot smoke + version check (push) Has been cancelled
2026-06-27 19:59:34 -05:00
ScreenTinker e2ff8f47b7 Tizen player: real Samsung B2B fleet control (#125), folding in #126
#123 already shipped a placeholder device:command handler (#121/#122): screen_off
was a black overlay, reboot/shutdown a toast, update a "re-install" toast. This
replaces that with the real control surface from #125, reconciled into the single
handler #123 introduced (rather than landing a second, competing handler).

- NEW tizen/js/device-control.js: window.STDeviceControl = { run, capabilities,
  backend }. Feature-detects webapis.systemcontrol.* (Tizen 6.5/7, sync/throws) then
  b2bapis.b2bcontrol.* (SSSP/Tizen 4, async), normalises both to Promises, re-probes
  each call. run() never rejects; resolves { ok, supported, action, note, reload }.
  Panel power: setPanelMute (mute ON = backlight OFF) -> setDisplayPanel/setPanelStatus
  fallback. reboot -> rebootDevice(); shutdown mutes the panel and notes SSSP has no
  true power-off; update/reload -> reload:true.
- tizen/js/app.js: device:command now calls STDeviceControl.run and reports the
  outcome via reportCmd (device:log tag=command -> dashboard:device-log, plus a
  structured device:command-result), reloading ~1.2s later on result.reload. screen_off
  falls back to the existing black overlay (showScreenOff) when no B2B surface exists;
  screen_on/launch still clear the overlay + keepAwake. Dropped the now-dead
  tryPowerControl. reportCapabilities() runs on device:registered so the dashboard sees
  the backend ("none" on web/URL-Launcher/consumer TV).
- tizen/config.xml: partner-level b2bcontrol + systemcontrol privileges (ignored, not
  fatal, on unsigned/URL-Launcher/web/consumer builds).
- tizen/index.html: load $WEBAPIS/webapis.js + $B2BAPIS/b2bapis.js before the app
  scripts (404 harmlessly off-hardware) and device-control.js before app.js.
- tizen/README.md: rewrote the remote-control table for real B2B control + a
  partner-signing caveat; added device-control.js to the file list.

Supersedes PR #126 (feat/tizen-device-command-125), which targeted main unaware that
this branch already had a device:command handler.

Verified: node --check on both JS files; config.xml well-formed (xmllint). Not yet
validated on a real SSSP panel — the control surface only takes effect on a
partner-signed .wgt (backend reports "none" on the dev/URL-Launcher build).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 13:28:08 -05:00
ScreenTinker 1f794ff7b4 chore(release): v1.9.1-beta1
Some checks are pending
CI / Unit tests (node --test) (push) Waiting to run
CI / OpenAPI spec lint (push) Waiting to run
CI / Android unit tests (Kotlin schedule evaluator vectors) (push) Waiting to run
CI / Boot smoke + version check (push) Waiting to run
2026-06-12 22:37:50 -05:00
ScreenTinker 4b688fcfb1 chore(release): v1.9.0
Some checks are pending
CI / Unit tests (node --test) (push) Waiting to run
CI / Android unit tests (Kotlin schedule evaluator vectors) (push) Waiting to run
CI / Boot smoke + version check (push) Waiting to run
2026-06-11 21:11:31 -05:00
ScreenTinker e8a318e5fb chore(release): v1.8.3
Some checks are pending
CI / Unit tests (node --test) (push) Waiting to run
CI / Boot smoke + version check (push) Waiting to run
2026-06-11 09:38:42 -05:00
ScreenTinker 53e32d31e2 fix(bump-version): do not rewrite the tizen config.xml XML declaration (#77)
bump-version.sh matched `<?xml version="1.0" ...?>` - the XML format version,
which has a leading space before version= just like the widget attribute - and
rewrote it to the app version, producing invalid XML that breaks the Tizen .wgt
build: 'XML version "X.Y.Z" is not supported, only XML 1.0 is supported'. (CI did
not catch it because the no-Tizen-CLI build path just zips the files without
validating the XML.)

- bump-version.sh: skip the `<?xml` declaration line in the tizen version sed.
- tizen/config.xml: restore the declaration to version="1.0" (prior bumps had
  corrupted it to 1.8.2).

The widget version and tizen:application required_version are still updated /
left alone correctly (verified with a dummy bump + an XML parse).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 08:56:59 -05:00
ScreenTinker 3545830ea6 chore(release): v1.8.2 2026-06-11 08:42:57 -05:00
ScreenTinker e9c89343d7 chore(release): v1.8.1 2026-06-10 14:12:47 -05:00
ScreenTinker 4f56199bc7 chore(release): v1.8.0 2026-06-10 13:46:17 -05:00
ScreenTinker 5396cf9896 chore(tizen): dev-signing setup + support@screentinker.net author email
- config.xml author email -> support@screentinker.net
- build-wgt.sh: stage app files only before signing (keeps README/build script
  out of the .wgt), auto-add the Tizen CLI to PATH if installed.
- README: document the configured 'ScreenTinker' signing profile (self-signed
  author + default Tizen distributor) — installs on dev-mode TVs / emulator;
  production retail needs a Samsung distributor cert.

Signed .wgt + the author cert are not committed (build artifact / secret).
2026-06-09 19:10:28 -05:00
ScreenTinker 0cfa09046c feat(tizen): Samsung Tizen TV web player (.wgt)
Ports the ScreenTinker player to a Tizen TV / signage web app, speaking the
SAME /device socket.io protocol as the Android player — no server changes; a
Tizen display pairs from the same dashboard.

- app.js: device protocol client — register (pairing_code | device_id+token),
  device:registered/paired/unpaired/playlist-update, 15s heartbeat, keep-awake.
  Always reaches the server prompt until the display is actually paired; a
  saved-but-unreachable server falls back to the prompt (no blank screen); BACK
  returns to it.
- player.js: fullscreen single-zone renderer — image (duration timer), video
  (play-to-end + loop), YouTube (iframe embed), widget (iframe render endpoint).
- config.xml: Tizen TV manifest; build-wgt.sh packages (signs if Tizen CLI
  present, else unsigned); README covers URL-Launcher and signed-.wgt deploy.

Validated: headless protocol test vs the live server passed end-to-end
(register -> pair -> reconnect-auth -> playlist(2) -> content 200); loads +
renders in Chromium with no JS errors.

Not yet ported (fullscreen single-zone covers most signage): multi-zone, video
walls, screenshots, remote control, self-OTA. .wgt is a build artifact (gitignored).
2026-06-09 19:01:58 -05:00