unified-video-framework 1.4.413 → 1.4.414

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 (106) hide show
  1. package/package.json +6 -1
  2. package/packages/core/src/BasePlayer.d.ts +61 -0
  3. package/packages/core/src/BasePlayer.d.ts.map +1 -0
  4. package/packages/core/src/BasePlayer.js +175 -0
  5. package/packages/core/src/BasePlayer.js.map +1 -0
  6. package/packages/core/src/VideoPlayerFactory.d.ts +8 -0
  7. package/packages/core/src/VideoPlayerFactory.d.ts.map +1 -0
  8. package/packages/core/src/VideoPlayerFactory.js +95 -0
  9. package/packages/core/src/VideoPlayerFactory.js.map +1 -0
  10. package/packages/core/src/analytics/adapters/PlayerAnalyticsAdapter.d.ts +18 -0
  11. package/packages/core/src/analytics/adapters/PlayerAnalyticsAdapter.d.ts.map +1 -0
  12. package/packages/core/src/analytics/adapters/PlayerAnalyticsAdapter.js +117 -0
  13. package/packages/core/src/analytics/adapters/PlayerAnalyticsAdapter.js.map +1 -0
  14. package/packages/core/src/analytics/core/AnalyticsProvider.d.ts +18 -0
  15. package/packages/core/src/analytics/core/AnalyticsProvider.d.ts.map +1 -0
  16. package/packages/core/src/analytics/core/AnalyticsProvider.js +99 -0
  17. package/packages/core/src/analytics/core/AnalyticsProvider.js.map +1 -0
  18. package/packages/core/src/analytics/core/DynamicAnalyticsManager.d.ts +20 -0
  19. package/packages/core/src/analytics/core/DynamicAnalyticsManager.d.ts.map +1 -0
  20. package/packages/core/src/analytics/core/DynamicAnalyticsManager.js +161 -0
  21. package/packages/core/src/analytics/core/DynamicAnalyticsManager.js.map +1 -0
  22. package/packages/core/src/analytics/core/EventBatcher.d.ts +32 -0
  23. package/packages/core/src/analytics/core/EventBatcher.d.ts.map +1 -0
  24. package/packages/core/src/analytics/core/EventBatcher.js +98 -0
  25. package/packages/core/src/analytics/core/EventBatcher.js.map +1 -0
  26. package/packages/core/src/analytics/core/PlayerAnalytics.d.ts +19 -0
  27. package/packages/core/src/analytics/core/PlayerAnalytics.d.ts.map +1 -0
  28. package/packages/core/src/analytics/core/PlayerAnalytics.js +80 -0
  29. package/packages/core/src/analytics/core/PlayerAnalytics.js.map +1 -0
  30. package/packages/core/src/analytics/index.d.ts +13 -0
  31. package/packages/core/src/analytics/index.d.ts.map +1 -0
  32. package/packages/core/src/analytics/index.js +13 -0
  33. package/packages/core/src/analytics/index.js.map +1 -0
  34. package/packages/core/src/analytics/types/AnalyticsTypes.d.ts +239 -0
  35. package/packages/core/src/analytics/types/AnalyticsTypes.d.ts.map +1 -0
  36. package/packages/core/src/analytics/types/AnalyticsTypes.js +8 -0
  37. package/packages/core/src/analytics/types/AnalyticsTypes.js.map +1 -0
  38. package/packages/core/src/analytics/utils/DeviceDetection.d.ts +27 -0
  39. package/packages/core/src/analytics/utils/DeviceDetection.d.ts.map +1 -0
  40. package/packages/core/src/analytics/utils/DeviceDetection.js +184 -0
  41. package/packages/core/src/analytics/utils/DeviceDetection.js.map +1 -0
  42. package/packages/core/src/chapter-manager.d.ts +39 -0
  43. package/packages/core/src/chapter-manager.d.ts.map +1 -0
  44. package/packages/core/src/chapter-manager.js +173 -0
  45. package/packages/core/src/chapter-manager.js.map +1 -0
  46. package/packages/core/src/index.d.ts +10 -0
  47. package/packages/core/src/index.d.ts.map +1 -0
  48. package/packages/core/src/index.js +8 -0
  49. package/packages/core/src/index.js.map +1 -0
  50. package/packages/core/src/interfaces/IVideoPlayer.d.ts +229 -0
  51. package/packages/core/src/interfaces/IVideoPlayer.d.ts.map +1 -0
  52. package/packages/core/src/interfaces/IVideoPlayer.js +2 -0
  53. package/packages/core/src/interfaces/IVideoPlayer.js.map +1 -0
  54. package/packages/core/src/interfaces.d.ts +455 -0
  55. package/packages/core/src/interfaces.d.ts.map +1 -0
  56. package/packages/core/src/interfaces.js +32 -0
  57. package/packages/core/src/interfaces.js.map +1 -0
  58. package/packages/core/src/utils/EventEmitter.d.ts +14 -0
  59. package/packages/core/src/utils/EventEmitter.d.ts.map +1 -0
  60. package/packages/core/src/utils/EventEmitter.js +55 -0
  61. package/packages/core/src/utils/EventEmitter.js.map +1 -0
  62. package/packages/web/dist/drm/DRMManager.d.ts +1 -1
  63. package/packages/web/dist/drm/DRMManager.d.ts.map +1 -1
  64. package/packages/web/dist/drm/DRMManager.js +7 -7
  65. package/packages/web/dist/drm/DRMManager.js.map +1 -1
  66. package/packages/web/dist/drm/providers/BunnyNetProvider.d.ts +1 -1
  67. package/packages/web/dist/drm/providers/BunnyNetProvider.d.ts.map +1 -1
  68. package/packages/web/dist/drm/providers/BunnyNetProvider.js +13 -13
  69. package/packages/web/dist/drm/providers/BunnyNetProvider.js.map +1 -1
  70. package/packages/web/dist/drm/providers/GenericProvider.d.ts +1 -1
  71. package/packages/web/dist/drm/providers/GenericProvider.d.ts.map +1 -1
  72. package/packages/web/dist/drm/providers/GenericProvider.js +8 -6
  73. package/packages/web/dist/drm/providers/GenericProvider.js.map +1 -1
  74. package/packages/web/dist/drm/systems/FairPlayDRM.d.ts.map +1 -1
  75. package/packages/web/dist/drm/systems/FairPlayDRM.js +2 -2
  76. package/packages/web/dist/drm/systems/FairPlayDRM.js.map +1 -1
  77. package/packages/web/dist/drm/systems/PlayReadyDRM.d.ts.map +1 -1
  78. package/packages/web/dist/drm/systems/PlayReadyDRM.js +7 -7
  79. package/packages/web/dist/drm/systems/PlayReadyDRM.js.map +1 -1
  80. package/packages/web/dist/drm/systems/WidevineDRM.d.ts.map +1 -1
  81. package/packages/web/dist/drm/systems/WidevineDRM.js +2 -2
  82. package/packages/web/dist/drm/systems/WidevineDRM.js.map +1 -1
  83. package/packages/web/dist/drm/types/BunnyNetTypes.d.ts +1 -1
  84. package/packages/web/dist/drm/types/BunnyNetTypes.d.ts.map +1 -1
  85. package/packages/web/dist/drm/types/DRMTypes.d.ts +1 -1
  86. package/packages/web/dist/drm/types/DRMTypes.d.ts.map +1 -1
  87. package/packages/web/dist/drm/types/DRMTypes.js +5 -5
  88. package/packages/web/dist/drm/types/DRMTypes.js.map +1 -1
  89. package/packages/web/dist/drm/utils/BrowserDetector.d.ts +1 -1
  90. package/packages/web/dist/drm/utils/BrowserDetector.d.ts.map +1 -1
  91. package/packages/web/dist/drm/utils/BrowserDetector.js +10 -10
  92. package/packages/web/dist/drm/utils/BrowserDetector.js.map +1 -1
  93. package/packages/web/src/drm/DRMManager.ts +214 -213
  94. package/packages/web/src/drm/index.ts +37 -37
  95. package/packages/web/src/drm/providers/BunnyNetProvider.ts +171 -170
  96. package/packages/web/src/drm/providers/GenericProvider.ts +151 -148
  97. package/packages/web/src/drm/systems/BaseDRM.ts +68 -68
  98. package/packages/web/src/drm/systems/FairPlayDRM.ts +306 -305
  99. package/packages/web/src/drm/systems/PlayReadyDRM.ts +132 -131
  100. package/packages/web/src/drm/systems/WidevineDRM.ts +106 -105
  101. package/packages/web/src/drm/types/BunnyNetTypes.ts +35 -35
  102. package/packages/web/src/drm/types/DRMTypes.ts +92 -91
  103. package/packages/web/src/drm/utils/BrowserDetector.ts +233 -232
  104. package/packages/web/src/drm/utils/CertificateManager.ts +86 -86
  105. package/packages/web/src/drm/utils/DRMErrorHandler.ts +84 -84
  106. package/packages/web/src/drm/utils/LicenseRequestHandler.ts +180 -180
@@ -1,305 +1,306 @@
1
- /**
2
- * FairPlay DRM Implementation
3
- * Implements FairPlay DRM for Safari/iOS using WebKit EME APIs
4
- */
5
-
6
- import { BaseDRM } from './BaseDRM';
7
- import { KEY_SYSTEMS, DRMErrorCode } from '../types/DRMTypes';
8
- import { DRMType } from '@unified-video/core';
9
- import { CertificateManager } from '../utils/CertificateManager';
10
- import { DRMErrorHandler } from '../utils/DRMErrorHandler';
11
-
12
- // WebKit-specific types
13
- declare global {
14
- interface HTMLVideoElement {
15
- webkitSetMediaKeys?: (mediaKeys: any) => void;
16
- webkitKeys?: any;
17
- }
18
-
19
- interface Window {
20
- WebKitMediaKeys?: any;
21
- }
22
- }
23
-
24
- export class FairPlayDRM extends BaseDRM {
25
- private certificateManager: CertificateManager;
26
- private serverCertificate: ArrayBuffer | null = null;
27
- private encryptedEventHandler: ((event: any) => Promise<void>) | null = null;
28
- private needKeyEventHandler: ((event: any) => Promise<void>) | null = null;
29
-
30
- constructor(videoElement: HTMLVideoElement, config: any, debug: boolean = false) {
31
- super(videoElement, config, debug);
32
- this.certificateManager = CertificateManager.getInstance();
33
- }
34
-
35
- async initialize(): Promise<void> {
36
- this.log('Initializing FairPlay DRM...');
37
-
38
- // Load server certificate
39
- await this.loadCertificate();
40
-
41
- // Set up encrypted event listeners
42
- this.setupEncryptedEventListeners();
43
-
44
- this.log('FairPlay DRM initialized');
45
- }
46
-
47
- getKeySystem(): string {
48
- return KEY_SYSTEMS[DRMType.FAIRPLAY][0];
49
- }
50
-
51
- getHLSConfig(): any {
52
- this.log('Generating HLS.js config for FairPlay');
53
-
54
- const certificateUrl = this.config.certificateUrl || this.config.fairplayOptions?.certificateUrl;
55
- const licenseUrl = this.config.licenseUrl || this.config.fairplayOptions?.licenseUrl;
56
-
57
- return {
58
- emeEnabled: true,
59
- drmSystems: {
60
- [this.getKeySystem()]: {
61
- licenseUrl: licenseUrl,
62
- serverCertificateUrl: certificateUrl,
63
- serverCertificate: this.serverCertificate,
64
- },
65
- },
66
- requestMediaKeySystemAccessFunc: this.createMediaKeySystemAccess.bind(this),
67
- };
68
- }
69
-
70
- getDashProtectionData(): any {
71
- this.log('Generating dash.js protection data for FairPlay');
72
-
73
- const licenseUrl = this.config.licenseUrl || this.config.fairplayOptions?.licenseUrl;
74
-
75
- return {
76
- [this.getKeySystem()]: {
77
- serverURL: licenseUrl,
78
- serverCertificate: this.serverCertificate,
79
- },
80
- };
81
- }
82
-
83
- async destroy(): Promise<void> {
84
- // Remove event listeners
85
- if (this.encryptedEventHandler) {
86
- this.videoElement.removeEventListener('encrypted', this.encryptedEventHandler as any);
87
- this.encryptedEventHandler = null;
88
- }
89
-
90
- if (this.needKeyEventHandler) {
91
- this.videoElement.removeEventListener('webkitneedkey', this.needKeyEventHandler as any);
92
- this.needKeyEventHandler = null;
93
- }
94
-
95
- await super.destroy();
96
- }
97
-
98
- private async loadCertificate(): Promise<void> {
99
- const certificateUrl = this.config.certificateUrl || this.config.fairplayOptions?.certificateUrl;
100
-
101
- if (!certificateUrl) {
102
- throw DRMErrorHandler.createError(
103
- DRMErrorCode.CONFIGURATION_ERROR,
104
- 'FairPlay certificate URL not provided',
105
- true
106
- );
107
- }
108
-
109
- this.log('Loading FairPlay certificate from:', certificateUrl);
110
-
111
- // Check cache first
112
- const cached = this.certificateManager.getCertificate(certificateUrl);
113
- if (cached) {
114
- this.log('Using cached certificate');
115
- this.serverCertificate = cached;
116
- return;
117
- }
118
-
119
- // Fetch certificate
120
- try {
121
- const response = await fetch(certificateUrl);
122
- if (!response.ok) {
123
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
124
- }
125
-
126
- this.serverCertificate = await response.arrayBuffer();
127
-
128
- // Cache for future use
129
- this.certificateManager.setCertificate(certificateUrl, this.serverCertificate);
130
-
131
- this.log('FairPlay certificate loaded, size:', this.serverCertificate.byteLength, 'bytes');
132
- } catch (error: any) {
133
- throw DRMErrorHandler.createError(
134
- DRMErrorCode.CERTIFICATE_LOAD_FAILED,
135
- `Failed to load FairPlay certificate: ${error.message}`,
136
- true,
137
- error
138
- );
139
- }
140
- }
141
-
142
- private setupEncryptedEventListeners(): void {
143
- // Modern EME encrypted event
144
- this.encryptedEventHandler = this.onEncrypted.bind(this);
145
- this.videoElement.addEventListener('encrypted', this.encryptedEventHandler as any);
146
-
147
- // Legacy WebKit needkey event
148
- this.needKeyEventHandler = this.onWebKitNeedKey.bind(this);
149
- this.videoElement.addEventListener('webkitneedkey', this.needKeyEventHandler as any);
150
-
151
- this.log('FairPlay encrypted event listeners set up');
152
- }
153
-
154
- private async onEncrypted(event: any): Promise<void> {
155
- this.log('Encrypted event received:', event);
156
-
157
- try {
158
- const initData = event.initData;
159
- const initDataType = event.initDataType;
160
-
161
- await this.createSession(initData, initDataType);
162
- } catch (error) {
163
- this.log('Error handling encrypted event:', error);
164
- throw error;
165
- }
166
- }
167
-
168
- private async onWebKitNeedKey(event: any): Promise<void> {
169
- this.log('WebKit needkey event received');
170
-
171
- try {
172
- const initData = event.initData;
173
- await this.createSession(initData, 'skd');
174
- } catch (error) {
175
- this.log('Error handling webkitneedkey event:', error);
176
- throw error;
177
- }
178
- }
179
-
180
- private async createSession(initData: ArrayBuffer, initDataType: string): Promise<void> {
181
- // Create media keys if not already created
182
- if (!this.mediaKeys) {
183
- try {
184
- const keySystemAccess = await navigator.requestMediaKeySystemAccess(
185
- this.getKeySystem(),
186
- this.getKeySystemConfiguration()
187
- );
188
- this.mediaKeys = await keySystemAccess.createMediaKeys();
189
- await this.videoElement.setMediaKeys(this.mediaKeys);
190
-
191
- this.log('FairPlay MediaKeys created and set');
192
- } catch (error: any) {
193
- throw DRMErrorHandler.createError(
194
- DRMErrorCode.MEDIA_KEYS_CREATION_FAILED,
195
- `Failed to create FairPlay MediaKeys: ${error.message}`,
196
- true,
197
- error
198
- );
199
- }
200
- }
201
-
202
- // Create session
203
- try {
204
- this.keySession = this.mediaKeys.createSession();
205
- this.log('FairPlay session created');
206
-
207
- // Handle license request
208
- this.keySession.addEventListener('message', async (event: any) => {
209
- await this.onMessage(event);
210
- });
211
-
212
- // Generate license request
213
- await this.keySession.generateRequest(initDataType, initData);
214
- } catch (error: any) {
215
- throw DRMErrorHandler.createError(
216
- DRMErrorCode.SESSION_CREATION_FAILED,
217
- `Failed to create FairPlay session: ${error.message}`,
218
- true,
219
- error
220
- );
221
- }
222
- }
223
-
224
- private async onMessage(event: any): Promise<void> {
225
- this.log('FairPlay message event:', event.messageType);
226
-
227
- try {
228
- const message = event.message;
229
-
230
- // Convert message to base64 for FairPlay license request
231
- const messageBase64 = this.arrayBufferToBase64(message);
232
-
233
- const licenseUrl = this.config.licenseUrl || this.config.fairplayOptions?.licenseUrl;
234
- if (!licenseUrl) {
235
- throw DRMErrorHandler.createError(
236
- DRMErrorCode.CONFIGURATION_ERROR,
237
- 'FairPlay license URL not provided',
238
- true
239
- );
240
- }
241
-
242
- // Request license from server
243
- const license = await this.licenseHandler.requestLicense({
244
- url: licenseUrl,
245
- method: 'POST',
246
- headers: {
247
- 'Content-Type': 'application/octet-stream',
248
- ...this.config.headers,
249
- },
250
- body: message,
251
- responseType: 'arraybuffer',
252
- });
253
-
254
- // Update session with license (CKC - Content Key Context)
255
- await this.keySession!.update(license.license);
256
-
257
- this.log('FairPlay license applied successfully');
258
- } catch (error: any) {
259
- throw DRMErrorHandler.createError(
260
- DRMErrorCode.LICENSE_REQUEST_FAILED,
261
- `Failed to process FairPlay license: ${error.message}`,
262
- true,
263
- error
264
- );
265
- }
266
- }
267
-
268
- private getKeySystemConfiguration(): MediaKeySystemConfiguration[] {
269
- return [{
270
- initDataTypes: ['sinf', 'skd'],
271
- audioCapabilities: [{
272
- contentType: 'audio/mp4; codecs="mp4a.40.2"',
273
- }],
274
- videoCapabilities: [
275
- {
276
- contentType: 'application/vnd.apple.mpegurl',
277
- robustness: 'SW_SECURE_CRYPTO',
278
- },
279
- {
280
- contentType: 'video/mp4; codecs="avc1.42E01E"',
281
- robustness: 'SW_SECURE_CRYPTO',
282
- },
283
- ],
284
- distinctiveIdentifier: 'not-allowed',
285
- persistentState: 'not-allowed',
286
- sessionTypes: ['temporary'],
287
- }];
288
- }
289
-
290
- private async createMediaKeySystemAccess(): Promise<MediaKeySystemAccess> {
291
- return navigator.requestMediaKeySystemAccess(
292
- this.getKeySystem(),
293
- this.getKeySystemConfiguration()
294
- );
295
- }
296
-
297
- private arrayBufferToBase64(buffer: ArrayBuffer): string {
298
- const bytes = new Uint8Array(buffer);
299
- let binary = '';
300
- for (let i = 0; i < bytes.byteLength; i++) {
301
- binary += String.fromCharCode(bytes[i]);
302
- }
303
- return btoa(binary);
304
- }
305
- }
1
+ /**
2
+ * FairPlay DRM Implementation
3
+ * Implements FairPlay DRM for Safari/iOS using WebKit EME APIs
4
+ */
5
+
6
+ import { BaseDRM } from './BaseDRM';
7
+ import { KEY_SYSTEMS, DRMErrorCode } from '../types/DRMTypes';
8
+ import type { DRMType } from '@unified-video/core';
9
+ import { CertificateManager } from '../utils/CertificateManager';
10
+ import { DRMErrorHandler } from '../utils/DRMErrorHandler';
11
+ import { DRMType as DRMTypeEnum } from '@unified-video/core';
12
+
13
+ // WebKit-specific types
14
+ declare global {
15
+ interface HTMLVideoElement {
16
+ webkitSetMediaKeys?: (mediaKeys: any) => void;
17
+ webkitKeys?: any;
18
+ }
19
+
20
+ interface Window {
21
+ WebKitMediaKeys?: any;
22
+ }
23
+ }
24
+
25
+ export class FairPlayDRM extends BaseDRM {
26
+ private certificateManager: CertificateManager;
27
+ private serverCertificate: ArrayBuffer | null = null;
28
+ private encryptedEventHandler: ((event: any) => Promise<void>) | null = null;
29
+ private needKeyEventHandler: ((event: any) => Promise<void>) | null = null;
30
+
31
+ constructor(videoElement: HTMLVideoElement, config: any, debug: boolean = false) {
32
+ super(videoElement, config, debug);
33
+ this.certificateManager = CertificateManager.getInstance();
34
+ }
35
+
36
+ async initialize(): Promise<void> {
37
+ this.log('Initializing FairPlay DRM...');
38
+
39
+ // Load server certificate
40
+ await this.loadCertificate();
41
+
42
+ // Set up encrypted event listeners
43
+ this.setupEncryptedEventListeners();
44
+
45
+ this.log('FairPlay DRM initialized');
46
+ }
47
+
48
+ getKeySystem(): string {
49
+ return KEY_SYSTEMS[DRMTypeEnum.FAIRPLAY][0];
50
+ }
51
+
52
+ getHLSConfig(): any {
53
+ this.log('Generating HLS.js config for FairPlay');
54
+
55
+ const certificateUrl = this.config.certificateUrl || this.config.fairplayOptions?.certificateUrl;
56
+ const licenseUrl = this.config.licenseUrl || this.config.fairplayOptions?.licenseUrl;
57
+
58
+ return {
59
+ emeEnabled: true,
60
+ drmSystems: {
61
+ [this.getKeySystem()]: {
62
+ licenseUrl: licenseUrl,
63
+ serverCertificateUrl: certificateUrl,
64
+ serverCertificate: this.serverCertificate,
65
+ },
66
+ },
67
+ requestMediaKeySystemAccessFunc: this.createMediaKeySystemAccess.bind(this),
68
+ };
69
+ }
70
+
71
+ getDashProtectionData(): any {
72
+ this.log('Generating dash.js protection data for FairPlay');
73
+
74
+ const licenseUrl = this.config.licenseUrl || this.config.fairplayOptions?.licenseUrl;
75
+
76
+ return {
77
+ [this.getKeySystem()]: {
78
+ serverURL: licenseUrl,
79
+ serverCertificate: this.serverCertificate,
80
+ },
81
+ };
82
+ }
83
+
84
+ async destroy(): Promise<void> {
85
+ // Remove event listeners
86
+ if (this.encryptedEventHandler) {
87
+ this.videoElement.removeEventListener('encrypted', this.encryptedEventHandler as any);
88
+ this.encryptedEventHandler = null;
89
+ }
90
+
91
+ if (this.needKeyEventHandler) {
92
+ this.videoElement.removeEventListener('webkitneedkey', this.needKeyEventHandler as any);
93
+ this.needKeyEventHandler = null;
94
+ }
95
+
96
+ await super.destroy();
97
+ }
98
+
99
+ private async loadCertificate(): Promise<void> {
100
+ const certificateUrl = this.config.certificateUrl || this.config.fairplayOptions?.certificateUrl;
101
+
102
+ if (!certificateUrl) {
103
+ throw DRMErrorHandler.createError(
104
+ DRMErrorCode.CONFIGURATION_ERROR,
105
+ 'FairPlay certificate URL not provided',
106
+ true
107
+ );
108
+ }
109
+
110
+ this.log('Loading FairPlay certificate from:', certificateUrl);
111
+
112
+ // Check cache first
113
+ const cached = this.certificateManager.getCertificate(certificateUrl);
114
+ if (cached) {
115
+ this.log('Using cached certificate');
116
+ this.serverCertificate = cached;
117
+ return;
118
+ }
119
+
120
+ // Fetch certificate
121
+ try {
122
+ const response = await fetch(certificateUrl);
123
+ if (!response.ok) {
124
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
125
+ }
126
+
127
+ this.serverCertificate = await response.arrayBuffer();
128
+
129
+ // Cache for future use
130
+ this.certificateManager.setCertificate(certificateUrl, this.serverCertificate);
131
+
132
+ this.log('FairPlay certificate loaded, size:', this.serverCertificate.byteLength, 'bytes');
133
+ } catch (error: any) {
134
+ throw DRMErrorHandler.createError(
135
+ DRMErrorCode.CERTIFICATE_LOAD_FAILED,
136
+ `Failed to load FairPlay certificate: ${error.message}`,
137
+ true,
138
+ error
139
+ );
140
+ }
141
+ }
142
+
143
+ private setupEncryptedEventListeners(): void {
144
+ // Modern EME encrypted event
145
+ this.encryptedEventHandler = this.onEncrypted.bind(this);
146
+ this.videoElement.addEventListener('encrypted', this.encryptedEventHandler as any);
147
+
148
+ // Legacy WebKit needkey event
149
+ this.needKeyEventHandler = this.onWebKitNeedKey.bind(this);
150
+ this.videoElement.addEventListener('webkitneedkey', this.needKeyEventHandler as any);
151
+
152
+ this.log('FairPlay encrypted event listeners set up');
153
+ }
154
+
155
+ private async onEncrypted(event: any): Promise<void> {
156
+ this.log('Encrypted event received:', event);
157
+
158
+ try {
159
+ const initData = event.initData;
160
+ const initDataType = event.initDataType;
161
+
162
+ await this.createSession(initData, initDataType);
163
+ } catch (error) {
164
+ this.log('Error handling encrypted event:', error);
165
+ throw error;
166
+ }
167
+ }
168
+
169
+ private async onWebKitNeedKey(event: any): Promise<void> {
170
+ this.log('WebKit needkey event received');
171
+
172
+ try {
173
+ const initData = event.initData;
174
+ await this.createSession(initData, 'skd');
175
+ } catch (error) {
176
+ this.log('Error handling webkitneedkey event:', error);
177
+ throw error;
178
+ }
179
+ }
180
+
181
+ private async createSession(initData: ArrayBuffer, initDataType: string): Promise<void> {
182
+ // Create media keys if not already created
183
+ if (!this.mediaKeys) {
184
+ try {
185
+ const keySystemAccess = await navigator.requestMediaKeySystemAccess(
186
+ this.getKeySystem(),
187
+ this.getKeySystemConfiguration()
188
+ );
189
+ this.mediaKeys = await keySystemAccess.createMediaKeys();
190
+ await this.videoElement.setMediaKeys(this.mediaKeys);
191
+
192
+ this.log('FairPlay MediaKeys created and set');
193
+ } catch (error: any) {
194
+ throw DRMErrorHandler.createError(
195
+ DRMErrorCode.MEDIA_KEYS_CREATION_FAILED,
196
+ `Failed to create FairPlay MediaKeys: ${error.message}`,
197
+ true,
198
+ error
199
+ );
200
+ }
201
+ }
202
+
203
+ // Create session
204
+ try {
205
+ this.keySession = this.mediaKeys.createSession();
206
+ this.log('FairPlay session created');
207
+
208
+ // Handle license request
209
+ this.keySession.addEventListener('message', async (event: any) => {
210
+ await this.onMessage(event);
211
+ });
212
+
213
+ // Generate license request
214
+ await this.keySession.generateRequest(initDataType, initData);
215
+ } catch (error: any) {
216
+ throw DRMErrorHandler.createError(
217
+ DRMErrorCode.SESSION_CREATION_FAILED,
218
+ `Failed to create FairPlay session: ${error.message}`,
219
+ true,
220
+ error
221
+ );
222
+ }
223
+ }
224
+
225
+ private async onMessage(event: any): Promise<void> {
226
+ this.log('FairPlay message event:', event.messageType);
227
+
228
+ try {
229
+ const message = event.message;
230
+
231
+ // Convert message to base64 for FairPlay license request
232
+ const messageBase64 = this.arrayBufferToBase64(message);
233
+
234
+ const licenseUrl = this.config.licenseUrl || this.config.fairplayOptions?.licenseUrl;
235
+ if (!licenseUrl) {
236
+ throw DRMErrorHandler.createError(
237
+ DRMErrorCode.CONFIGURATION_ERROR,
238
+ 'FairPlay license URL not provided',
239
+ true
240
+ );
241
+ }
242
+
243
+ // Request license from server
244
+ const license = await this.licenseHandler.requestLicense({
245
+ url: licenseUrl,
246
+ method: 'POST',
247
+ headers: {
248
+ 'Content-Type': 'application/octet-stream',
249
+ ...this.config.headers,
250
+ },
251
+ body: message,
252
+ responseType: 'arraybuffer',
253
+ });
254
+
255
+ // Update session with license (CKC - Content Key Context)
256
+ await this.keySession!.update(license.license);
257
+
258
+ this.log('FairPlay license applied successfully');
259
+ } catch (error: any) {
260
+ throw DRMErrorHandler.createError(
261
+ DRMErrorCode.LICENSE_REQUEST_FAILED,
262
+ `Failed to process FairPlay license: ${error.message}`,
263
+ true,
264
+ error
265
+ );
266
+ }
267
+ }
268
+
269
+ private getKeySystemConfiguration(): MediaKeySystemConfiguration[] {
270
+ return [{
271
+ initDataTypes: ['sinf', 'skd'],
272
+ audioCapabilities: [{
273
+ contentType: 'audio/mp4; codecs="mp4a.40.2"',
274
+ }],
275
+ videoCapabilities: [
276
+ {
277
+ contentType: 'application/vnd.apple.mpegurl',
278
+ robustness: 'SW_SECURE_CRYPTO',
279
+ },
280
+ {
281
+ contentType: 'video/mp4; codecs="avc1.42E01E"',
282
+ robustness: 'SW_SECURE_CRYPTO',
283
+ },
284
+ ],
285
+ distinctiveIdentifier: 'not-allowed',
286
+ persistentState: 'not-allowed',
287
+ sessionTypes: ['temporary'],
288
+ }];
289
+ }
290
+
291
+ private async createMediaKeySystemAccess(): Promise<MediaKeySystemAccess> {
292
+ return navigator.requestMediaKeySystemAccess(
293
+ this.getKeySystem(),
294
+ this.getKeySystemConfiguration()
295
+ );
296
+ }
297
+
298
+ private arrayBufferToBase64(buffer: ArrayBuffer): string {
299
+ const bytes = new Uint8Array(buffer);
300
+ let binary = '';
301
+ for (let i = 0; i < bytes.byteLength; i++) {
302
+ binary += String.fromCharCode(bytes[i]);
303
+ }
304
+ return btoa(binary);
305
+ }
306
+ }