# xiboplayer PWA

Lightweight PWA Xibo digital signage player built on the [`@xiboplayer` SDK](https://github.com/xibo-players/xiboplayer). Part of the SDK monorepo at `packages/pwa/`.

## CMS Communication

- **REST API** (primary) — lighter JSON transport with ETag caching (~30% smaller payloads)
- **XMDS SOAP** (fallback) — standard Xibo player protocol when REST is unavailable

## Features

- **Offline-first** — Service Worker caching with parallel chunk downloads and progressive streaming
- **XLF layout rendering** — video (MP4/HLS), images (scale/align), audio overlay, PDF, text/ticker, web pages via RendererLite
- **Campaign scheduling** — priority-based campaigns, dayparting, interrupts, and overlays
- **Playback control** — skip to next/previous layout via keyboard or click a layout in the timeline overlay
- **Conflict indicators** — timeline overlay highlights overlapping schedule entries with accurate per-layout durations
- **Multi-display sync** — BroadcastChannel-based lead/follower synchronized playback for video walls, with cross-tab stats/logs delegation so followers submit proof-of-play through the sync lead
- **Real-time CMS commands** — collectNow, screenshot, changeLayout, overlayLayout via XMR WebSocket
- **SDK event wiring** — widget duration events, scheduled commands, event-based proof of play
- **Proof of play** — per-layout and per-widget duration tracking with stats reporting
- **Log batching** — aggregated log submission aligned with CMS spec
- **Screenshots** — html2canvas-based capture for non-Electron browsers, Electron uses `capturePage()`
- **Screen Wake Lock** — prevents display from sleeping during playback
- **Configurable log levels** — `DEBUG`, `INFO`, `WARNING`, `ERROR`, `NONE` (via URL param or CMS settings; log levels only affect verbosity, not overlays)

## Keyboard Shortcuts

All keyboard shortcuts and mouse hover are **disabled by default** for secure kiosk operation. Enable them via `controls` in the host player's `config.json` (Electron or Chromium). The config is injected into localStorage by `@xiboplayer/proxy`.

| Key | Group | Action |
|-----|-------|--------|
| `D` | `debugOverlays` | Toggle download progress overlay |
| `T` | `debugOverlays` | Toggle timeline overlay (click-to-skip supported) |
| `S` | `setupKey` | Toggle CMS setup screen |
| `V` | `videoControls` | Toggle native `<video>` controls |
| `→` / `PageDown` | `playbackControl` | Skip to next layout |
| `←` / `PageUp` | `playbackControl` | Skip to previous layout |
| `Space` | `playbackControl` | Pause / resume playback |
| `R` | `playbackControl` | Revert to scheduled layout |
| Media keys | `playbackControl` | Next/prev/pause/play (MediaSession API) |

## Auto-authorize via CMS API (optional)

By default, new displays must be manually authorized by a CMS administrator. To skip this step and have the player authorize itself automatically, provide OAuth2 API credentials — either via the setup page or via `config.json` provisioning in the Electron/Chromium shells.

### How it works

After the player registers with the CMS (via XMDS `RegisterDisplay`), it uses the CMS REST API with an OAuth2 `client_credentials` flow to:

1. Obtain an access token from `/api/authorize/access_token`
2. Find the display by hardware key via `GET /api/display?hardwareKey=...`
3. Authorize the display via `PUT /api/display/{id}/authorise`

If auto-authorize fails (wrong credentials, missing scope, CMS unreachable), the player silently falls back to manual authorization — the CMS administrator sees the display as "Awaiting approval".

### CMS setup

1. In the CMS, go to **Administration > Applications**
2. Click **Add Application**
3. Set **Grant Type** to `client_credentials`
4. **Important:** on the **Scopes** tab (2nd tab), enable the **`displays`** scope — this is required for the player to find and authorize itself
5. Save and copy the **Client ID** and **Client Secret**

Without the `displays` scope enabled, the API will return `403 Forbidden` and auto-authorize will not work.

### Interactive setup

In the setup page, expand "Auto-authorize via API (optional)" and enter the Client ID and Client Secret. These are saved to localStorage alongside the CMS configuration.

### Provisioning via config.json

When using the Electron or Chromium shells, add the credentials to `config.json`:

```json
{
  "cmsUrl": "https://your-cms.example.com",
  "cmsKey": "your-cms-key",
  "displayName": "Lobby Display",
  "apiClientId": "your-client-id",
  "apiClientSecret": "your-client-secret"
}
```

## Debug Overlays

Three toggleable overlays provide real-time insight into player operation without leaving the playback screen. All are disabled by default — enable `controls.keyboard.debugOverlays` in the host player's `config.json`, then press the corresponding key to toggle. Overlays are independent of log level — `DEBUG` logging does not enable overlays.

### Timeline Overlay (`T`)

Shows the upcoming schedule as a scrollable list (up to 8 entries visible):

- **Current layout** highlighted with a `▶` marker and blue left border
- **Time range** and **duration** for each entry (e.g. `19:25–19:31  #362  6m 15s`)
- **`[def]`** tag on default/fallback layouts (no campaign scheduled)
- **`OFFLINE`** badge when the player has lost CMS connectivity
- **Conflict indicators** — a `+N` badge appears next to a layout when N other layouts were scheduled for the same time slot but suppressed by a higher-priority campaign. Hover over it to see which layouts were hidden and their priorities (e.g. `Also scheduled: #366 (p0), #362 (p0)`)
- **Click-to-skip** — click any future layout to jump to it immediately. The player enters override mode (the schedule won't auto-advance). Press `R` to return to normal schedule playback
- **Remaining duration** — the currently playing layout shows time remaining rather than full duration, so the predicted end time is always accurate

### Download Overlay (`D`)

Shows real-time media download progress:

- **Active downloads** with progress bars, speed, and chunk status
- **Queue depth** — how many files are waiting to download
- **Idle status** when all media is cached and up to date
- Auto-hides when all downloads complete (if auto-hide is enabled)

Useful during initial deployment or after a `purgeAll` command to monitor how quickly content is being cached.

### Video Controls (`V`)

Toggles native browser `<video>` controls on all video elements currently in the DOM. Shows play/pause, seek bar, volume, and fullscreen buttons on each video widget — helpful for debugging video playback issues, checking codec info, or manually seeking within a video.

## Service Worker Architecture

The Service Worker (`sw-pwa.js`) provides:

- **Progressive streaming** — large media served via chunk streaming with Range request support
- **BlobCache** — in-memory assembled chunks with LRU eviction for fast video seeking
- **XLF-driven media resolution** — parses layout XLF to download exactly the media each layout needs, including data widget IDs (media tags without fileId) and unclaimed media files
- **Server-enriched widget data** — RSS/dataset widget data paths are provided by the CMS in enriched RequiredFiles responses, downloaded alongside regular media rather than fetched client-side
- **Layout-ordered downloads** — media queued with barriers so the currently-playing layout downloads first
- **Version-aware activation** — same-version SW update skips activation to preserve in-flight video streams
- **Adaptive chunk sizing** — adjusts chunk size and concurrency based on device RAM (4GB/8GB+ tiers)

## Installation

The PWA can be served from:

1. **CMS origin** — deploy `dist/` to a path under the CMS web server (e.g. `https://your-cms.example.com/player/pwa/`) to avoid CORS
2. **Standalone with proxy** — use `@xiboplayer/proxy` to serve the PWA and proxy CMS requests (used by Electron and Chromium shells)

## Development

### Prerequisites

- Node.js 20+
- pnpm 10+

### Setup

```bash
pnpm install
```

### Build

```bash
pnpm run build
```

### Dev server

```bash
pnpm run dev
```

### Link SDK for local development

```bash
pnpm link ../xiboplayer/packages/{utils,cache,renderer,schedule,xmds,xmr,core,stats,settings}
```

## Testing

### Electron (recommended for development)

```bash
cd ../xiboplayer-electron && npx electron . --dev --no-kiosk
```

### Chromium kiosk

```bash
cd ../xiboplayer-chromium && ./xiboplayer/launch-kiosk.sh --no-kiosk
```

### Playwright E2E

Playwright tests in `playwright-tests/` run against a live CMS with scheduled layouts — not for CI.

```bash
PWA_URL=https://your-cms.example.com/player/pwa/ npx playwright test
```

## License

Apache-2.0
