@xiboplayer/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CAMPAIGNS.md +254 -0
- package/README.md +163 -0
- package/TESTING_STATUS.md +281 -0
- package/TEST_STANDARDIZATION_COMPLETE.md +287 -0
- package/docs/ARCHITECTURE.md +714 -0
- package/docs/README.md +92 -0
- package/examples/dayparting-schedule-example.json +190 -0
- package/index.html +262 -0
- package/package.json +53 -0
- package/proxy.js +72 -0
- package/public/manifest.json +22 -0
- package/public/sw.js +218 -0
- package/setup.html +220 -0
- package/src/data-connectors.js +198 -0
- package/src/index.js +4 -0
- package/src/main.js +580 -0
- package/src/player-core.js +1120 -0
- package/src/player-core.test.js +1796 -0
- package/src/state.js +54 -0
- package/src/state.test.js +206 -0
- package/src/test-utils.js +217 -0
- package/src/xmds-test.html +109 -0
- package/src/xmds.test.js +516 -0
- package/vite.config.js +51 -0
- package/vitest.config.js +35 -0
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
# Architecture Documentation
|
|
2
|
+
|
|
3
|
+
Technical architecture and design decisions for the Xibo Player multi-platform implementation.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Overview](#overview)
|
|
8
|
+
- [Core Architecture](#core-architecture)
|
|
9
|
+
- [Platform Wrappers](#platform-wrappers)
|
|
10
|
+
- [Communication Protocols](#communication-protocols)
|
|
11
|
+
- [Data Flow](#data-flow)
|
|
12
|
+
- [Security Considerations](#security-considerations)
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
The Xibo Player is a free, open-source implementation of a Xibo-compatible digital signage player with full compatibility with Xibo CMS.
|
|
17
|
+
|
|
18
|
+
###Key Design Principles
|
|
19
|
+
|
|
20
|
+
1. **Multi-Platform**: Single PWA core wrapped for all platforms
|
|
21
|
+
3. **Offline-First**: Service Worker caching for reliability
|
|
22
|
+
4. **Real-Time Capable**: XMR WebSocket support for instant updates
|
|
23
|
+
5. **Lightweight**: Minimal dependencies, small bundle size
|
|
24
|
+
|
|
25
|
+
### Architecture Diagram
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
29
|
+
│ Xibo CMS │
|
|
30
|
+
│ ┌─────────────┐ ┌─────────────┐ ┌────────────────┐ │
|
|
31
|
+
│ │ XMDS │ │ XMR │ │ Media Library │ │
|
|
32
|
+
│ │(SOAP/HTTP) │ │ (WebSocket) │ │ (HTTP) │ │
|
|
33
|
+
│ └──────┬──────┘ └──────┬──────┘ └────────┬───────┘ │
|
|
34
|
+
└─────────┼─────────────────┼──────────────────┼─────────────┘
|
|
35
|
+
│ │ │
|
|
36
|
+
│ SOAP XML │ WS JSON │ HTTP
|
|
37
|
+
▼ ▼ ▼
|
|
38
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
39
|
+
│ PWA Core (packages/core/) │
|
|
40
|
+
│ ┌───────────┐ ┌──────────┐ ┌────────┐ ┌──────────┐ │
|
|
41
|
+
│ │ xmds.js │ │xmr-wrap │ │ cache │ │ layout │ │
|
|
42
|
+
│ │ │ │per.js │ │ .js │ │ .js │ │
|
|
43
|
+
│ │clientType │ │ │ │ │ │ │ │
|
|
44
|
+
│ │='linux' │ │XCF lib │ │SW+IDB │ │XLF→HTML │ │
|
|
45
|
+
│ └───────────┘ └──────────┘ └────────┘ └──────────┘ │
|
|
46
|
+
│ ┌───────────────────────────────────────────────────┐ │
|
|
47
|
+
│ │ PDF.js (pdfjs-dist) │ │
|
|
48
|
+
│ └───────────────────────────────────────────────────┘ │
|
|
49
|
+
└─────────────────┬───────────────────────────────────────────┘
|
|
50
|
+
│
|
|
51
|
+
┌─────────┼─────────┬──────────┬─────────────┐
|
|
52
|
+
▼ ▼ ▼ ▼ ▼
|
|
53
|
+
┌───────┐ ┌───────┐ ┌────────┐ ┌────────┐ ┌────────┐
|
|
54
|
+
│Browser│ │Chrome │ │Electron│ │Android │ │ webOS │
|
|
55
|
+
│ PWA │ │ Ext │ │Desktop │ │WebView │ │Cordova │
|
|
56
|
+
└───────┘ └───────┘ └────────┘ └────────┘ └────────┘
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Core Architecture
|
|
60
|
+
|
|
61
|
+
The PWA core is built with vanilla JavaScript (ES modules) and minimal dependencies.
|
|
62
|
+
|
|
63
|
+
### File Structure
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
packages/core/
|
|
67
|
+
├── src/
|
|
68
|
+
│ ├── main.js # Player orchestrator
|
|
69
|
+
│ ├── xmds.js # SOAP client
|
|
70
|
+
│ ├── xmr-wrapper.js # XMR WebSocket client
|
|
71
|
+
│ ├── cache.js # Cache & download manager
|
|
72
|
+
│ ├── schedule.js # Schedule interpreter
|
|
73
|
+
│ ├── layout.js # XLF → HTML translator
|
|
74
|
+
│ └── config.js # Configuration management
|
|
75
|
+
├── public/
|
|
76
|
+
│ ├── manifest.json # PWA manifest
|
|
77
|
+
│ └── sw.js # Service Worker
|
|
78
|
+
├── index.html # Player page
|
|
79
|
+
├── setup.html # Configuration page
|
|
80
|
+
└── vite.config.js # Build configuration
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Dependencies
|
|
84
|
+
|
|
85
|
+
**Production dependencies:**
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"@xibosignage/xibo-communication-framework": "^0.0.6", // XMR
|
|
89
|
+
"pdfjs-dist": "^4.10.38", // PDF rendering
|
|
90
|
+
"spark-md5": "^3.0.2" // File checksums
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Dev dependencies:**
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"vite": "^7.3.1" // Build tool
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Component Breakdown
|
|
102
|
+
|
|
103
|
+
#### main.js - Player Orchestrator
|
|
104
|
+
|
|
105
|
+
**Responsibilities:**
|
|
106
|
+
- Initialize all subsystems
|
|
107
|
+
- Run collection cycles (XMDS sync)
|
|
108
|
+
- Check and apply schedule
|
|
109
|
+
- Display layouts
|
|
110
|
+
- Handle XMR commands
|
|
111
|
+
|
|
112
|
+
**Key methods:**
|
|
113
|
+
```javascript
|
|
114
|
+
class Player {
|
|
115
|
+
async init() // Initialize player
|
|
116
|
+
async collect() // XMDS collection cycle
|
|
117
|
+
async checkSchedule() // Apply current schedule
|
|
118
|
+
async showLayout() // Display layout
|
|
119
|
+
async initializeXmr() // Setup XMR
|
|
120
|
+
async captureScreenshot() // Screenshot (XMR command)
|
|
121
|
+
async changeLayout() // Change layout (XMR command)
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### xmds.js - SOAP Client
|
|
126
|
+
|
|
127
|
+
**Responsibilities:**
|
|
128
|
+
- Communicate with Xibo CMS via SOAP
|
|
129
|
+
- Register display with CMS
|
|
130
|
+
- Get required files list
|
|
131
|
+
- Get schedule
|
|
132
|
+
- Report status
|
|
133
|
+
|
|
134
|
+
**Key methods:**
|
|
135
|
+
```javascript
|
|
136
|
+
class XmdsClient {
|
|
137
|
+
async call(method, params) // Generic SOAP call
|
|
138
|
+
async registerDisplay() // Register (clientType='linux')
|
|
139
|
+
async requiredFiles() // Get files to download
|
|
140
|
+
async schedule() // Get schedule
|
|
141
|
+
async notifyStatus(status) // Report player status
|
|
142
|
+
async submitScreenshot(blob) // Upload screenshot
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**SOAP Envelope Structure:**
|
|
147
|
+
```xml
|
|
148
|
+
<soap:Envelope xmlns:xsi="..." xmlns:xsd="..." xmlns:soap="...">
|
|
149
|
+
<soap:Body>
|
|
150
|
+
<RegisterDisplay xmlns="...">
|
|
151
|
+
<serverKey>isiSdUCy</serverKey>
|
|
152
|
+
<hardwareKey>abc123</hardwareKey>
|
|
153
|
+
<displayName>test-display</displayName>
|
|
154
|
+
<clientType>linux</clientType> <clientVersion>0.1.0</clientVersion>
|
|
155
|
+
<clientCode>1</clientCode>
|
|
156
|
+
<operatingSystem>Linux</operatingSystem>
|
|
157
|
+
<macAddress>n/a</macAddress>
|
|
158
|
+
</RegisterDisplay>
|
|
159
|
+
</soap:Body>
|
|
160
|
+
</soap:Envelope>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### xmr-wrapper.js - Real-Time Messaging
|
|
164
|
+
|
|
165
|
+
**Responsibilities:**
|
|
166
|
+
- WebSocket connection to CMS
|
|
167
|
+
- Handle real-time commands
|
|
168
|
+
- Graceful fallback if unavailable
|
|
169
|
+
|
|
170
|
+
**Supported commands:**
|
|
171
|
+
- `collectNow`: Trigger immediate collection
|
|
172
|
+
- `screenShot`: Capture and upload screenshot
|
|
173
|
+
- `changeLayout`: Switch to specific layout
|
|
174
|
+
- `licenceCheck`: Acknowledges license check request
|
|
175
|
+
|
|
176
|
+
**Connection flow:**
|
|
177
|
+
```javascript
|
|
178
|
+
1. Player calls initializeXmr() after RegisterDisplay
|
|
179
|
+
2. XMR connects via WebSocket: wss://cms/xmr
|
|
180
|
+
3. Subscribes to channel: player-{hardwareKey}
|
|
181
|
+
4. Listens for commands from CMS
|
|
182
|
+
5. Executes commands on player
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### cache.js - Download Manager
|
|
186
|
+
|
|
187
|
+
**Responsibilities:**
|
|
188
|
+
- Download media files from CMS
|
|
189
|
+
- Verify checksums (MD5)
|
|
190
|
+
- Store in Cache API
|
|
191
|
+
- Manage cache size
|
|
192
|
+
|
|
193
|
+
**Cache structure:**
|
|
194
|
+
```
|
|
195
|
+
Cache: xibo-media-v1
|
|
196
|
+
├── /cache/media/image123.jpg
|
|
197
|
+
├── /cache/media/video456.mp4
|
|
198
|
+
├── /cache/media/document789.pdf
|
|
199
|
+
└── /cache/layout-html/42
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### schedule.js - Schedule Interpreter
|
|
203
|
+
|
|
204
|
+
**Responsibilities:**
|
|
205
|
+
- Parse schedule XML from CMS
|
|
206
|
+
- Determine which layout(s) to show
|
|
207
|
+
- Handle default layout
|
|
208
|
+
- Check schedule every minute
|
|
209
|
+
|
|
210
|
+
**Schedule XML:**
|
|
211
|
+
```xml
|
|
212
|
+
<schedule>
|
|
213
|
+
<default file="1.xlf"/>
|
|
214
|
+
<layout file="42.xlf" fromdt="2026-01-29 08:00:00" todt="2026-01-29 18:00:00"/>
|
|
215
|
+
<layout file="99.xlf" fromdt="2026-01-29 18:00:00" todt="2026-01-29 23:59:59"/>
|
|
216
|
+
</schedule>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
#### layout.js - XLF Translator
|
|
220
|
+
|
|
221
|
+
**Responsibilities:**
|
|
222
|
+
- Parse XLF (Xibo Layout Format) XML
|
|
223
|
+
- Translate to HTML/CSS/JavaScript
|
|
224
|
+
- Generate media playback code
|
|
225
|
+
- Handle PDF rendering
|
|
226
|
+
|
|
227
|
+
**XLF → HTML translation:**
|
|
228
|
+
```
|
|
229
|
+
XLF:
|
|
230
|
+
<layout width="1920" height="1080" bgcolor="#000">
|
|
231
|
+
<region id="1" width="1920" height="540" top="0" left="0">
|
|
232
|
+
<media type="image" duration="10" id="123">
|
|
233
|
+
<options><uri>image.jpg</uri></options>
|
|
234
|
+
</media>
|
|
235
|
+
</region>
|
|
236
|
+
</layout>
|
|
237
|
+
|
|
238
|
+
↓ translates to ↓
|
|
239
|
+
|
|
240
|
+
HTML:
|
|
241
|
+
<div id="layout_42" style="width:1920px; height:1080px; background:#000">
|
|
242
|
+
<div id="region_1" style="width:1920px; height:540px; top:0; left:0">
|
|
243
|
+
<!-- JavaScript-driven media playback -->
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
<script>
|
|
248
|
+
// Media sequencer
|
|
249
|
+
// startFn() → shows media
|
|
250
|
+
// wait duration
|
|
251
|
+
// stopFn() → hides media
|
|
252
|
+
// repeat for next media
|
|
253
|
+
</script>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Supported media types:**
|
|
257
|
+
- `image`: JPG, PNG, GIF
|
|
258
|
+
- `video`: MP4, WebM (HTML5 video)
|
|
259
|
+
- `pdf`: PDF documents (PDF.js)
|
|
260
|
+
- `webpage`: Embedded iframe
|
|
261
|
+
- `text`: HTML text blocks
|
|
262
|
+
- Widgets: clock, calendar, weather, etc.
|
|
263
|
+
|
|
264
|
+
#### config.js - Configuration
|
|
265
|
+
|
|
266
|
+
**Stored in localStorage:**
|
|
267
|
+
```javascript
|
|
268
|
+
{
|
|
269
|
+
cmsAddress: "https://displays.superpantalles.com",
|
|
270
|
+
cmsKey: "isiSdUCy",
|
|
271
|
+
hardwareKey: "abc123-def456",
|
|
272
|
+
displayName: "Lobby Display",
|
|
273
|
+
xmrChannel: "player-abc123-def456"
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Service Worker (sw.js)
|
|
278
|
+
|
|
279
|
+
**Responsibilities:**
|
|
280
|
+
- Cache all static assets (HTML, JS, CSS)
|
|
281
|
+
- Cache media files
|
|
282
|
+
- Enable offline operation
|
|
283
|
+
- Update cache on new versions
|
|
284
|
+
|
|
285
|
+
**Caching strategy:**
|
|
286
|
+
```javascript
|
|
287
|
+
// Static assets: Cache-first
|
|
288
|
+
self.addEventListener('fetch', (event) => {
|
|
289
|
+
if (event.request.url.includes('/assets/')) {
|
|
290
|
+
event.respondWith(
|
|
291
|
+
caches.match(event.request)
|
|
292
|
+
.then(response => response || fetch(event.request))
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// HTML pages: Network-first (for updates)
|
|
298
|
+
if (event.request.url.match(/\\.html$/)) {
|
|
299
|
+
event.respondWith(
|
|
300
|
+
fetch(event.request)
|
|
301
|
+
.catch(() => caches.match(event.request))
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Platform Wrappers
|
|
307
|
+
|
|
308
|
+
Each platform wraps the PWA core with platform-specific functionality.
|
|
309
|
+
|
|
310
|
+
### Chrome Extension
|
|
311
|
+
|
|
312
|
+
**Structure:**
|
|
313
|
+
```
|
|
314
|
+
platforms/chrome/
|
|
315
|
+
├── manifest.json # Manifest V3
|
|
316
|
+
├── background.js # Service worker
|
|
317
|
+
├── popup.html/js # Settings UI
|
|
318
|
+
└── dist/player/ # PWA core (synced)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Key features:**
|
|
322
|
+
- Background service worker (keep-alive)
|
|
323
|
+
- Chrome storage API for config
|
|
324
|
+
- Periodic alarm for backup collection
|
|
325
|
+
- Message passing to/from player
|
|
326
|
+
|
|
327
|
+
### Electron (Desktop)
|
|
328
|
+
|
|
329
|
+
**Structure:**
|
|
330
|
+
```
|
|
331
|
+
platforms/electron/
|
|
332
|
+
├── src/
|
|
333
|
+
│ ├── main/ # Main process (Node.js)
|
|
334
|
+
│ │ ├── index.ts
|
|
335
|
+
│ │ └── config/
|
|
336
|
+
│ │ └── config.ts # clientType='linux' (line 166)
|
|
337
|
+
│ └── renderer/ # Renderer process (PWA)
|
|
338
|
+
│ └── dist/ # PWA core (synced)
|
|
339
|
+
└── electron-forge config
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
**Key features:**
|
|
343
|
+
- Multi-window support
|
|
344
|
+
- System tray integration
|
|
345
|
+
- Auto-update capability
|
|
346
|
+
- Native menus and shortcuts
|
|
347
|
+
- Fullscreen/kiosk mode
|
|
348
|
+
|
|
349
|
+
### Android (WebView)
|
|
350
|
+
|
|
351
|
+
**Structure:**
|
|
352
|
+
```
|
|
353
|
+
platforms/android/
|
|
354
|
+
├── app/
|
|
355
|
+
│ ├── src/main/
|
|
356
|
+
│ │ ├── kotlin/
|
|
357
|
+
│ │ │ ├── MainActivity.kt # WebView host
|
|
358
|
+
│ │ │ ├── BootReceiver.kt # Auto-start
|
|
359
|
+
│ │ │ └── KioskHelper.kt # Kiosk mode
|
|
360
|
+
│ │ └── assets/ # PWA core (synced)
|
|
361
|
+
│ └── build.gradle
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**Key features:**
|
|
365
|
+
- Android WebView (embedded browser)
|
|
366
|
+
- Kiosk mode lock (disable home/back)
|
|
367
|
+
- Auto-start on boot
|
|
368
|
+
- Wake lock (screen stays on)
|
|
369
|
+
- Network change detection
|
|
370
|
+
|
|
371
|
+
### webOS (Cordova)
|
|
372
|
+
|
|
373
|
+
**Structure:**
|
|
374
|
+
```
|
|
375
|
+
platforms/webos/
|
|
376
|
+
├── appinfo.json # App metadata
|
|
377
|
+
├── app/
|
|
378
|
+
│ └── www/ # PWA core (synced)
|
|
379
|
+
└── service/ # Node.js service (optional)
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
**Key features:**
|
|
383
|
+
- LG TV integration
|
|
384
|
+
- Remote control navigation
|
|
385
|
+
- 4K resolution support
|
|
386
|
+
- Pro:Centric compatibility
|
|
387
|
+
|
|
388
|
+
## Communication Protocols
|
|
389
|
+
|
|
390
|
+
### XMDS (SOAP)
|
|
391
|
+
|
|
392
|
+
**Transport:** HTTP POST
|
|
393
|
+
**Format:** XML (SOAP 1.1)
|
|
394
|
+
**Endpoint:** `https://cms/xmds.php?v=7`
|
|
395
|
+
|
|
396
|
+
**Methods:**
|
|
397
|
+
- `RegisterDisplay`: Authenticate and get settings
|
|
398
|
+
- `RequiredFiles`: Get list of files to download
|
|
399
|
+
- `GetFile`: Download file (chunked, for large files)
|
|
400
|
+
- `Schedule`: Get schedule
|
|
401
|
+
- `NotifyStatus`: Report player status
|
|
402
|
+
- `SubmitLog`: Upload log messages
|
|
403
|
+
- `SubmitStats`: Upload playback statistics
|
|
404
|
+
- `SubmitScreenShot`: Upload screenshot
|
|
405
|
+
|
|
406
|
+
**Example request:**
|
|
407
|
+
```xml
|
|
408
|
+
POST /xmds.php?v=7 HTTP/1.1
|
|
409
|
+
Content-Type: text/xml
|
|
410
|
+
|
|
411
|
+
<?xml version="1.0"?>
|
|
412
|
+
<soap:Envelope>
|
|
413
|
+
<soap:Body>
|
|
414
|
+
<RegisterDisplay>
|
|
415
|
+
<serverKey>isiSdUCy</serverKey>
|
|
416
|
+
<hardwareKey>abc123</hardwareKey>
|
|
417
|
+
<clientType>linux</clientType> </RegisterDisplay>
|
|
418
|
+
</soap:Body>
|
|
419
|
+
</soap:Envelope>
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### XMR (WebSocket)
|
|
423
|
+
|
|
424
|
+
**Transport:** WebSocket
|
|
425
|
+
**Format:** JSON
|
|
426
|
+
**Endpoint:** `wss://cms/xmr`
|
|
427
|
+
|
|
428
|
+
**Message format:**
|
|
429
|
+
```json
|
|
430
|
+
{
|
|
431
|
+
"channel": "player-abc123",
|
|
432
|
+
"action": "collectNow",
|
|
433
|
+
"payload": {}
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
**Actions:**
|
|
438
|
+
- `collectNow`: Trigger collection
|
|
439
|
+
- `screenShot`: Capture screenshot
|
|
440
|
+
- `changeLayout`: Switch layout
|
|
441
|
+
- `commandUpdate`: Update command status
|
|
442
|
+
|
|
443
|
+
**Connection:**
|
|
444
|
+
```
|
|
445
|
+
Client Server
|
|
446
|
+
| |
|
|
447
|
+
|--- WS Connect (wss://cms/xmr) -->|
|
|
448
|
+
|<-- WS Upgrade ----------------|
|
|
449
|
+
| |
|
|
450
|
+
|--- Subscribe {channel} ------>|
|
|
451
|
+
|<-- Subscribed ----------------|
|
|
452
|
+
| |
|
|
453
|
+
|<-- collectNow command --------|
|
|
454
|
+
|--- Execute ----------------> |
|
|
455
|
+
|--- Status update ------------>|
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Media Download
|
|
459
|
+
|
|
460
|
+
**Transport:** HTTP GET
|
|
461
|
+
**Format:** Binary
|
|
462
|
+
**Endpoint:** `https://cms/{path}`
|
|
463
|
+
|
|
464
|
+
**Files downloaded:**
|
|
465
|
+
- Layout XLF files
|
|
466
|
+
- Media files (images, videos, PDFs)
|
|
467
|
+
- Widget HTML files
|
|
468
|
+
|
|
469
|
+
**Verification:**
|
|
470
|
+
- MD5 checksum from RequiredFiles
|
|
471
|
+
- Calculated on downloaded file
|
|
472
|
+
- Re-download if mismatch
|
|
473
|
+
|
|
474
|
+
## Data Flow
|
|
475
|
+
|
|
476
|
+
### Initial Setup Flow
|
|
477
|
+
|
|
478
|
+
```
|
|
479
|
+
User opens player
|
|
480
|
+
↓
|
|
481
|
+
Check localStorage for config
|
|
482
|
+
↓
|
|
483
|
+
[No config] → Redirect to setup.html
|
|
484
|
+
↓
|
|
485
|
+
User enters CMS details
|
|
486
|
+
↓
|
|
487
|
+
Save to localStorage
|
|
488
|
+
↓
|
|
489
|
+
Redirect to index.html
|
|
490
|
+
↓
|
|
491
|
+
Player initializes
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### Collection Cycle Flow
|
|
495
|
+
|
|
496
|
+
```
|
|
497
|
+
Player.collect() triggered
|
|
498
|
+
↓
|
|
499
|
+
1. RegisterDisplay (XMDS)
|
|
500
|
+
clientType='linux' → CMS returns READY ✓
|
|
501
|
+
↓
|
|
502
|
+
2. RequiredFiles (XMDS)
|
|
503
|
+
CMS returns list of files needed
|
|
504
|
+
↓
|
|
505
|
+
3. Download files (HTTP)
|
|
506
|
+
For each file: fetch → verify MD5 → cache
|
|
507
|
+
↓
|
|
508
|
+
4. Translate layouts (XLF → HTML)
|
|
509
|
+
Parse XLF → generate HTML/JS → cache
|
|
510
|
+
↓
|
|
511
|
+
5. Get Schedule (XMDS)
|
|
512
|
+
CMS returns schedule XML
|
|
513
|
+
↓
|
|
514
|
+
6. NotifyStatus (XMDS)
|
|
515
|
+
Report current status to CMS
|
|
516
|
+
↓
|
|
517
|
+
Schedule next collection (default: 15 minutes)
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### XMR Real-Time Flow
|
|
521
|
+
|
|
522
|
+
```
|
|
523
|
+
Player connects XMR WebSocket
|
|
524
|
+
↓
|
|
525
|
+
CMS sends collectNow command
|
|
526
|
+
↓
|
|
527
|
+
Player.collect() triggered immediately
|
|
528
|
+
↓
|
|
529
|
+
(Same as collection cycle above)
|
|
530
|
+
↓
|
|
531
|
+
Player reports completion to CMS
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### Layout Playback Flow
|
|
535
|
+
|
|
536
|
+
```
|
|
537
|
+
Schedule check (every minute)
|
|
538
|
+
↓
|
|
539
|
+
Determine current layout(s) from schedule
|
|
540
|
+
↓
|
|
541
|
+
Load layout HTML from cache
|
|
542
|
+
↓
|
|
543
|
+
Insert into DOM (replace previous layout)
|
|
544
|
+
↓
|
|
545
|
+
Execute layout JavaScript
|
|
546
|
+
↓
|
|
547
|
+
JavaScript sequencer:
|
|
548
|
+
for each media item:
|
|
549
|
+
startFn() → show media
|
|
550
|
+
wait duration
|
|
551
|
+
stopFn() → hide media
|
|
552
|
+
next media
|
|
553
|
+
loop forever
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
## Security Considerations
|
|
557
|
+
|
|
558
|
+
### Authentication
|
|
559
|
+
|
|
560
|
+
**CMS Key:**
|
|
561
|
+
- Shared secret between player and CMS
|
|
562
|
+
- Sent in every XMDS request
|
|
563
|
+
- Not encrypted in SOAP (relies on HTTPS)
|
|
564
|
+
|
|
565
|
+
**Hardware Key:**
|
|
566
|
+
- Unique identifier per display
|
|
567
|
+
- Generated on first setup
|
|
568
|
+
- Used for display identification
|
|
569
|
+
|
|
570
|
+
### Transport Security
|
|
571
|
+
|
|
572
|
+
**HTTPS Required:**
|
|
573
|
+
- All XMDS calls over HTTPS
|
|
574
|
+
- XMR WebSocket over WSS
|
|
575
|
+
- Media downloads over HTTPS
|
|
576
|
+
|
|
577
|
+
**Certificate Validation:**
|
|
578
|
+
- Browser/platform validates SSL certificates
|
|
579
|
+
- Self-signed certificates require user approval
|
|
580
|
+
|
|
581
|
+
### Content Security
|
|
582
|
+
|
|
583
|
+
**XSS Prevention:**
|
|
584
|
+
- All media loads from cache (controlled origin)
|
|
585
|
+
- Layouts are user-generated (trusted)
|
|
586
|
+
- No user input fields (no injection risk)
|
|
587
|
+
|
|
588
|
+
**CORS:**
|
|
589
|
+
- Media served from same origin (or CORS-enabled)
|
|
590
|
+
- Widgets may need CORS headers
|
|
591
|
+
|
|
592
|
+
### Storage Security
|
|
593
|
+
|
|
594
|
+
**localStorage:**
|
|
595
|
+
- Plain text storage (browser API)
|
|
596
|
+
- Accessible to same-origin JavaScript
|
|
597
|
+
- Not encrypted (CMS key exposed to anyone with file system access)
|
|
598
|
+
|
|
599
|
+
**Cache API:**
|
|
600
|
+
- Stores downloaded media
|
|
601
|
+
- Same-origin only
|
|
602
|
+
- Not encrypted
|
|
603
|
+
|
|
604
|
+
**Recommendations for production:**
|
|
605
|
+
1. Use HTTPS always (enforced)
|
|
606
|
+
2. Secure server file system (limit access)
|
|
607
|
+
3. Use VPN for signage network (optional)
|
|
608
|
+
4. Firewall CMS to known IPs (optional)
|
|
609
|
+
|
|
610
|
+
## Performance Considerations
|
|
611
|
+
|
|
612
|
+
### Bundle Size Optimization
|
|
613
|
+
|
|
614
|
+
**Code splitting:**
|
|
615
|
+
```javascript
|
|
616
|
+
// PDF.js loaded on-demand
|
|
617
|
+
if (media.type === 'pdf') {
|
|
618
|
+
const pdfjsLib = await import('pdfjs-dist');
|
|
619
|
+
}
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
**Lazy loading:**
|
|
623
|
+
- PDF worker (1.4 MB) only loads when needed
|
|
624
|
+
- XMR only initializes if CMS supports it
|
|
625
|
+
|
|
626
|
+
### Caching Strategy
|
|
627
|
+
|
|
628
|
+
**Static assets:**
|
|
629
|
+
- Cache-first (immutable)
|
|
630
|
+
- Long cache headers (1 year)
|
|
631
|
+
|
|
632
|
+
**HTML pages:**
|
|
633
|
+
- Network-first (updates)
|
|
634
|
+
- Cache fallback (offline)
|
|
635
|
+
|
|
636
|
+
**Media files:**
|
|
637
|
+
- Pre-fetch on collection cycle
|
|
638
|
+
- Persist in Cache API
|
|
639
|
+
- No network requests during playback
|
|
640
|
+
|
|
641
|
+
### Memory Management
|
|
642
|
+
|
|
643
|
+
**Layout cleanup:**
|
|
644
|
+
```javascript
|
|
645
|
+
// Remove previous layout before loading new one
|
|
646
|
+
container.innerHTML = ''; // Frees memory
|
|
647
|
+
|
|
648
|
+
// Stop media playback
|
|
649
|
+
stopFn(); // Releases media resources
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
**Cache limits:**
|
|
653
|
+
- Browser may evict old cache entries
|
|
654
|
+
- Implement LRU cache strategy (future)
|
|
655
|
+
|
|
656
|
+
## Future Enhancements
|
|
657
|
+
|
|
658
|
+
### Planned Features
|
|
659
|
+
|
|
660
|
+
1. **Multi-page PDF support**
|
|
661
|
+
- Rotate through pages automatically
|
|
662
|
+
- Duration per page
|
|
663
|
+
|
|
664
|
+
2. **Advanced scheduling**
|
|
665
|
+
- Priority levels
|
|
666
|
+
- Day-parting
|
|
667
|
+
- Recurring events
|
|
668
|
+
|
|
669
|
+
3. **Analytics**
|
|
670
|
+
- Proof of play logging
|
|
671
|
+
- Audience measurement (camera)
|
|
672
|
+
|
|
673
|
+
4. **Synchronized playback**
|
|
674
|
+
- Multiple displays in sync
|
|
675
|
+
- Video walls
|
|
676
|
+
|
|
677
|
+
5. **Embedded browser improvements**
|
|
678
|
+
- Better iframe sandboxing
|
|
679
|
+
- Enhanced widget API
|
|
680
|
+
|
|
681
|
+
### Technical Debt
|
|
682
|
+
|
|
683
|
+
1. **TypeScript migration**
|
|
684
|
+
- Convert core JavaScript to TypeScript
|
|
685
|
+
- Better type safety
|
|
686
|
+
|
|
687
|
+
2. **Unit tests**
|
|
688
|
+
- Jest/Vitest for unit testing
|
|
689
|
+
- Higher code coverage
|
|
690
|
+
|
|
691
|
+
3. **E2E tests**
|
|
692
|
+
- Playwright for browser testing
|
|
693
|
+
- Automated UI testing
|
|
694
|
+
|
|
695
|
+
4. **Offline-first improvements**
|
|
696
|
+
- Better cache invalidation
|
|
697
|
+
- Background sync API
|
|
698
|
+
|
|
699
|
+
## Contributing
|
|
700
|
+
|
|
701
|
+
When contributing, remember:
|
|
702
|
+
|
|
703
|
+
1. **Never change `clientType: 'linux'`**
|
|
704
|
+
2. Always run `npm test` before committing
|
|
705
|
+
3. Document breaking changes
|
|
706
|
+
4. Test on multiple platforms
|
|
707
|
+
5. Update this document for architectural changes
|
|
708
|
+
|
|
709
|
+
## References
|
|
710
|
+
|
|
711
|
+
- [Xibo CMS API Documentation](https://xibo.org.uk/manual/en/)
|
|
712
|
+
- [Service Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
|
|
713
|
+
- [PDF.js Documentation](https://mozilla.github.io/pdf.js/)
|
|
714
|
+
- [Electron Documentation](https://www.electronjs.org/docs)
|