screentinker/tizen
ScreenTinker eb3abf3a0e Tizen player 1.9.1-beta3: bug fixes, multi-zone layouts, video walls
Brings the Tizen TV player to parity with the other players: closes the five
Tizen issues Bold Media Group filed (#118-#122) and adds the two larger renderer
features it was still missing.

Fixes (#118-#122)
- #118 Sticky "Not authenticated" banner. On TV sleep/wake the socket reconnects
  and a heartbeat could land on the fresh, not-yet-registered socket; the server
  rejected it and the old handler painted a permanent banner AND dropped the saved
  credentials, forcing a re-pair. Heartbeats are now gated on a per-connection
  authenticated flag (true only between device:registered and disconnect/auth-error),
  the heartbeat stops on connect/disconnect/auth-error, the banner clears on
  device:registered, and the auth-error toast is non-sticky.
- #119 app_version stuck at 1.0.0. Resolved at runtime from config.xml via the Tizen
  application API, with a fallback constant that build-wgt.sh stamps from config.xml.
- #121 Remote commands. Added a device:command handler (refresh/launch/screen_on/
  screen_off; honest no-op toasts for update/reboot/shutdown, which need B2B/MDM
  privileges a sideloaded app lacks). Removed the dead device:reload listener.
- #120 Dashboard preview. Added device:screenshot-request + remote-start/remote-stop.
  Images capture; video/YouTube fall back to a status card (TV hardware video plane
  and cross-origin iframes can't be read into a canvas).
- #122 Updates/boot. Documented the real paths (re-sideload or URL Launcher/MDM
  refresh; display-level kiosk/boot settings) since a sideloaded .wgt has no in-app
  OTA or config.xml autostart.

Multi-zone layouts (Android parity)
- New ZoneRenderer ports the Android ZoneManager: zones positioned by percent
  geometry with z_index/fit_mode/background, assignments grouped by zone_id
  (unassigned content goes to the first zone), each zone rotating independently with
  the same per-item schedule gating (#74/#75). app.js selects the renderer from
  payload.layout; single-zone playback is unchanged.

Video walls (web-player parity; Android has none)
- New WallController mirrors the web player: when payload.wall_config is present the
  stage is positioned (vw/vh) as this screen's slice of the wall. The leader plays
  normally and broadcasts wall:sync at 4Hz; followers hold the leader's item, align
  index, and lock their video to the leader's clock with a latency-compensated drift
  controller (hard-seek past 0.3s, gentle +/-3% playbackRate nudge past 0.05s), and
  request an immediate position on (re)connect via wall:sync-request. Per-tile
  rotation is not applied yet (matches the web player). Wall emits are gated on
  auth + connection so a pre-register tick can't trip device:auth-error.

Not ported: video-wall per-tile rotation, plus the minor Android-only reporting
events (device:playback-state, device:log) and the N/A offline-cache events
(device:content-ack/content-delete). None affect on-screen playback.

Verified: JS syntax + headless unit tests of zone grouping/geometry and wall
leader/follower + drift logic. NOT yet validated on Tizen hardware - multi-screen
video sync in particular needs a real wall to tune.
2026-06-17 18:47:51 -05:00
..
css Tizen player 1.9.1-beta3: bug fixes, multi-zone layouts, video walls 2026-06-17 18:47:51 -05:00
js Tizen player 1.9.1-beta3: bug fixes, multi-zone layouts, video walls 2026-06-17 18:47:51 -05:00
.gitignore feat(tizen): Samsung Tizen TV web player (.wgt) 2026-06-09 19:01:58 -05:00
build-wgt.sh Tizen player 1.9.1-beta3: bug fixes, multi-zone layouts, video walls 2026-06-17 18:47:51 -05:00
config.xml chore(release): v1.9.1-beta1 2026-06-12 22:37:50 -05:00
icon.png feat(tizen): Samsung Tizen TV web player (.wgt) 2026-06-09 19:01:58 -05:00
index.html feat(scheduling): per-item schedule blocks (#74 dayparting, #75 auto-expire) 2026-06-11 15:46:41 -05:00
README.md Tizen player 1.9.1-beta3: bug fixes, multi-zone layouts, video walls 2026-06-17 18:47:51 -05:00

ScreenTinker — Tizen TV Player (.wgt)

A Samsung Tizen TV / signage web port of the ScreenTinker player. It speaks the exact same /device socket.io protocol as the Android player, so a Tizen display pairs and plays from the same dashboard with no server changes.

What it does

  • Enter a server URL → connects to {server}/device (socket.io v4).
  • Registers, shows a 6-digit pairing code; you claim it in the dashboard (Devices → Pair a display). On device:paired it switches to playback.
  • Reconnects automatically with a stored device_id + device_token.
  • Renders multi-zone layouts (matching the Android player) when a layout is assigned — each zone has its own percent geometry, z_index, fit_mode, background, and rotates its own assignments independently — and falls back to fullscreen single-zone when no layout is set, looping:
    • image → shown for duration_sec (min 3s)
    • video (/api/content/{id}/file or remote_url) → plays to end, then next; single item loops
    • YouTube (mime video/youtube) → muted autoplay <iframe> embed
    • widget<iframe> of {server}/api/widgets/{id}/render
  • Sends device:heartbeat every 15s (with best-effort Tizen telemetry).
  • Keeps the screen awake (tizen.power / Samsung appcommon screensaver-off).
  • Video walls (mirrors the web player): when the device is a wall member the payload carries wall_config; the stage is positioned (in vw/vh) as this screen's slice of the wall, the leader broadcasts wall:sync and followers align index + drift-correct their video to the leader's clock. Per-tile rotation is not applied yet (matches the web player); video walls have no Android equivalent.

Files

config.xml          Tizen TV web-app manifest (privileges, profile, icon)
index.html          setup / pairing / stage screens
css/style.css
js/app.js           device protocol client (register, pair, heartbeat, state)
js/player.js        fullscreen playlist renderer
js/socket.io.min.js socket.io-client v4.7.5 (bundled)
icon.png
build-wgt.sh        package (signed if Tizen CLI present, else unsigned)

Build

./build-wgt.sh            # -> ScreenTinker.wgt

Without the Tizen CLI this is an unsigned .wgt.

Why the released .wgt is unsigned: Samsung distributor certificates are locked to the DUID of the signer's own TVs, so a .wgt we signed would not install on your TV anyway. Releases therefore ship it unsigned (for inspection only). To actually run it, use path A (no signing) or sign it yourself with your own certificate (path B).

Deploy — two paths

A) URL Launcher / TV browser (easiest, no signing)

No package, no Tizen Studio. Point the TV's URL Launcher (or just its web browser) at your server's built-in web player: https://<your-instance>/player. The TV runs it as a web app on boot, pairs with a 6-digit code, and plays - best for Samsung B2B signage (SSSP). (You can instead self-host this tizen/ folder and point the URL Launcher at …/index.html for the Tizen-specific build.)

B) Signed .wgt (installed app)

A signing profile is already set up on the build box (Tizen Studio CLI 6.1):

  • Profile ScreenTinker = a self-signed author cert (~/tizen-studio-data/keystore/author/st_author.p12) + the default Tizen distributor cert. ./build-wgt.sh auto-detects the CLI and signs with it, producing a .wgt with author-signature.xml + signature1.xml.
  • This installs on developer-mode Samsung TVs and the Tizen emulator — the right path for a self-hosted fleet you control (enable Developer Mode on each TV once: Apps → enter 12345 → set the host IP).

Install onto a dev-mode TV:

sdb connect <tv-ip>
tizen install -n ScreenTinker.wgt -t <tv-device>

Production / retail (no developer mode): re-sign with a Samsung Partner or Public distributor certificate from the Tizen Certificate Manager (free Samsung account; distributor cert tied to each TV's DUID), then ./build-wgt.sh <thatProfile>. The self-signed author cert is not committed (it lives in ~/tizen-studio-data, password screentinker).

Validated (2026-06-09)

  • Protocol: headless test against the live server passed end-to-end — register(pairing_code) → device:registered → pair → reconnect(device_id+token) → device:playlist-update(2 items) → GET /api/content/{id}/file = 200.
  • Runtime: loads + renders in Chromium with no JS errors (setup screen verified).
  • Not yet on real Tizen hardware — needs signing + a TV (or URL Launcher).

Remote control & preview (#120 / #121)

The Tizen player now listens for the same dashboard events as the web/Android player. What it can actually do depends on what a sideloaded web app is allowed to do on the TV runtime:

Command (device:command type) Tizen behaviour
refresh location.reload()
launch / screen_on clears the screen-off overlay + re-requests screen-awake
screen_off black full-screen overlay (content keeps running behind)
update toast: must re-install the .wgt (see Updates below)
reboot / shutdown MDM-only — not reachable from a sideloaded app (toast)
device:screenshot-request best-effort capture (see note)
device:remote-start / -stop start/stop ~1 fps preview streaming

Screenshot/preview note: the TV decodes <video> onto a hardware overlay plane and plays YouTube in a cross-origin <iframe>, neither of which can be read back into a <canvas>. So images capture for real; video/YouTube fall back to a status card (device + timestamp). The dashboard preview shows a truthful frame rather than a dead button. Full-fidelity video preview isn't feasible on the sideloaded Tizen runtime.

screen_off uses an overlay, not a real panel power-off — a sideloaded app has no clean panel-power API. On B2B/MDM (SSSP) firmware, true power and reboot/shutdown go through Samsung's device-management channel, not this app.

Updates (#122)

There is no in-app OTA for a sideloaded, signed .wgt. Updating a screen means re-building and re-sideloading the .wgt (path B above), or — on Samsung B2B signage — pushing it through the URL Launcher refresh / MDM (MagicINFO / SSSP) channel. The dashboard update command therefore just tells the screen an update is pending; it cannot self-apply. If you run the URL Launcher path (A), a plain TV reboot re-fetches …/player and you're current with the server with no .wgt step.

Auto-launch on boot (#122)

Boot auto-start for a sideloaded consumer TV web app is a display setting, not an app setting — there's no config.xml autostart for the TV profile. Configure it on the panel:

  • URL Launcher path (A): set the URL Launcher as the boot app (it relaunches on power-up automatically) — the recommended signage setup.
  • Signed-app path (B): use the TV's kiosk / auto-start app setting (B2B/SSSP firmware) to launch ScreenTinker on boot; on dev-mode consumer TVs there's no guaranteed boot-launch, so the URL Launcher path is preferred for unattended screens.

Version reporting (#119)

app_version is sourced from config.xml's version="" — read at runtime via the Tizen application API, with a build-stamped constant fallback (build-wgt.sh stamps it from config.xml). The dashboard always shows the version actually installed.