unified-video-framework 1.4.157 → 1.4.159

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 (67) hide show
  1. package/package.json +12 -2
  2. package/packages/core/dist/analytics/adapters/PlayerAnalyticsAdapter.d.ts +18 -0
  3. package/packages/core/dist/analytics/adapters/PlayerAnalyticsAdapter.d.ts.map +1 -0
  4. package/packages/core/dist/analytics/adapters/PlayerAnalyticsAdapter.js +117 -0
  5. package/packages/core/dist/analytics/adapters/PlayerAnalyticsAdapter.js.map +1 -0
  6. package/packages/core/dist/analytics/core/AnalyticsProvider.d.ts +18 -0
  7. package/packages/core/dist/analytics/core/AnalyticsProvider.d.ts.map +1 -0
  8. package/packages/core/dist/analytics/core/AnalyticsProvider.js +99 -0
  9. package/packages/core/dist/analytics/core/AnalyticsProvider.js.map +1 -0
  10. package/packages/core/dist/analytics/core/DynamicAnalyticsManager.d.ts +20 -0
  11. package/packages/core/dist/analytics/core/DynamicAnalyticsManager.d.ts.map +1 -0
  12. package/packages/core/dist/analytics/core/DynamicAnalyticsManager.js +161 -0
  13. package/packages/core/dist/analytics/core/DynamicAnalyticsManager.js.map +1 -0
  14. package/packages/core/dist/analytics/core/EventBatcher.d.ts +32 -0
  15. package/packages/core/dist/analytics/core/EventBatcher.d.ts.map +1 -0
  16. package/packages/core/dist/analytics/core/EventBatcher.js +98 -0
  17. package/packages/core/dist/analytics/core/EventBatcher.js.map +1 -0
  18. package/packages/core/dist/analytics/core/PlayerAnalytics.d.ts +19 -0
  19. package/packages/core/dist/analytics/core/PlayerAnalytics.d.ts.map +1 -0
  20. package/packages/core/dist/analytics/core/PlayerAnalytics.js +80 -0
  21. package/packages/core/dist/analytics/core/PlayerAnalytics.js.map +1 -0
  22. package/packages/core/dist/analytics/examples/DynamicAnalyticsExample.d.ts +32 -0
  23. package/packages/core/dist/analytics/examples/DynamicAnalyticsExample.d.ts.map +1 -0
  24. package/packages/core/dist/analytics/examples/DynamicAnalyticsExample.js +220 -0
  25. package/packages/core/dist/analytics/examples/DynamicAnalyticsExample.js.map +1 -0
  26. package/packages/core/dist/analytics/index.d.ts +13 -0
  27. package/packages/core/dist/analytics/index.d.ts.map +1 -0
  28. package/packages/core/dist/analytics/index.js +13 -0
  29. package/packages/core/dist/analytics/index.js.map +1 -0
  30. package/packages/core/dist/analytics/types/AnalyticsTypes.d.ts +239 -0
  31. package/packages/core/dist/analytics/types/AnalyticsTypes.d.ts.map +1 -0
  32. package/packages/core/dist/analytics/types/AnalyticsTypes.js +8 -0
  33. package/packages/core/dist/analytics/types/AnalyticsTypes.js.map +1 -0
  34. package/packages/core/dist/analytics/utils/DeviceDetection.d.ts +27 -0
  35. package/packages/core/dist/analytics/utils/DeviceDetection.d.ts.map +1 -0
  36. package/packages/core/dist/analytics/utils/DeviceDetection.js +184 -0
  37. package/packages/core/dist/analytics/utils/DeviceDetection.js.map +1 -0
  38. package/packages/core/dist/chapter-manager.d.ts +39 -0
  39. package/packages/core/dist/index.d.ts +1 -0
  40. package/packages/core/dist/index.d.ts.map +1 -1
  41. package/packages/core/dist/index.js +1 -0
  42. package/packages/core/dist/index.js.map +1 -1
  43. package/packages/core/src/analytics/README.md +902 -0
  44. package/packages/core/src/analytics/adapters/PlayerAnalyticsAdapter.ts +156 -0
  45. package/packages/core/src/analytics/core/AnalyticsProvider.ts +169 -0
  46. package/packages/core/src/analytics/core/DynamicAnalyticsManager.ts +199 -0
  47. package/packages/core/src/analytics/core/EventBatcher.ts +160 -0
  48. package/packages/core/src/analytics/core/PlayerAnalytics.ts +147 -0
  49. package/packages/core/src/analytics/index.ts +51 -0
  50. package/packages/core/src/analytics/types/AnalyticsTypes.ts +315 -0
  51. package/packages/core/src/analytics/utils/DeviceDetection.ts +220 -0
  52. package/packages/core/src/index.ts +3 -0
  53. package/packages/ios/README.md +84 -0
  54. package/packages/web/dist/WebPlayer.d.ts +6 -0
  55. package/packages/web/dist/WebPlayer.d.ts.map +1 -1
  56. package/packages/web/dist/WebPlayer.js +273 -67
  57. package/packages/web/dist/WebPlayer.js.map +1 -1
  58. package/packages/web/dist/epg/EPGController.d.ts +78 -0
  59. package/packages/web/dist/epg/EPGController.d.ts.map +1 -0
  60. package/packages/web/dist/epg/EPGController.js +476 -0
  61. package/packages/web/dist/epg/EPGController.js.map +1 -0
  62. package/packages/web/src/WebPlayer.ts +336 -85
  63. package/src/analytics/README.md +902 -0
  64. package/src/analytics/adapters/PlayerAnalyticsAdapter.ts +572 -0
  65. package/src/analytics/core/DynamicAnalyticsManager.ts +526 -0
  66. package/src/analytics/examples/DynamicAnalyticsExample.ts +324 -0
  67. package/src/analytics/index.ts +60 -0
@@ -0,0 +1,526 @@
1
+ /**
2
+ * DynamicAnalyticsManager - Manages multiple analytics providers dynamically
3
+ * Supports different analytics systems including the player analytics API
4
+ */
5
+
6
+ import { EventEmitter } from 'events';
7
+ import {
8
+ AnalyticsEvent,
9
+ PlayerState,
10
+ PlayerEventType,
11
+ PlayerSessionInfo
12
+ } from '../types/AnalyticsTypes';
13
+ import { PlayerAnalyticsAdapter, PlayerAnalyticsConfig } from '../adapters/PlayerAnalyticsAdapter';
14
+
15
+ // Provider types enum
16
+ export enum AnalyticsProviderType {
17
+ PLAYER_ANALYTICS = 'player_analytics',
18
+ CUSTOM = 'custom'
19
+ }
20
+
21
+ // Base interface for all analytics providers
22
+ export interface BaseAnalyticsProvider {
23
+ startSession(videoInfo: any, customData?: any): string;
24
+ endSession(): Promise<void>;
25
+ trackEvent(event: any, playerState?: PlayerState, videoInfo?: any): void;
26
+ trackCustomEvent(eventType: string, data?: any): void;
27
+ flush(): Promise<void>;
28
+ getCurrentSession(): any;
29
+ destroy(): void;
30
+ }
31
+
32
+ // Configuration for different provider types
33
+ export interface DynamicAnalyticsConfig {
34
+ enabled: boolean;
35
+ providers: Array<{
36
+ type: AnalyticsProviderType;
37
+ name: string;
38
+ config: any; // Provider-specific configuration
39
+ enabled: boolean;
40
+ priority?: number; // For ordering multiple providers
41
+ }>;
42
+ globalSettings?: {
43
+ enableConsoleLogging?: boolean;
44
+ enableErrorReporting?: boolean;
45
+ sessionTimeout?: number; // minutes
46
+ };
47
+ }
48
+
49
+ export class DynamicAnalyticsManager extends EventEmitter {
50
+ private config: DynamicAnalyticsConfig;
51
+ private providers: Map<string, BaseAnalyticsProvider> = new Map();
52
+ private activeSession: {
53
+ sessionId: string;
54
+ videoInfo: any;
55
+ startTime: number;
56
+ providers: string[];
57
+ } | null = null;
58
+ private sessionTimeout: NodeJS.Timeout | null = null;
59
+
60
+ constructor(config: DynamicAnalyticsConfig) {
61
+ super();
62
+ this.config = config;
63
+ this.initializeProviders();
64
+ }
65
+
66
+ /**
67
+ * Start a new analytics session across all enabled providers
68
+ */
69
+ public startSession(videoInfo: {
70
+ id: string;
71
+ title?: string;
72
+ type?: string;
73
+ duration?: number;
74
+ isLive?: boolean;
75
+ metadata?: Record<string, any>;
76
+ }, customData?: Record<string, any>): string {
77
+ if (!this.config.enabled) {
78
+ return 'disabled';
79
+ }
80
+
81
+ // End existing session if active
82
+ if (this.activeSession) {
83
+ this.endSession();
84
+ }
85
+
86
+ const sessionId = this.generateSessionId();
87
+ const enabledProviders: string[] = [];
88
+
89
+ // Start session on all enabled providers
90
+ for (const [name, provider] of this.providers) {
91
+ try {
92
+ const providerSessionId = provider.startSession(videoInfo, customData);
93
+ enabledProviders.push(name);
94
+
95
+ this.log(`Started session on provider ${name}: ${providerSessionId}`);
96
+ } catch (error) {
97
+ this.handleError(`Failed to start session on provider ${name}`, error);
98
+ }
99
+ }
100
+
101
+ this.activeSession = {
102
+ sessionId,
103
+ videoInfo,
104
+ startTime: Date.now(),
105
+ providers: enabledProviders
106
+ };
107
+
108
+ // Set session timeout if configured
109
+ if (this.config.globalSettings?.sessionTimeout) {
110
+ this.setSessionTimeout();
111
+ }
112
+
113
+ this.emit('sessionStart', {
114
+ sessionId,
115
+ videoInfo,
116
+ providers: enabledProviders
117
+ });
118
+
119
+ return sessionId;
120
+ }
121
+
122
+ /**
123
+ * End the current analytics session
124
+ */
125
+ public async endSession(): Promise<void> {
126
+ if (!this.activeSession) {
127
+ return;
128
+ }
129
+
130
+ const { sessionId, providers } = this.activeSession;
131
+
132
+ // Clear session timeout
133
+ if (this.sessionTimeout) {
134
+ clearTimeout(this.sessionTimeout);
135
+ this.sessionTimeout = null;
136
+ }
137
+
138
+ // End session on all active providers
139
+ const endPromises = providers.map(async (name) => {
140
+ const provider = this.providers.get(name);
141
+ if (provider) {
142
+ try {
143
+ await provider.endSession();
144
+ this.log(`Ended session on provider ${name}`);
145
+ } catch (error) {
146
+ this.handleError(`Failed to end session on provider ${name}`, error);
147
+ }
148
+ }
149
+ });
150
+
151
+ await Promise.allSettled(endPromises);
152
+
153
+ this.emit('sessionEnd', {
154
+ sessionId,
155
+ duration: Date.now() - this.activeSession.startTime,
156
+ providers
157
+ });
158
+
159
+ this.activeSession = null;
160
+ }
161
+
162
+ /**
163
+ * Track an analytics event across all providers
164
+ */
165
+ public trackEvent(
166
+ eventType: PlayerEventType,
167
+ playerState: PlayerState,
168
+ additionalData?: Record<string, any>
169
+ ): void {
170
+ if (!this.config.enabled || !this.activeSession) {
171
+ return;
172
+ }
173
+
174
+ const event: AnalyticsEvent = {
175
+ eventId: this.generateEventId(),
176
+ eventType,
177
+ timestamp: Date.now(),
178
+ sessionId: this.activeSession.sessionId,
179
+ videoId: this.activeSession.videoInfo.id,
180
+ currentTime: playerState.currentTime,
181
+ duration: playerState.duration,
182
+ ...additionalData
183
+ } as AnalyticsEvent;
184
+
185
+ // Track on all active providers
186
+ for (const providerName of this.activeSession.providers) {
187
+ const provider = this.providers.get(providerName);
188
+ if (provider) {
189
+ try {
190
+ provider.trackEvent(event, playerState, this.activeSession.videoInfo);
191
+ } catch (error) {
192
+ this.handleError(`Failed to track event on provider ${providerName}`, error);
193
+ }
194
+ }
195
+ }
196
+
197
+ this.emit('eventTracked', {
198
+ event,
199
+ providers: this.activeSession.providers
200
+ });
201
+ }
202
+
203
+ /**
204
+ * Track a custom event
205
+ */
206
+ public trackCustomEvent(eventType: string, data?: Record<string, any>): void {
207
+ if (!this.config.enabled || !this.activeSession) {
208
+ return;
209
+ }
210
+
211
+ // Track on all active providers
212
+ for (const providerName of this.activeSession.providers) {
213
+ const provider = this.providers.get(providerName);
214
+ if (provider) {
215
+ try {
216
+ provider.trackCustomEvent(eventType, data);
217
+ } catch (error) {
218
+ this.handleError(`Failed to track custom event on provider ${providerName}`, error);
219
+ }
220
+ }
221
+ }
222
+
223
+ this.emit('customEventTracked', {
224
+ eventType,
225
+ data,
226
+ providers: this.activeSession.providers
227
+ });
228
+ }
229
+
230
+ /**
231
+ * Flush all pending events across providers
232
+ */
233
+ public async flush(): Promise<void> {
234
+ if (!this.activeSession) {
235
+ return;
236
+ }
237
+
238
+ const flushPromises = this.activeSession.providers.map(async (name) => {
239
+ const provider = this.providers.get(name);
240
+ if (provider) {
241
+ try {
242
+ await provider.flush();
243
+ this.log(`Flushed events for provider ${name}`);
244
+ } catch (error) {
245
+ this.handleError(`Failed to flush events for provider ${name}`, error);
246
+ }
247
+ }
248
+ });
249
+
250
+ await Promise.allSettled(flushPromises);
251
+ }
252
+
253
+ /**
254
+ * Add a new analytics provider dynamically
255
+ */
256
+ public addProvider(
257
+ name: string,
258
+ type: AnalyticsProviderType,
259
+ config: any,
260
+ enabled: boolean = true
261
+ ): void {
262
+ try {
263
+ const provider = this.createProvider(type, config);
264
+ this.providers.set(name, provider);
265
+
266
+ this.log(`Added provider ${name} of type ${type}`);
267
+
268
+ // If there's an active session, start it on the new provider
269
+ if (enabled && this.activeSession) {
270
+ provider.startSession(this.activeSession.videoInfo);
271
+ this.activeSession.providers.push(name);
272
+ }
273
+
274
+ this.emit('providerAdded', { name, type, enabled });
275
+ } catch (error) {
276
+ this.handleError(`Failed to add provider ${name}`, error);
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Remove an analytics provider
282
+ */
283
+ public async removeProvider(name: string): Promise<void> {
284
+ const provider = this.providers.get(name);
285
+ if (!provider) {
286
+ return;
287
+ }
288
+
289
+ try {
290
+ // End session on this provider if active
291
+ if (this.activeSession && this.activeSession.providers.includes(name)) {
292
+ await provider.endSession();
293
+ this.activeSession.providers = this.activeSession.providers.filter(p => p !== name);
294
+ }
295
+
296
+ // Destroy the provider
297
+ provider.destroy();
298
+ this.providers.delete(name);
299
+
300
+ this.log(`Removed provider ${name}`);
301
+ this.emit('providerRemoved', { name });
302
+ } catch (error) {
303
+ this.handleError(`Failed to remove provider ${name}`, error);
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Enable or disable a provider
309
+ */
310
+ public toggleProvider(name: string, enabled: boolean): void {
311
+ const provider = this.providers.get(name);
312
+ if (!provider) {
313
+ this.log(`Provider ${name} not found`);
314
+ return;
315
+ }
316
+
317
+ if (this.activeSession) {
318
+ if (enabled && !this.activeSession.providers.includes(name)) {
319
+ // Enable: start session on this provider
320
+ try {
321
+ provider.startSession(this.activeSession.videoInfo);
322
+ this.activeSession.providers.push(name);
323
+ } catch (error) {
324
+ this.handleError(`Failed to enable provider ${name}`, error);
325
+ }
326
+ } else if (!enabled && this.activeSession.providers.includes(name)) {
327
+ // Disable: end session on this provider
328
+ try {
329
+ provider.endSession();
330
+ this.activeSession.providers = this.activeSession.providers.filter(p => p !== name);
331
+ } catch (error) {
332
+ this.handleError(`Failed to disable provider ${name}`, error);
333
+ }
334
+ }
335
+ }
336
+
337
+ this.emit('providerToggled', { name, enabled });
338
+ }
339
+
340
+ /**
341
+ * Get current session information
342
+ */
343
+ public getCurrentSession(): any {
344
+ return this.activeSession;
345
+ }
346
+
347
+ /**
348
+ * Get analytics statistics
349
+ */
350
+ public getStats(): {
351
+ enabled: boolean;
352
+ totalProviders: number;
353
+ activeProviders: number;
354
+ currentSession: any;
355
+ providers: Array<{
356
+ name: string;
357
+ type: string;
358
+ active: boolean;
359
+ sessionInfo?: any;
360
+ }>;
361
+ } {
362
+ const providerStats = Array.from(this.providers.entries()).map(([name, provider]) => ({
363
+ name,
364
+ type: this.getProviderType(name),
365
+ active: this.activeSession?.providers.includes(name) || false,
366
+ sessionInfo: provider.getCurrentSession()
367
+ }));
368
+
369
+ return {
370
+ enabled: this.config.enabled,
371
+ totalProviders: this.providers.size,
372
+ activeProviders: this.activeSession?.providers.length || 0,
373
+ currentSession: this.activeSession,
374
+ providers: providerStats
375
+ };
376
+ }
377
+
378
+ /**
379
+ * Update configuration
380
+ */
381
+ public updateConfig(newConfig: Partial<DynamicAnalyticsConfig>): void {
382
+ this.config = { ...this.config, ...newConfig };
383
+
384
+ if (!this.config.enabled && this.activeSession) {
385
+ // Disable analytics if turned off
386
+ this.endSession();
387
+ }
388
+
389
+ this.emit('configUpdated', this.config);
390
+ }
391
+
392
+ /**
393
+ * Destroy the analytics manager
394
+ */
395
+ public destroy(): void {
396
+ // End active session
397
+ if (this.activeSession) {
398
+ this.endSession();
399
+ }
400
+
401
+ // Destroy all providers
402
+ for (const [name, provider] of this.providers) {
403
+ try {
404
+ provider.destroy();
405
+ } catch (error) {
406
+ this.handleError(`Error destroying provider ${name}`, error);
407
+ }
408
+ }
409
+
410
+ this.providers.clear();
411
+ this.removeAllListeners();
412
+ }
413
+
414
+ // Private methods
415
+
416
+ private initializeProviders(): void {
417
+ if (!this.config.enabled) {
418
+ return;
419
+ }
420
+
421
+ // Sort providers by priority
422
+ const sortedProviders = [...this.config.providers].sort((a, b) => {
423
+ return (a.priority || 0) - (b.priority || 0);
424
+ });
425
+
426
+ for (const providerConfig of sortedProviders) {
427
+ if (providerConfig.enabled) {
428
+ try {
429
+ const provider = this.createProvider(providerConfig.type, providerConfig.config);
430
+ this.providers.set(providerConfig.name, provider);
431
+ this.log(`Initialized provider ${providerConfig.name}`);
432
+ } catch (error) {
433
+ this.handleError(`Failed to initialize provider ${providerConfig.name}`, error);
434
+ }
435
+ }
436
+ }
437
+ }
438
+
439
+ private createProvider(type: AnalyticsProviderType, config: any): BaseAnalyticsProvider {
440
+ switch (type) {
441
+ case AnalyticsProviderType.PLAYER_ANALYTICS:
442
+ return new PlayerAnalyticsAdapter(config as PlayerAnalyticsConfig);
443
+
444
+ case AnalyticsProviderType.CUSTOM:
445
+ // For custom providers, expect a factory function in config
446
+ if (typeof config.factory !== 'function') {
447
+ throw new Error('Custom provider must include a factory function');
448
+ }
449
+ return config.factory(config);
450
+
451
+ default:
452
+ throw new Error(`Unsupported provider type: ${type}`);
453
+ }
454
+ }
455
+
456
+ private getProviderType(name: string): string {
457
+ const providerConfig = this.config.providers.find(p => p.name === name);
458
+ return providerConfig?.type || 'unknown';
459
+ }
460
+
461
+ private setSessionTimeout(): void {
462
+ if (this.sessionTimeout) {
463
+ clearTimeout(this.sessionTimeout);
464
+ }
465
+
466
+ const timeoutMs = (this.config.globalSettings!.sessionTimeout! * 60 * 1000);
467
+ this.sessionTimeout = setTimeout(() => {
468
+ this.log('Session timeout reached, ending session');
469
+ this.endSession();
470
+ }, timeoutMs);
471
+ }
472
+
473
+ private log(message: string): void {
474
+ if (this.config.globalSettings?.enableConsoleLogging) {
475
+ console.log(`[DynamicAnalyticsManager] ${message}`);
476
+ }
477
+ }
478
+
479
+ private handleError(message: string, error: any): void {
480
+ const errorMessage = `${message}: ${error?.message || error}`;
481
+
482
+ if (this.config.globalSettings?.enableConsoleLogging) {
483
+ console.error(`[DynamicAnalyticsManager] ${errorMessage}`);
484
+ }
485
+
486
+ if (this.config.globalSettings?.enableErrorReporting) {
487
+ this.emit('error', { message: errorMessage, error });
488
+ }
489
+ }
490
+
491
+ private generateSessionId(): string {
492
+ return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
493
+ }
494
+
495
+ private generateEventId(): string {
496
+ return `event_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
497
+ }
498
+ }
499
+
500
+ // Factory function
501
+ export function createDynamicAnalyticsManager(config: DynamicAnalyticsConfig): DynamicAnalyticsManager {
502
+ return new DynamicAnalyticsManager(config);
503
+ }
504
+
505
+ // Helper function to create player analytics provider config
506
+ export function createPlayerAnalyticsProviderConfig(
507
+ baseUrl: string,
508
+ apiKey: string,
509
+ playerId: string,
510
+ options?: {
511
+ tenantId?: string;
512
+ heartbeatInterval?: number;
513
+ batchSize?: number;
514
+ flushInterval?: number;
515
+ }
516
+ ): PlayerAnalyticsConfig {
517
+ return {
518
+ baseUrl,
519
+ apiKey,
520
+ playerId,
521
+ tenantId: options?.tenantId,
522
+ heartbeatInterval: options?.heartbeatInterval || 10,
523
+ batchSize: options?.batchSize || 10,
524
+ flushInterval: options?.flushInterval || 30
525
+ };
526
+ }