From 6f5907a1d46a1157c463f07042a7366d20d5d11c Mon Sep 17 00:00:00 2001 From: ScreenTinker Date: Tue, 4 Aug 2026 20:26:12 -0500 Subject: [PATCH] BrightSign: supervised player host, JS bridge, and per-group sync backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The player is the unmodified web player in an roHtmlWidget — that already runs on real hardware. What was missing is everything a page cannot do for itself. autorun.brs becomes a host rather than a URL wrapper. It owns the widget lifecycle, because a page-initiated location.reload() does not reliably bring an roHtmlWidget back: a deploy on 2026-07-28 reloaded every connected player and the BrightSign was the only one that never returned. The page now posts {type:"restart"} and the host rebuilds the widget. It also retries load-error with backoff, falls back to a local page, and runs a heartbeat watchdog that catches the case load-error never reports — a page that loaded fine and then wedged on a dead socket or a stalled decoder. st-bridge.js is the page's half over @brightsign/messageport: registry-backed identity (localStorage is origin- and quota-bound, the registry is not), restart-instead-of-reload, heartbeat, and sync-backend reporting. Every method degrades to a no-op off-platform, so it is safe to load unconditionally. sync-backend.js decides whose synchronisation a group runs. Ours is clock-derived and spans any mix of Android, web, Tizen and BrightSign; BrightWall is frame-accurate and BrightSign-only. auto picks native when every member is a BrightSign. The refusal that matters: native sync selected for a mixed group downgrades and says why, because a half-synced group would look perfectly synchronised on the dashboard while one panel drifted alone. Dual output via output_mode single|dual|clone — a second widget loads the same player with &screen=2 so the server can give it its own playlist. Written against the BrightDeveloper docs; not yet run on hardware. The README lists what is unimplemented, including the BrightWall runtime API, which that doc set does not cover. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL --- brightsign/README.md | 145 +++++++++++++----- brightsign/autorun.brs | 246 ++++++++++++++++++++++++++++--- brightsign/st-bridge.js | 213 ++++++++++++++++++++++++++ server/lib/sync-backend.js | 82 +++++++++++ server/test/sync-backend.test.js | 80 ++++++++++ 5 files changed, 703 insertions(+), 63 deletions(-) create mode 100644 brightsign/st-bridge.js create mode 100644 server/lib/sync-backend.js create mode 100644 server/test/sync-backend.test.js diff --git a/brightsign/README.md b/brightsign/README.md index 8f28106..44dfd21 100644 --- a/brightsign/README.md +++ b/brightsign/README.md @@ -1,51 +1,116 @@ -# ScreenTinker on BrightSign — capability probe +# ScreenTinker on BrightSign -Not a port. This answers, on **real hardware**, the questions that decide what a port looks like — -so the design isn't guessed from documentation. +The player is the ordinary web player (`server/player/index.html`) running in an `roHtmlWidget`. +It already runs unmodified on real hardware — a Series 5 (HD1026, BOS 9.1, Chromium 120) played +4,723 items over 12.4h averaging 9.4s against a 10s slot. So the port is not "can it run". It is +the four things a page cannot do for itself. -## Run it +``` + autorun.brs the host: owns the widget, identity, outputs, recovery + | @brightsign/messageport (bidirectional) + st-bridge.js the page's half of the same contract + | + server/player/index.html the unmodified player +``` -1. FAT32-format an SD card. It must be **empty** — a card with leftover data won't trigger a fresh - provisioning cycle. -2. Copy `autorun.brs` and `probe.html` to the **root**. -3. Insert with the player powered off, then power on. -4. Read the screen. Remote devtools are on `http://:2999` if you'd rather read it there. -5. **Power-cycle and reload.** The reboot markers are the point — first run writes them, second run - says which survived. +## Files -## What it answers, and why each matters - -| check | why it decides something | +| file | role | |---|---| -| which `@brightsign/*` modules resolve | `nodejs_enabled: true` injects them into the runtime. If injection is origin-independent, a **remotely-served** page gets them too — which is the whole cheap path. | -| `registry` survives reboot | ScreenTinker's device identity (`deviceId`, `deviceToken`, `paired`, `serverUrl`) lives in `localStorage`, and on BrightSign that behaves like sessionStorage. Without a durable store every panel re-pairs on every boot and spawns a new device row. | -| `localStorage` survives reboot | If it does on this OS build, the port gets dramatically simpler. Reports say it doesn't; worth confirming rather than inheriting a 2019 answer. | -| serviceWorker / Cache API / indexedDB | The web player registers `/player/sw.js` for content caching. If unavailable, offline playback has to move to BrightSign's storage APIs — which is the "extra mile" work anyway. | -| `