@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.
@@ -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)