Skip to content

Latest commit

 

History

History
507 lines (371 loc) · 19.2 KB

File metadata and controls

507 lines (371 loc) · 19.2 KB

Xibo Electron Player — Developer Manual

A practical guide for developers setting up and working with the Xibo Electron Player and its companion layout renderer library.


Table of Contents

  1. Prerequisites
  2. Repository Setup
  3. Running in Development
  4. Building & Packaging
  5. Architecture Overview
  6. Process Deep-Dives
  7. Key Data Flows
  8. Configuration & Environment
  9. Logging & Debugging
  10. Common Tasks

1. Prerequisites

Tool Version Notes
Node.js 22 or newer Electron and better-sqlite3 both require Node >= 22
npm bundled with Node No Yarn/pnpm
Git any recent SSH key required (XLR is a git dependency)
Electron auto-installed Version pinned in package.json devDependencies
Python 3.6 or newer Required to compile better-sqlite3. See below

better-sqlite3 has no prebuilt binaries for the Electron versions this project targets, so it is compiled from source during npm run rebuild. That needs both a C++ compiler and Python, on every platform.

All platforms: Install Python 3. On Windows, use the installer from python.org with "Add python.exe to PATH" ticked; the Microsoft Store version does not work with node-gyp. After installing, restart your terminal or editor so it picks up the new PATH.

Windows only: Install Visual Studio Build Tools (C++ workload), required for better-sqlite3 native bindings.

Linux only: snapcraft is required if packaging Snap.


2. Repository Setup

This player depends on two repositories. Clone both into the same parent directory so the dev alias works correctly.

# From your preferred workspace root (e.g. ~/dev/xibo/)
git clone git@github.com:xibosignage/electron-player.git
git clone git@github.com:xibosignage/xibo-layout-renderer.git

Your directory structure should look like:

xibo/
├── electron-player/      ← this app
└── xibo-layout-renderer/ ← layout renderer library

Install dependencies

cd electron-player
npm install
npm run rebuild

Both steps are required. npm install also downloads the Electron binary via a postinstall hook, since Electron no longer fetches it during a plain install.

npm run rebuild compiles better-sqlite3 against the current Electron version. It takes a few minutes and prints compiler output, which is expected. Skipping it lets the app start and then fail when it first opens the database, so run it after every npm install and after every Electron or Node version change.

If it fails with Could not find any Python installation to use, check Prerequisites. If Python is installed and works in a fresh terminal but not in your editor, fully quit and reopen the editor: it passes its own copy of PATH to integrated terminals, and that copy is captured at launch.

Build the layout renderer (first time only)

The renderer library must be built before the player can use it in dev mode:

cd ../xibo-layout-renderer
npm install
npm run build   # generates dist/

You only need to redo this when you pull changes to xibo-layout-renderer.


3. Running in Development

cd electron-player
npm run dev

This starts electron-vite dev with:

  • HMR on the renderer process
  • Sourcemaps enabled
  • Remote debugging on port 9222 (attach Chrome DevTools at chrome://inspect)
  • Node inspector on the main process (--inspect src/main/index.ts)

In dev mode, the alias @xibosignage/xibo-layout-renderer resolves to ../xibo-layout-renderer (your local clone), so changes to the renderer library are picked up immediately after a rebuild.

First launch

On first run the player has no configuration. It will show the activation screen where you enter:

  • A CMS URL
  • A CMS security key

Or you can enter an activation code generated by the CMS.


4. Building & Packaging

Command What it does
npm run build Compiles all processes to dist/ via electron-vite
npm run start Previews the built output (no HMR)
npm run package Build + Electron Forge package (creates unpacked app)
npm run make Build + Electron Forge make (creates installer)
npm run make:snap Builds a Snap package (Linux, requires snapcraft)

Platform-specific make targets

Electron Forge is configured in forge.config.cjs with makers for:

  • Windows — Squirrel installer (.exe/.msi)
  • Linux — Debian package (.deb) + Snap
  • macOS — ZIP archive

In production builds, xibo-layout-renderer is bundled into the app (not aliased to the local clone).


5. Architecture Overview

The application is a standard three-process Electron app augmented by a separate renderer library.

┌─────────────────────────────────────────────────────────────┐
│                     electron-player                          │
│                                                             │
│  ┌──────────────────┐        ┌─────────────────────────┐   │
│  │   Main Process   │  IPC   │   Renderer Process       │   │
│  │  (Node.js)       │◄──────►│   (Chromium / browser)  │   │
│  │                  │        │                          │   │
│  │  - XMDS client   │        │  - XiboLayoutRenderer   │   │
│  │  - File manager  │        │    (XLR library)         │   │
│  │  - Schedule mgr  │        │  - ConfigHandler UI      │   │
│  │  - Express server│        │  - Stats / Faults UI     │   │
│  │  - SQLite stats  │        │                          │   │
│  └──────────────────┘        └─────────────────────────┘   │
│           ▲                            ▲                     │
│           │                           │ HTTP                 │
│     ┌─────┴──────┐             ┌──────┴──────┐              │
│     │  Preload   │             │   Express   │              │
│     │  (bridge)  │             │  port 9696  │              │
│     └────────────┘             └─────────────┘              │
│                                      ▲                      │
│                                      │ serves               │
│                              ~/Documents/xibo_library/      │
└─────────────────────────────────────────────────────────────┘
                  ▲
                  │ SOAP (XMDS)
             Xibo CMS

The three source directories

Directory Electron process Runtime
src/main/ Main Node.js
src/preload/ Preload Sandboxed Node subset
src/renderer/ Renderer Chromium (browser APIs)
src/shared/ Used by both main and renderer

6. Process Deep-Dives

6.1 Main Process (src/main/)

The main process is the backend of the application. Its responsibilities:

index.ts — orchestrator

  • Creates the BrowserWindow and loads the renderer
  • Initialises Config, ConsoleDB, Faults, and State singletons
  • Manages the XMDS polling loop (register → get required files → schedule → collect stats)
  • Listens for IPC events from the renderer and forwards commands back

config/config.ts — persistent configuration

  • Reads/writes config.json and cms_config.json from Electron's userData directory
  • Generates a stable hardwareKey from the machine ID (max 40 chars)
  • Assigns a random UUID as the xmrChannel on first run

common/scheduleManager.ts — schedule orchestration

  • Polls the CMS schedule
  • Determines which layout(s) to play, including overlay layouts
  • Sends update-loop and update-overlays IPC events to the renderer

common/fileManager.ts — asset management

  • Downloads required media files from the CMS
  • Stores them in ~/Documents/xibo_library/
  • Tracks download state to avoid re-fetching unchanged files

common/stats/ — proof-of-play

  • StatsDB.ts uses better-sqlite3 to persist play statistics locally
  • PoPStats.ts batches and submits stats to the CMS via XMDS

express.ts — local file server

  • Runs an Express HTTP server on port 9696
  • Serves media files from the library directory to the renderer via http://localhost:9696/files/
  • The renderer cannot access the local filesystem directly, so this bridge is necessary

xmds/xmds.ts — CMS communication

  • SOAP client for the XMDS protocol (Xibo's proprietary CMS API)
  • Handles: RegisterDisplay, RequiredFiles, Schedule, SubmitLog, SubmitStats

6.2 Preload Script (src/preload/index.ts)

The preload runs in a privileged context with access to both Node.js APIs and the DOM. It exposes a safe, typed API to the renderer via contextBridge.

Two objects are exposed on window:

window.apiHandler   // renderer → main (config, XMDS triggers)
window.playerAPI    // main ↔ renderer (layout lifecycle, state updates)

The preload also installs an extended console that pipes console.log/warn/error calls over IPC so they are captured by the main process logger.


6.3 Renderer Process (src/renderer/)

The renderer is a plain HTML/JS page running inside the Electron BrowserWindow. No framework (React, Vue, etc.) — it uses the XLR library directly.

renderer.ts — entry point

  1. Initialises XiboLayoutRenderer with current schedule and overlay data
  2. Calls xlr.bootstrap() to insert the XLR container into the DOM
  3. Registers event handlers for layout lifecycle events:
    • layoutStart / layoutEnd → report stats via BroadcastChannel
    • commandCodeReceived → forward command to main process
    • overlayEnd → request next overlay from main process
  4. Subscribes to playerAPI events pushed from the main process:
    • onUpdateLoop → calls xlr.updateLoop(newLayouts)
    • onUpdateOverlays → calls xlr.updateOverlays(overlays)
    • onStateChange → updates displayed state info

ConfigHandler.ts — activation UI

  • Shown when the player is not yet configured
  • Supports both activation-code flow and manual CMS URL + key entry
  • Calls window.apiHandler.xmdsTryRegister() to register with the CMS

6.4 Xibo Layout Renderer (xibo-layout-renderer)

This is a separate library consumed by the renderer process. It handles all the visual playback logic — the electron-player is responsible for delivering schedules and assets; XLR renders them.

XiboLayoutRenderer(layouts, overlays, options)
        │
        ▼
   xlr.bootstrap()    ← inserts DOM skeleton, shows splash screen
        │
   xlr.init()         ← parses layout XLF XML, creates Layout objects
        │
   xlr.playSchedules()← makes the first layout visible
        │
   ┌────▼──────┐
   │  Layout   │  ← manages a single layout's DOM and timing
   │  ├─Region │  ← a rectangle on screen, plays a media playlist
   │  │  └─Media  ← image / video / audio / html widget
   │  └─Region │
   └───────────┘

Layout lifecycle states: IDLE → RUNNING → PLAYED → CANCELLED | ERROR

Key events emitted by XLR (subscribe via xlr.on(event, handler)):

Event When
layoutStart A layout begins playing
layoutEnd A layout finishes all regions
widgetStart / widgetEnd An individual media item starts/stops
updateLoop XLR requests a refreshed schedule
overlayEnd An overlay layout has finished
commandCodeReceived A command widget fired

Platform mode: the player sets platform: ConsumerPlatform.ELECTRON in the XLR options. This enables electron-specific behaviour (fault reporting, local file URLs, etc.).


7. Key Data Flows

7.1 Initial startup & registration

Main: index.ts
  → loads config.json
  → if not configured → sends 'configure' IPC → renderer shows ConfigHandler
  → if configured     → starts XMDS registration loop
      → xmds.RegisterDisplay()
      → xmds.RequiredFiles() → fileManager downloads assets
      → xmds.Schedule()     → scheduleManager parses schedule
      → sends 'update-loop' IPC to renderer

7.2 Schedule → playback

Main: scheduleManager
  → parses schedule XML into layout objects
  → sends IPC 'update-loop' with array of { layoutId, file, ... }

Renderer: renderer.ts
  → receives onUpdateLoop(layouts)
  → calls xlr.updateLoop(layouts)

XLR: xibo-layout-renderer.ts
  → fetches layout XLF from http://localhost:9696/files/<layoutId>.xlf
  → parses XML → creates Layout → Region → Media objects
  → plays current layout, queues next
  → emits 'layoutStart', 'layoutEnd' events back to renderer

7.3 Proof-of-play stats

XLR emits layoutStart / widgetStart
  → renderer.ts catches event
  → sends stats message via BroadcastChannel ('xibo-stats')

Main process stats listener
  → receives BroadcastChannel message via IPC
  → writes to SQLite via StatsDB
  → batches and submits to CMS via xmds.SubmitStats()

8. Configuration & Environment

Versioning

package.json is the single source of truth for the app version. Two fields are defined there:

{
  "version": "4.0.3",
  "versionCode": 403
}

electron.vite.config.js reads these at build time via readFileSync and injects them as compile-time constants available to all processes:

Constant Type Value example
__APP_VERSION__ string "4.0.3"
__APP_VERSION_CODE__ number 403

These constants replace any runtime variable lookups — no .env files are needed for versioning.

snap/snapcraft.yaml is also kept in sync: scripts/set-snap-version.cjs runs automatically as part of make:snap and writes the version field from package.json into the YAML before snapcraft pack is invoked.

To bump the version, edit only package.json.

Runtime config files (userData directory)

File Contents
config.json Player settings: CMS URL, key, hardware ID, display name, paths
cms_config.json CMS-pushed display settings (resolution, timezone, etc.)

Library directory

Media assets are downloaded to:

~/Documents/xibo_library/

The Express server at http://localhost:9696/files/ serves everything in this directory.

TypeScript paths

Alias Resolves to
@renderer/* src/renderer/src/*
@shared/* src/shared/*

9. Logging & Debugging

Remote DevTools

Attach Chrome DevTools to the renderer process:

  1. Open chrome://inspect in any Chromium browser
  2. Click Configure and add localhost:9222
  3. The player's renderer window will appear under "Remote Target"

The main process Node inspector is also available on the default port — attach with VS Code's Node debugger or chrome://inspect.

In-app console

The preload overrides console.* methods to forward all log output over IPC to the main process. Logs are persisted to ConsoleDB (SQLite) and can be viewed via the player's status window.

Fault reporting

Faults.ts tracks and persists faults. The renderer initiates fault collection via playerAPI.initFaults() and reports faults back via BroadcastChannel (xibo-faults).

Useful IPC event names

Channel Direction Meaning
configure main → renderer Show configuration screen
state-change main → renderer Player state updated
update-loop main → renderer New layout schedule
update-overlays main → renderer New overlay list
showStatusWindow main → renderer Show status overlay
open-child-window renderer → main Open a secondary window
report-fault renderer → main Fault detected

10. Common Tasks

Modifying layout rendering logic

Edit files in ../xibo-layout-renderer/src/, then:

cd ../xibo-layout-renderer && npm run build

The dev server will pick up the rebuilt output automatically (the alias points to dist/). Restart npm run dev if HMR doesn't catch the change.

Adding a new IPC event

  1. Define the handler type in src/shared/types.ts (add to PlayerAPI or ApiHandler)
  2. Register the ipcMain.handle or ipcMain.on in src/main/index.ts
  3. Expose it via contextBridge.exposeInMainWorld in src/preload/index.ts
  4. Consume window.playerAPI.* or window.apiHandler.* in src/renderer/

Adding a new XMDS operation

  1. Create a response parser in src/main/xmds/response/
  2. Add the call in src/main/xmds/xmds.ts
  3. Wire it into the polling loop in src/main/index.ts

Changing the file server port

The Express server port is hardcoded to 9696. To change it, update the port constant in src/main/express.ts and the appHost option passed to XiboLayoutRenderer in src/renderer/src/renderer.ts.

Running a release build locally

npm run build  # compiles to dist/
npm run start  # launches the compiled app

Creating a distributable

npm run make   # produces installer in out/make/

Windows builds must be made on Windows; Linux builds on Linux.

Upgrading Electron

Electron only supports the latest three major versions, so this needs doing periodically. Check the release schedule for what is still supported.

npm install electron@<major>   # bump the version
npm install                    # required: see below
npm run rebuild                # recompile better-sqlite3 for the new version
npm run build

The bare npm install on the second line is not redundant. npm skips the project's postinstall hook when you install a named package, so npm install electron@<major> swaps the package without downloading the new binary. The build then fails with [vite:bytecode] Electron uninstall, which gives no hint about the cause. A plain npm install afterwards triggers the hook. Running npx install-electron --no directly has the same effect.

Two other things may need attention when moving to a new major:

  • node-abi, overridden in package.json, maps Electron versions to build identifiers. If npm run rebuild fails with Could not detect abi for version <x> and runtime electron, raise that override to a version that lists the new release.
  • better-sqlite3 must support the V8 version Electron ships. If the rebuild fails with C++ errors referencing v8::, the library needs upgrading rather than the build fixing.