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