screentinker/server/routes
screentinker 6f0e4a07f6
Fix per-item mute (#129): persist, ship to device, and toggle in real time (#130)
* fix(server): persist + ship + real-time per-item mute (#129)

The dashboard mute toggle was a no-op end to end. The active model is playlist_items
(the device payload is its published_snapshot); the legacy `assignments` table the bug
report cited is unused for devices. Three breaks:

- PUT /api/assignments/:id silently dropped `muted` (only read sort_order/duration_sec/
  zone_id). It now accepts muted (coerced 0/1) and ITEM_SELECT returns it, so the toggle
  persists and its on/off state sticks.
- playlist_items had no `muted` column — added (schema + idempotent migration).
- buildSnapshotItems didn't select muted, so it never reached the published_snapshot /
  device payload — now included.

Real-time: on a mute change, emit device:mute-changed { content_id, widget_id, muted } to
every device on that playlist so the player toggles the matching item's volume live,
decoupled from publish (the value is also in the next snapshot, so it persists). Adds a
[mute] log line (the report noted zero mute log entries).

Test: test/mute.test.js — PUT persists + returns muted, it reaches the published
snapshot, and a non-mute update doesn't reset it. Server suite 164/164.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(player): apply per-item mute live on Android + web (#129)

Honor the new per-item mute from the server, both in real time and on reload.

Android:
- WebSocketService: onMuteChanged callback + main-thread device:mute-changed handler.
- MediaPlayerManager.setVideoMuted(): flips the live ExoPlayer volume on the current
  video (YouTube autoplays muted; images/widgets are silent).
- MainActivity: on device:mute-changed, apply immediately if the toggled item is the
  one playing now.
- PlaylistController.sig(): include muted so a published mute change re-renders/persists
  instead of being de-duped.

Web player (server/player/index.html):
- device:mute-changed handler toggles the current <video>; the video mount now also
  honors item.muted so a published mute sticks across reloads.

Tizen intentionally not included: its player mutes ALL video for autoplay, so per-item
unmute isn't achievable there.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 16:54:23 -05:00
..
activity.js Phase 2.1: tenancy middleware, permission helpers, JWT workspace context, frontend + backend role-rename compat 2026-05-11 20:02:00 -05:00
admin.js feat(admin): Delete Organization + Workspace with cascade (#36) 2026-06-09 09:22:21 -05:00
agency.js feat: full-screen-only guardrail for agency designations (#73) 2026-06-14 17:36:30 -05:00
ai.js feat(ai): separate optional image API key (#41) 2026-06-09 13:47:47 -05:00
assignments.js Fix per-item mute (#129): persist, ship to device, and toggle in real time (#130) 2026-06-18 16:54:23 -05:00
auth.js feat(config): HIDE_BILLING flag to hide the Subscription/billing UI (#116) 2026-06-16 09:19:24 -05:00
contact.js fix(landing): replace broken Custom pricing card with enterprise contact form 2026-05-14 13:52:24 -05:00
content.js refactor(content): extract the upload ingest into a shared lib (#73) 2026-06-13 22:48:42 -05:00
device-groups.js feat(api): scoped API token foundation + secure-by-exclusion mounts 2026-06-12 18:45:09 -05:00
devices.js Merge #111: device-free preview, playlist + device surfaces (#104) 2026-06-15 15:20:57 -05:00
folders.js Phase 2.2c: content_folders gets workspace_id (schema + backfill); folders.js scoped; content.js folder-move strict same-workspace 2026-05-11 21:04:03 -05:00
kiosk.js fix(widgets): no-store on widget/kiosk render 2026-06-08 23:46:42 -05:00
layouts.js fix(layouts): atomic zone save (stop template zone duplication) 2026-06-09 10:16:01 -05:00
pip.js PiP overlay MVP: push image/web overlays to a device or group (#109) (#127) 2026-06-18 14:54:44 -05:00
player-debug.js Add player debug overlay and server-side error telemetry sink 2026-05-15 15:20:42 -05:00
playlists.js Fix per-item mute (#129): persist, ship to device, and toggle in real time (#130) 2026-06-18 16:54:23 -05:00
provisioning.js fix(api): consolidate device pairing to /pair, remove vestigial bare endpoint (#90) 2026-06-12 20:13:16 -05:00
reports.js Phase 2.2g: reports.js scoped to workspace_id; fixes pre-existing /export and /uptime cross-tenant leaks 2026-05-11 21:36:54 -05:00
schedules.js Phase 2.2m: schedules.js scoped to workspace_id; schedule.workspace_id inherited from target (device/group); fixes 6 pre-existing cross-tenant leaks (POST content/widget/layout/playlist accepted with no check, PUT verifyOwnership rewrite across all 6 polymorphic targets) 2026-05-11 23:03:54 -05:00
status.js chore(version): single-source VERSION, env-configurable data paths, bump tooling 2026-06-10 12:56:03 -05:00
stripe.js Security audit remediation: auth, IDOR, XSS, hardening 2026-04-11 22:48:07 -05:00
subscription.js Initial open source release 2026-04-08 12:14:53 -05:00
teams.js feat(teams): temporarily disable Teams API while feature is redesigned 2026-05-12 13:30:55 -05:00
tokens.js feat: full-screen-only guardrail for agency designations (#73) 2026-06-14 17:36:30 -05:00
video-walls.js feat(socket): delivery queue for offline-device emits 2026-05-14 13:06:43 -05:00
white-label.js fix(security): patch quick-win findings from the codebase review 2026-06-08 19:02:19 -05:00
widgets.js fix(widgets): no-store on widget/kiosk render 2026-06-08 23:46:42 -05:00
workspaces.js fix(workspaces): use APP_URL env var for invite-accept URL generation 2026-05-17 15:26:07 -05:00