SDK Architecture
System Diagram
┌─────────────────────────────────────────────────────┐
│ Platform Layer (pwa/main.ts) │
│ PwaPlayer: wires all packages together │
│ Wake Lock, Screenshot, SW registration │
└──────────────────────┬──────────────────────────────┘
│
┌──────────────────────────────────────┼──────────────────────────────────┐
│ │ │
┌───┴────────────┐ ┌─────────┴─────────┐ ┌──┴──────────────┐ ┌───────────────┐
│ PlayerCore │ │ RendererLite │ │ Service Worker │ │ XmrWrapper │
│ Orchestration │ │ XLF rendering │ │ Chunk streaming │ │ WebSocket │
│ Collection │ │ Element reuse │ │ Range requests │ │ 13 commands │
│ Offline cache │ │ Transitions │ │ IC interception │ │ Reconnect │
│ Commands │ │ Touch/keyboard │ │ Cache-first │ │ │
└───────┬────────┘ └──────────────────┘ └───────────────────┘ └───────────────┘
│
├───────────────────────┬───────────────────────┬──────────────────┐
│ │ │ │
┌───────┴──────────┐ ┌────────┴────────┐ ┌──────────┴──────┐ ┌────────┴───────┐
│ XmdsClient │ │ ScheduleManager │ │ CacheManager │ │ StatsCollector │
│ + RestClient │ │ + Interrupts │ │ + CacheProxy │ │ + LogReporter │
│ SOAP + REST │ │ + Overlays │ │ + DlManager │ │ Proof-of-play │
│ ETag caching │ │ Dayparting │ │ Parallel chunks│ │ Fault reporting │
└──────────────────┘ └─────────────────┘ └─────────────────┘ └────────┬────────┘
│
┌────────┴────────┐
│ SyncManager │
│ Lead/follower │
│ BC + WebSocket │
│ Group isolation│
│ Stats delegate │
└─────────────────┘
Key Design Patterns
1. Platform-Independent Core
PlayerCore contains all business logic without platform assumptions. The platform layer (PWA, Electron, Android) wires packages together and provides platform-specific implementations (Wake Lock, screenshot capture, Service Worker registration).
2. EventEmitter Communication
All modules communicate via events, not direct method calls:
PlayerCore emits: schedule-updated, collection-complete, purge-request
RendererLite emits: layoutStart, layoutEnd, widgetStart, widgetEnd
StatsCollector: listens to layout/widget events for proof-of-play
LogReporter: listens for errors from IC, renderer, collection
3. Dual Transport
The XMDS package provides two transport implementations with identical API surfaces:
- XmdsClient (SOAP/XML) — traditional protocol, all CMS versions
- RestClient (REST/JSON) — 30% smaller payloads, ETag 304 caching
PlayerCore doesn't know which transport is active.
4. Element Reuse
RendererLite pre-creates ALL widget DOM elements at layout load, stores them in a Map, and toggles visibility:
Layout load: Pre-create all elements → widgetElements.set(widgetId, element)
Widget switch: Hide current → Show next (visibility toggle, no DOM create/destroy)
Layout replay: Detect isSameLayout → Reuse elements → Restart videos
Layout change: Revoke blob URLs → Destroy elements → Create new set
5. Service Worker as Media Server
Instead of a local HTTP server, the PWA uses its Service Worker to intercept fetch requests and serve cached media:
- Chunk streaming with Range request support
- IC interception (Interactive Control routes)
- Font CSS URL rewriting
- Cache-first with network fallback
- Offline operation
Data Flow
Collection Cycle
1. RegisterDisplay() → CMS settings, commands, XMR address
2. CRC32 comparison → Skip if unchanged
3. RequiredFiles() → File list with MD5 hashes
4. Download missing files → 4 parallel chunks, MD5 verify
5. Schedule() → Layout schedule XML
6. Parse schedule → Layouts, overlays, actions, commands
7. MediaInventory() → Report cached file inventory
8. NotifyStatus() → Report status (disk, timezone, layout)
9. SubmitStats() → Proof-of-play records
10. SubmitLog() → Queued log entries
11. SubmitScreenShot() → If captured
Offline Mode
Network down:
→ XMDS calls fail → Use IndexedDB-cached schedule/settings
→ Media requests → Service Worker serves from Cache API
→ Stats/logs → Queued in IndexedDB, submitted when network returns
→ Player continues rendering with last known schedule
Storage Architecture
Cache API (Binary Files)
Cache: xibo-media
├── /media/{id} → Images, videos, audio
├── /widget/{widgetId} → Widget HTML (getWidgetHtml)
├── /font/{fontFile} → Font files
└── /static/{path} → Static player assets
IndexedDB (Structured Data)
Database: xibo-player
├── files → File metadata (id, type, md5, size)
├── stats → Proof-of-play (pending submission)
├── logs → Log entries (pending submission)
├── schedule → Last known schedule (offline fallback)
├── settings → Last known CMS settings
└── requiredFiles → Last known required files
Technology Stack
| Layer | Technology | Rationale |
|---|---|---|
| Language | JavaScript (ES2020+) | Cross-platform, no transpilation |
| HTTP client | fetch() + fetchWithRetry | Built-in, configurable retry |
| XML parsing | DOMParser | Built-in, namespace-aware |
| Storage | Cache API + IndexedDB | Built-in PWA APIs |
| Offline | Service Worker | Intercepts all fetches |
| MD5 hashing | spark-md5 | 4KB, ArrayBuffer support |
| PDF.js (lazy-loaded) | Industry standard | |
| HLS | hls.js (lazy-loaded) | Polyfill for non-Safari |
| Animations | Web Animations API | Built-in, GPU-accelerated |
| Build | Vite | Tree-shaking, minification |
| Packages | pnpm workspaces | Workspace management |
Runtime dependencies: spark-md5 (4KB), hls.js (lazy), PDF.js (lazy), xibo-communication-framework
