@xiboplayer/stats 0.2.0 → 0.3.1

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.
Files changed (3) hide show
  1. package/README.md +17 -354
  2. package/package.json +2 -2
  3. package/src/index.js +2 -0
package/README.md CHANGED
@@ -1,15 +1,15 @@
1
1
  # @xiboplayer/stats
2
2
 
3
- Proof of play tracking and CMS logging for Xibo Players.
3
+ **Proof of play tracking, stats reporting, and CMS logging.**
4
4
 
5
- ## Features
5
+ ## Overview
6
6
 
7
- - **StatsCollector**: Track layout and widget playback for proof of play reporting
8
- - **LogReporter**: Collect and submit application logs to CMS
9
- - **IndexedDB Storage**: Persistent storage across browser sessions
10
- - **Offline Support**: Stats and logs are queued when offline and submitted when online
11
- - **Quota Management**: Automatic cleanup of old data when storage quota is exceeded
12
- - **XMDS Integration**: Format stats and logs as XML for CMS submission
7
+ Collects and reports display analytics to the Xibo CMS:
8
+
9
+ - **Proof of play** per-layout and per-widget duration tracking
10
+ - **Aggregation modes** individual or aggregated stat submission (configurable from CMS)
11
+ - **Log reporting** display logs batched and submitted to CMS
12
+ - **Fault alerts** error deduplication and fault reporting
13
13
 
14
14
  ## Installation
15
15
 
@@ -19,357 +19,20 @@ npm install @xiboplayer/stats
19
19
 
20
20
  ## Usage
21
21
 
22
- ### StatsCollector - Proof of Play Tracking
23
-
24
- The StatsCollector tracks when layouts and widgets (media) are played for reporting to the CMS.
25
-
26
- ```javascript
27
- import { StatsCollector, formatStats } from '@xiboplayer/stats';
28
-
29
- // Initialize collector
30
- const collector = new StatsCollector();
31
- await collector.init();
32
-
33
- // Track layout playback
34
- await collector.startLayout(layoutId, scheduleId);
35
- // ... layout plays for 5 minutes ...
36
- await collector.endLayout(layoutId, scheduleId);
37
-
38
- // Track widget playback
39
- await collector.startWidget(mediaId, layoutId, scheduleId);
40
- // ... widget plays for 30 seconds ...
41
- await collector.endWidget(mediaId, layoutId, scheduleId);
42
-
43
- // Submit stats to CMS
44
- const stats = await collector.getStatsForSubmission(50); // Get up to 50 stats
45
- const xml = formatStats(stats);
46
- const success = await xmds.submitStats(xml);
47
-
48
- if (success) {
49
- await collector.clearSubmittedStats(stats);
50
- }
51
- ```
52
-
53
- #### API Reference
54
-
55
- **`new StatsCollector()`**
56
-
57
- Create a new stats collector instance.
58
-
59
- **`async init()`**
60
-
61
- Initialize IndexedDB storage. Must be called before using other methods.
62
-
63
- **`async startLayout(layoutId, scheduleId)`**
64
-
65
- Start tracking a layout. Creates a new stat entry and marks it as in-progress.
66
-
67
- - `layoutId` (number): Layout ID from CMS
68
- - `scheduleId` (number): Schedule ID that triggered this layout
69
-
70
- **`async endLayout(layoutId, scheduleId)`**
71
-
72
- End tracking a layout. Calculates duration and saves to database.
73
-
74
- - `layoutId` (number): Layout ID from CMS
75
- - `scheduleId` (number): Schedule ID
76
-
77
- **`async startWidget(mediaId, layoutId, scheduleId)`**
78
-
79
- Start tracking a widget/media item.
80
-
81
- - `mediaId` (number): Media ID from CMS
82
- - `layoutId` (number): Parent layout ID
83
- - `scheduleId` (number): Schedule ID
84
-
85
- **`async endWidget(mediaId, layoutId, scheduleId)`**
86
-
87
- End tracking a widget. Calculates duration and saves to database.
88
-
89
- - `mediaId` (number): Media ID from CMS
90
- - `layoutId` (number): Parent layout ID
91
- - `scheduleId` (number): Schedule ID
92
-
93
- **`async getStatsForSubmission(limit = 50)`**
94
-
95
- Get unsubmitted stats ready for submission to CMS.
96
-
97
- - `limit` (number): Maximum number of stats to return (default: 50)
98
- - Returns: Array of stat objects
99
-
100
- **`async clearSubmittedStats(stats)`**
101
-
102
- Delete stats that were successfully submitted to CMS.
103
-
104
- - `stats` (Array): Array of stat objects to delete
105
-
106
- **`async getAllStats()`**
107
-
108
- Get all stats from database (for debugging).
109
-
110
- - Returns: Array of all stat objects
111
-
112
- **`async clearAllStats()`**
113
-
114
- Clear all stats from database (for testing).
115
-
116
- #### Stat Object Structure
117
-
118
- ```javascript
119
- {
120
- id: 123, // Auto-generated ID
121
- type: 'layout', // 'layout' or 'media'
122
- layoutId: 456, // Layout ID from CMS
123
- scheduleId: 789, // Schedule ID
124
- mediaId: 111, // Only for type='media'
125
- start: Date, // Start time
126
- end: Date, // End time
127
- duration: 300, // Duration in seconds
128
- count: 1, // Number of plays (always 1)
129
- submitted: 0 // 0 = not submitted, 1 = submitted
130
- }
131
- ```
132
-
133
- ### formatStats - XML Formatting
134
-
135
- Format stats array as XML for XMDS SubmitStats API.
136
-
137
- ```javascript
138
- import { formatStats } from '@xiboplayer/stats';
139
-
140
- const stats = await collector.getStatsForSubmission(50);
141
- const xml = formatStats(stats);
142
-
143
- console.log(xml);
144
- // <stats>
145
- // <stat type="layout" fromdt="2026-02-10 12:00:00" todt="2026-02-10 12:05:00"
146
- // scheduleid="123" layoutid="456" count="1" duration="300" />
147
- // <stat type="media" fromdt="2026-02-10 12:00:00" todt="2026-02-10 12:01:00"
148
- // scheduleid="123" layoutid="456" mediaid="789" count="1" duration="60" />
149
- // </stats>
150
- ```
151
-
152
- ### LogReporter - CMS Logging
153
-
154
- The LogReporter collects application logs and submits them to the CMS via XMDS.
155
-
156
- ```javascript
157
- import { LogReporter, formatLogs } from '@xiboplayer/stats';
158
-
159
- // Initialize reporter
160
- const reporter = new LogReporter();
161
- await reporter.init();
162
-
163
- // Log messages
164
- await reporter.error('Failed to load layout 123', 'PLAYER');
165
- await reporter.audit('User logged in', 'AUTH');
166
- await reporter.info('Layout loaded successfully', 'RENDERER');
167
- await reporter.debug('Cache hit for file ABC', 'CACHE');
168
-
169
- // Submit logs to CMS
170
- const logs = await reporter.getLogsForSubmission(100); // Get up to 100 logs
171
- const xml = formatLogs(logs);
172
- const success = await xmds.submitLog(xml);
173
-
174
- if (success) {
175
- await reporter.clearSubmittedLogs(logs);
176
- }
177
- ```
178
-
179
- #### API Reference
180
-
181
- **`new LogReporter()`**
182
-
183
- Create a new log reporter instance.
184
-
185
- **`async init()`**
186
-
187
- Initialize IndexedDB storage. Must be called before using other methods.
188
-
189
- **`async log(level, message, category = 'PLAYER')`**
190
-
191
- Log a message with specified level.
192
-
193
- - `level` (string): Log level - 'error', 'audit', 'info', or 'debug'
194
- - `message` (string): Log message
195
- - `category` (string): Log category (default: 'PLAYER')
196
-
197
- **`async error(message, category = 'PLAYER')`**
198
-
199
- Shorthand for logging an error message.
200
-
201
- **`async audit(message, category = 'PLAYER')`**
202
-
203
- Shorthand for logging an audit message.
204
-
205
- **`async info(message, category = 'PLAYER')`**
206
-
207
- Shorthand for logging an info message.
208
-
209
- **`async debug(message, category = 'PLAYER')`**
210
-
211
- Shorthand for logging a debug message.
212
-
213
- **`async getLogsForSubmission(limit = 100)`**
214
-
215
- Get unsubmitted logs ready for submission to CMS.
216
-
217
- - `limit` (number): Maximum number of logs to return (default: 100)
218
- - Returns: Array of log objects
219
-
220
- **`async clearSubmittedLogs(logs)`**
221
-
222
- Delete logs that were successfully submitted to CMS.
223
-
224
- - `logs` (Array): Array of log objects to delete
225
-
226
- **`async getAllLogs()`**
227
-
228
- Get all logs from database (for debugging).
229
-
230
- - Returns: Array of all log objects
231
-
232
- **`async clearAllLogs()`**
233
-
234
- Clear all logs from database (for testing).
235
-
236
- #### Log Object Structure
237
-
238
22
  ```javascript
239
- {
240
- id: 123, // Auto-generated ID
241
- level: 'error', // 'error', 'audit', 'info', or 'debug'
242
- message: 'Error text', // Log message
243
- category: 'PLAYER', // Log category
244
- timestamp: Date, // When log was created
245
- submitted: 0 // 0 = not submitted, 1 = submitted
246
- }
247
- ```
248
-
249
- #### Common Categories
23
+ import { StatsCollector } from '@xiboplayer/stats';
250
24
 
251
- - `PLAYER` - Player lifecycle and general operations
252
- - `RENDERER` - Layout rendering and widget display
253
- - `CACHE` - File caching and downloads
254
- - `XMDS` - CMS communication
255
- - `AUTH` - Authentication and registration
256
- - `SCHEDULE` - Schedule management
25
+ const stats = new StatsCollector({ transport });
26
+ stats.init();
257
27
 
258
- ### formatLogs - XML Formatting
259
-
260
- Format logs array as XML for XMDS SubmitLog API.
261
-
262
- ```javascript
263
- import { formatLogs } from '@xiboplayer/stats';
264
-
265
- const logs = await reporter.getLogsForSubmission(100);
266
- const xml = formatLogs(logs);
267
-
268
- console.log(xml);
269
- // <logs>
270
- // <log date="2026-02-10 12:00:00" category="PLAYER" type="error"
271
- // message="Failed to load layout 123" />
272
- // <log date="2026-02-10 12:01:00" category="AUTH" type="audit"
273
- // message="User logged in" />
274
- // </logs>
275
- ```
276
-
277
- ## Storage
278
-
279
- Both StatsCollector and LogReporter use IndexedDB for persistent storage:
280
-
281
- - **Database Names**: `xibo-player-stats` and `xibo-player-logs`
282
- - **Indexes**: Both use an index on the `submitted` field for fast queries
283
- - **Quota Management**: Automatically cleans old submitted entries when quota is exceeded
284
- - **Offline Support**: Data persists across browser sessions and restarts
285
-
286
- ### Storage Limits
287
-
288
- - **Chrome**: ~60% of available disk space
289
- - **Firefox**: ~10% of available disk space
290
- - **Safari**: ~1GB
291
-
292
- When storage quota is exceeded, the oldest 100 submitted entries are automatically deleted.
293
-
294
- ## Integration with PWA Platform
295
-
296
- The stats package is integrated into the PWA player (`xiboplayer-pwa/src/main.ts`):
297
-
298
- ```typescript
299
- // Initialize stats collector
300
- this.statsCollector = new StatsCollector();
301
- await this.statsCollector.init();
302
-
303
- // Track layout events
304
- this.renderer.on('layoutStart', (layoutId) => {
305
- this.statsCollector.startLayout(layoutId, this.currentScheduleId);
306
- });
307
-
308
- this.renderer.on('layoutEnd', (layoutId) => {
309
- this.statsCollector.endLayout(layoutId, this.currentScheduleId);
310
- });
311
-
312
- // Track widget events
313
- this.renderer.on('widgetStart', ({ widgetId, layoutId, mediaId }) => {
314
- if (mediaId) {
315
- this.statsCollector.startWidget(mediaId, layoutId, this.currentScheduleId);
316
- }
317
- });
318
-
319
- this.renderer.on('widgetEnd', ({ widgetId, layoutId, mediaId }) => {
320
- if (mediaId) {
321
- this.statsCollector.endWidget(mediaId, layoutId, this.currentScheduleId);
322
- }
323
- });
324
-
325
- // Submit stats periodically (every 10 minutes)
326
- setInterval(async () => {
327
- const stats = await this.statsCollector.getStatsForSubmission(50);
328
- if (stats.length > 0) {
329
- const xml = formatStats(stats);
330
- const success = await this.xmds.submitStats(xml);
331
- if (success) {
332
- await this.statsCollector.clearSubmittedStats(stats);
333
- }
334
- }
335
- }, 600000);
28
+ // Stats are collected automatically from player events
29
+ // and submitted during each collection cycle
336
30
  ```
337
31
 
338
- ## Testing
339
-
340
- The package includes comprehensive tests using Vitest and fake-indexeddb:
341
-
342
- ```bash
343
- # Run tests
344
- npm test
345
-
346
- # Run tests in watch mode
347
- npm run test:watch
348
-
349
- # Run tests with coverage
350
- npm run test:coverage
351
- ```
352
-
353
- ### Test Coverage
354
-
355
- - **StatsCollector**: 32 tests covering initialization, layout tracking, widget tracking, submission flow, edge cases, and database operations
356
- - **LogReporter**: 35 tests covering initialization, log creation, submission flow, edge cases, and database operations
357
- - **Total**: 67 tests with 100% pass rate
358
-
359
- ## Compatibility
360
-
361
- - **Node.js**: 18+ (for ESM support)
362
- - **Browsers**: Chrome 58+, Firefox 52+, Safari 12+, Edge 79+
363
- - **IndexedDB**: Required for persistent storage
364
-
365
- ## Author
366
-
367
- Pau Aliagas <linuxnow@gmail.com>
368
-
369
- ## Repository
32
+ ## Dependencies
370
33
 
371
- https://github.com/xibo/xibo-players/tree/main/packages/stats
34
+ - `@xiboplayer/utils` — logger, events
372
35
 
373
- ## License
36
+ ---
374
37
 
375
- AGPL-3.0-or-later
38
+ **Part of the [XiboPlayer SDK](https://github.com/xibo-players/xiboplayer)** | [MCP Server](https://github.com/xibo-players/xiboplayer/tree/main/mcp-server) for AI-assisted development
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiboplayer/stats",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Proof of play tracking, stats reporting, and CMS logging",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -9,7 +9,7 @@
9
9
  "./collector": "./src/stats-collector.js"
10
10
  },
11
11
  "dependencies": {
12
- "@xiboplayer/utils": "0.2.0"
12
+ "@xiboplayer/utils": "0.3.1"
13
13
  },
14
14
  "devDependencies": {
15
15
  "vitest": "^2.0.0",
package/src/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  // @xiboplayer/stats - Proof of play and statistics reporting
2
+ import pkg from '../package.json' with { type: 'json' };
3
+ export const VERSION = pkg.version;
2
4
 
3
5
  /**
4
6
  * Stats collector for proof of play tracking