unified-video-framework 1.4.412 → 1.4.413
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/interfaces/IVideoPlayer.d.ts +0 -22
- package/packages/core/dist/interfaces/IVideoPlayer.d.ts.map +1 -1
- package/packages/core/src/interfaces/IVideoPlayer.ts +0 -27
- package/packages/web/dist/WebPlayer.d.ts +0 -4
- package/packages/web/dist/WebPlayer.d.ts.map +1 -1
- package/packages/web/dist/WebPlayer.js +62 -73
- package/packages/web/dist/WebPlayer.js.map +1 -1
- package/packages/web/dist/drm/DRMManager.d.ts +16 -9
- package/packages/web/dist/drm/DRMManager.d.ts.map +1 -1
- package/packages/web/dist/drm/DRMManager.js +109 -42
- package/packages/web/dist/drm/DRMManager.js.map +1 -1
- package/packages/web/dist/drm/index.d.ts +15 -0
- package/packages/web/dist/drm/index.d.ts.map +1 -0
- package/packages/web/dist/drm/index.js +13 -0
- package/packages/web/dist/drm/index.js.map +1 -0
- package/packages/web/dist/drm/providers/BunnyNetProvider.d.ts +19 -0
- package/packages/web/dist/drm/providers/BunnyNetProvider.d.ts.map +1 -0
- package/packages/web/dist/drm/providers/BunnyNetProvider.js +112 -0
- package/packages/web/dist/drm/providers/BunnyNetProvider.js.map +1 -0
- package/packages/web/dist/drm/providers/GenericProvider.d.ts +30 -0
- package/packages/web/dist/drm/providers/GenericProvider.d.ts.map +1 -0
- package/packages/web/dist/drm/providers/GenericProvider.js +102 -0
- package/packages/web/dist/drm/providers/GenericProvider.js.map +1 -0
- package/packages/web/dist/drm/systems/BaseDRM.d.ts +18 -0
- package/packages/web/dist/drm/systems/BaseDRM.d.ts.map +1 -0
- package/packages/web/dist/drm/systems/BaseDRM.js +29 -0
- package/packages/web/dist/drm/systems/BaseDRM.js.map +1 -0
- package/packages/web/dist/drm/systems/FairPlayDRM.d.ts +32 -0
- package/packages/web/dist/drm/systems/FairPlayDRM.d.ts.map +1 -0
- package/packages/web/dist/drm/systems/FairPlayDRM.js +198 -0
- package/packages/web/dist/drm/systems/FairPlayDRM.js.map +1 -0
- package/packages/web/dist/drm/systems/PlayReadyDRM.d.ts +9 -0
- package/packages/web/dist/drm/systems/PlayReadyDRM.d.ts.map +1 -0
- package/packages/web/dist/drm/systems/PlayReadyDRM.js +92 -0
- package/packages/web/dist/drm/systems/PlayReadyDRM.js.map +1 -0
- package/packages/web/dist/drm/systems/WidevineDRM.d.ts +9 -0
- package/packages/web/dist/drm/systems/WidevineDRM.d.ts.map +1 -0
- package/packages/web/dist/drm/systems/WidevineDRM.js +73 -0
- package/packages/web/dist/drm/systems/WidevineDRM.js.map +1 -0
- package/packages/web/dist/drm/types/BunnyNetTypes.d.ts +20 -0
- package/packages/web/dist/drm/types/BunnyNetTypes.d.ts.map +1 -0
- package/packages/web/dist/drm/types/BunnyNetTypes.js +8 -0
- package/packages/web/dist/drm/types/BunnyNetTypes.js.map +1 -0
- package/packages/web/dist/drm/types/DRMTypes.d.ts +56 -49
- package/packages/web/dist/drm/types/DRMTypes.d.ts.map +1 -1
- package/packages/web/dist/drm/types/DRMTypes.js +19 -13
- package/packages/web/dist/drm/types/DRMTypes.js.map +1 -1
- package/packages/web/dist/drm/utils/BrowserDetector.d.ts +20 -0
- package/packages/web/dist/drm/utils/BrowserDetector.d.ts.map +1 -0
- package/packages/web/dist/drm/utils/BrowserDetector.js +168 -0
- package/packages/web/dist/drm/utils/BrowserDetector.js.map +1 -0
- package/packages/web/dist/drm/utils/CertificateManager.d.ts +15 -0
- package/packages/web/dist/drm/utils/CertificateManager.d.ts.map +1 -0
- package/packages/web/dist/drm/utils/CertificateManager.js +46 -0
- package/packages/web/dist/drm/utils/CertificateManager.js.map +1 -0
- package/packages/web/dist/drm/utils/DRMErrorHandler.d.ts +7 -0
- package/packages/web/dist/drm/utils/DRMErrorHandler.d.ts.map +1 -0
- package/packages/web/dist/drm/utils/DRMErrorHandler.js +49 -0
- package/packages/web/dist/drm/utils/DRMErrorHandler.js.map +1 -0
- package/packages/web/dist/drm/utils/LicenseRequestHandler.d.ts +15 -0
- package/packages/web/dist/drm/utils/LicenseRequestHandler.d.ts.map +1 -0
- package/packages/web/dist/drm/utils/LicenseRequestHandler.js +110 -0
- package/packages/web/dist/drm/utils/LicenseRequestHandler.js.map +1 -0
- package/packages/web/dist/react/WebPlayerView.d.ts +1 -18
- 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 +79 -106
- package/packages/web/src/drm/DRMManager.ts +172 -58
- package/packages/web/src/drm/index.ts +37 -0
- package/packages/web/src/drm/providers/BunnyNetProvider.ts +170 -0
- package/packages/web/src/drm/providers/GenericProvider.ts +148 -0
- package/packages/web/src/drm/systems/BaseDRM.ts +68 -0
- package/packages/web/src/drm/systems/FairPlayDRM.ts +305 -0
- package/packages/web/src/drm/systems/PlayReadyDRM.ts +131 -0
- package/packages/web/src/drm/systems/WidevineDRM.ts +105 -0
- package/packages/web/src/drm/types/BunnyNetTypes.ts +35 -0
- package/packages/web/src/drm/types/DRMTypes.ts +70 -76
- package/packages/web/src/drm/utils/BrowserDetector.ts +232 -0
- package/packages/web/src/drm/utils/CertificateManager.ts +86 -0
- package/packages/web/src/drm/utils/DRMErrorHandler.ts +84 -0
- package/packages/web/src/drm/utils/LicenseRequestHandler.ts +180 -0
- package/packages/web/src/react/WebPlayerView.tsx +1 -28
- package/packages/web/src/drm/BunnyCDNDRMProvider.ts +0 -104
- package/packages/web/src/drm/FairPlayDRMHandler.ts +0 -322
- package/packages/web/src/drm/WidevineDRMHandler.ts +0 -246
|
@@ -1,99 +1,213 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DRM Manager
|
|
3
|
-
* Main orchestrator for
|
|
3
|
+
* Main orchestrator for all DRM systems
|
|
4
|
+
* Provides a unified interface for HLS.js, dash.js, and native playback
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { DRMType, DRMConfig } from '@unified-video/core';
|
|
8
|
+
import { ExtendedDRMConfig, DRMInitResult, DRMError, DRMErrorCode, DRMCapabilities } from './types/DRMTypes';
|
|
9
|
+
import { BrowserDetector } from './utils/BrowserDetector';
|
|
10
|
+
import { BunnyNetProvider } from './providers/BunnyNetProvider';
|
|
11
|
+
import { WidevineDRM } from './systems/WidevineDRM';
|
|
12
|
+
import { FairPlayDRM } from './systems/FairPlayDRM';
|
|
13
|
+
import { PlayReadyDRM } from './systems/PlayReadyDRM';
|
|
14
|
+
import { BaseDRM } from './systems/BaseDRM';
|
|
15
|
+
import { DRMErrorHandler } from './utils/DRMErrorHandler';
|
|
9
16
|
|
|
10
17
|
export class DRMManager {
|
|
11
|
-
private
|
|
12
|
-
private config:
|
|
18
|
+
private videoElement: HTMLVideoElement;
|
|
19
|
+
private config: ExtendedDRMConfig;
|
|
20
|
+
private drmSystem: BaseDRM | null = null;
|
|
21
|
+
private browserDetector: BrowserDetector;
|
|
22
|
+
private debug: boolean = false;
|
|
13
23
|
|
|
14
|
-
constructor(config:
|
|
15
|
-
this.
|
|
24
|
+
constructor(videoElement: HTMLVideoElement, config: DRMConfig, debug: boolean = false) {
|
|
25
|
+
this.videoElement = videoElement;
|
|
26
|
+
this.config = this.normalizeConfig(config);
|
|
27
|
+
this.debug = debug || this.config.debug || false;
|
|
28
|
+
this.browserDetector = BrowserDetector.getInstance();
|
|
16
29
|
}
|
|
17
30
|
|
|
18
31
|
/**
|
|
19
|
-
*
|
|
32
|
+
* Initialize DRM system based on browser capabilities
|
|
20
33
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
async initialize(): Promise<DRMInitResult> {
|
|
35
|
+
try {
|
|
36
|
+
this.log('Initializing DRM system...');
|
|
37
|
+
|
|
38
|
+
// Detect browser capabilities
|
|
39
|
+
const capabilities = await this.browserDetector.detectCapabilities();
|
|
40
|
+
this.log('Browser DRM capabilities:', capabilities);
|
|
41
|
+
|
|
42
|
+
// Determine which DRM system to use
|
|
43
|
+
const drmType = this.selectDRMType(capabilities);
|
|
44
|
+
if (!drmType) {
|
|
45
|
+
const browserName = this.browserDetector.getBrowserName();
|
|
46
|
+
throw DRMErrorHandler.createError(
|
|
47
|
+
DRMErrorCode.UNSUPPORTED_BROWSER,
|
|
48
|
+
`No supported DRM system found in ${browserName}. Please use Chrome, Safari, Firefox, or Edge.`,
|
|
49
|
+
true,
|
|
50
|
+
{ browser: browserName, capabilities }
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.log(`Selected DRM type: ${drmType}`);
|
|
55
|
+
|
|
56
|
+
// Create appropriate DRM system
|
|
57
|
+
this.drmSystem = this.createDRMSystem(drmType);
|
|
58
|
+
|
|
59
|
+
// Initialize the DRM system
|
|
60
|
+
await this.drmSystem.initialize();
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
drmType,
|
|
65
|
+
keySystem: this.drmSystem.getKeySystem(),
|
|
66
|
+
};
|
|
67
|
+
} catch (error: any) {
|
|
68
|
+
this.log('DRM initialization failed:', error);
|
|
69
|
+
|
|
70
|
+
const drmError = error.code && error.code.startsWith('DRM_')
|
|
71
|
+
? error
|
|
72
|
+
: DRMErrorHandler.createError(
|
|
73
|
+
DRMErrorCode.CONFIGURATION_ERROR,
|
|
74
|
+
error.message || 'DRM initialization failed',
|
|
75
|
+
true,
|
|
76
|
+
error
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
success: false,
|
|
81
|
+
drmType: this.config.type,
|
|
82
|
+
keySystem: '',
|
|
83
|
+
error: drmError,
|
|
84
|
+
};
|
|
31
85
|
}
|
|
32
86
|
}
|
|
33
87
|
|
|
34
88
|
/**
|
|
35
|
-
*
|
|
89
|
+
* Get HLS.js configuration for DRM
|
|
36
90
|
*/
|
|
37
|
-
|
|
38
|
-
|
|
91
|
+
getHLSConfig(): any {
|
|
92
|
+
if (!this.drmSystem) {
|
|
93
|
+
throw new Error('DRM system not initialized. Call initialize() first.');
|
|
94
|
+
}
|
|
39
95
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
this.handler = this.detectDRMHandler();
|
|
43
|
-
|
|
44
|
-
// Check if DRM is supported
|
|
45
|
-
if (!this.handler.isSupported()) {
|
|
46
|
-
throw new DRMError(
|
|
47
|
-
'DRM is not supported in this browser',
|
|
48
|
-
'DRM_NOT_SUPPORTED'
|
|
49
|
-
);
|
|
50
|
-
}
|
|
96
|
+
return this.drmSystem.getHLSConfig();
|
|
97
|
+
}
|
|
51
98
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Get dash.js protection data for DRM
|
|
101
|
+
*/
|
|
102
|
+
getDashProtectionData(): any {
|
|
103
|
+
if (!this.drmSystem) {
|
|
104
|
+
throw new Error('DRM system not initialized. Call initialize() first.');
|
|
105
|
+
}
|
|
55
106
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
107
|
+
return this.drmSystem.getDashProtectionData();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Cleanup DRM resources
|
|
112
|
+
*/
|
|
113
|
+
async destroy(): Promise<void> {
|
|
114
|
+
if (this.drmSystem) {
|
|
115
|
+
this.log('Destroying DRM system');
|
|
116
|
+
await this.drmSystem.destroy();
|
|
117
|
+
this.drmSystem = null;
|
|
59
118
|
}
|
|
60
119
|
}
|
|
61
120
|
|
|
62
121
|
/**
|
|
63
|
-
*
|
|
122
|
+
* Select appropriate DRM type based on config and capabilities
|
|
64
123
|
*/
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
124
|
+
private selectDRMType(capabilities: DRMCapabilities): DRMType | null {
|
|
125
|
+
// If config specifies a type and it's supported, use it
|
|
126
|
+
if (this.config.type) {
|
|
127
|
+
const typeSupported = this.isDRMTypeSupported(this.config.type, capabilities);
|
|
128
|
+
if (typeSupported) {
|
|
129
|
+
return this.config.type;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
this.log(`Requested DRM type ${this.config.type} not supported, falling back to browser default`);
|
|
68
133
|
}
|
|
69
134
|
|
|
70
|
-
|
|
71
|
-
|
|
135
|
+
// Otherwise, use browser's preferred DRM system
|
|
136
|
+
return capabilities.supportedType;
|
|
72
137
|
}
|
|
73
138
|
|
|
74
139
|
/**
|
|
75
|
-
* Check if DRM is
|
|
140
|
+
* Check if specific DRM type is supported
|
|
76
141
|
*/
|
|
77
|
-
|
|
78
|
-
|
|
142
|
+
private isDRMTypeSupported(drmType: DRMType, capabilities: DRMCapabilities): boolean {
|
|
143
|
+
switch (drmType) {
|
|
144
|
+
case DRMType.WIDEVINE:
|
|
145
|
+
return capabilities.widevine;
|
|
146
|
+
case DRMType.FAIRPLAY:
|
|
147
|
+
return capabilities.fairplay;
|
|
148
|
+
case DRMType.PLAYREADY:
|
|
149
|
+
return capabilities.playready;
|
|
150
|
+
default:
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
79
153
|
}
|
|
80
154
|
|
|
81
155
|
/**
|
|
82
|
-
*
|
|
156
|
+
* Create DRM system instance
|
|
83
157
|
*/
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
158
|
+
private createDRMSystem(drmType: DRMType): BaseDRM {
|
|
159
|
+
switch (drmType) {
|
|
160
|
+
case DRMType.FAIRPLAY:
|
|
161
|
+
return new FairPlayDRM(this.videoElement, this.config, this.debug);
|
|
162
|
+
|
|
163
|
+
case DRMType.WIDEVINE:
|
|
164
|
+
return new WidevineDRM(this.videoElement, this.config, this.debug);
|
|
165
|
+
|
|
166
|
+
case DRMType.PLAYREADY:
|
|
167
|
+
return new PlayReadyDRM(this.videoElement, this.config, this.debug);
|
|
168
|
+
|
|
169
|
+
default:
|
|
170
|
+
throw DRMErrorHandler.createError(
|
|
171
|
+
DRMErrorCode.CONFIGURATION_ERROR,
|
|
172
|
+
`Unsupported DRM type: ${drmType}`,
|
|
173
|
+
true
|
|
174
|
+
);
|
|
175
|
+
}
|
|
87
176
|
}
|
|
88
177
|
|
|
89
178
|
/**
|
|
90
|
-
*
|
|
179
|
+
* Normalize and enrich DRM config
|
|
180
|
+
*/
|
|
181
|
+
private normalizeConfig(config: DRMConfig): ExtendedDRMConfig {
|
|
182
|
+
const extended = { ...config } as ExtendedDRMConfig;
|
|
183
|
+
|
|
184
|
+
// Auto-detect Bunny.net and enhance config
|
|
185
|
+
if (BunnyNetProvider.isBunnyNetConfig(config)) {
|
|
186
|
+
extended.provider = 'bunny';
|
|
187
|
+
|
|
188
|
+
// Extract IDs if available
|
|
189
|
+
const ids = BunnyNetProvider.extractIdsFromUrl(config.licenseUrl);
|
|
190
|
+
if (ids.libraryId) extended.libraryId = ids.libraryId;
|
|
191
|
+
if (ids.videoId) extended.videoId = ids.videoId;
|
|
192
|
+
|
|
193
|
+
this.log('Detected Bunny.net DRM configuration');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Set defaults
|
|
197
|
+
extended.retryConfig = extended.retryConfig || {
|
|
198
|
+
maxRetries: 3,
|
|
199
|
+
retryDelay: 1000,
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
return extended;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Debug logging
|
|
91
207
|
*/
|
|
92
|
-
|
|
93
|
-
if (this.
|
|
94
|
-
console.log('[
|
|
95
|
-
this.handler.destroy();
|
|
96
|
-
this.handler = null;
|
|
208
|
+
private log(...args: any[]): void {
|
|
209
|
+
if (this.debug) {
|
|
210
|
+
console.log('[DRMManager]', ...args);
|
|
97
211
|
}
|
|
98
212
|
}
|
|
99
213
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DRM System - Public API
|
|
3
|
+
* Exports for the DRM system
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Main DRM Manager
|
|
7
|
+
export { DRMManager } from './DRMManager';
|
|
8
|
+
|
|
9
|
+
// Providers
|
|
10
|
+
export { BunnyNetProvider } from './providers/BunnyNetProvider';
|
|
11
|
+
export { GenericProvider } from './providers/GenericProvider';
|
|
12
|
+
|
|
13
|
+
// Types
|
|
14
|
+
export type {
|
|
15
|
+
ExtendedDRMConfig,
|
|
16
|
+
DRMCapabilities,
|
|
17
|
+
LicenseRequest,
|
|
18
|
+
LicenseResponse,
|
|
19
|
+
DRMInitResult,
|
|
20
|
+
DRMError,
|
|
21
|
+
} from './types/DRMTypes';
|
|
22
|
+
|
|
23
|
+
export { DRMErrorCode, KEY_SYSTEMS } from './types/DRMTypes';
|
|
24
|
+
|
|
25
|
+
export type { BunnyNetConfig, BunnyNetEndpoints } from './types/BunnyNetTypes';
|
|
26
|
+
export { BUNNY_NET_BASE_URL, BUNNY_NET_ENDPOINTS } from './types/BunnyNetTypes';
|
|
27
|
+
|
|
28
|
+
// Utilities
|
|
29
|
+
export { BrowserDetector } from './utils/BrowserDetector';
|
|
30
|
+
export { DRMErrorHandler } from './utils/DRMErrorHandler';
|
|
31
|
+
export { CertificateManager } from './utils/CertificateManager';
|
|
32
|
+
|
|
33
|
+
// DRM Systems (for advanced usage)
|
|
34
|
+
export { BaseDRM } from './systems/BaseDRM';
|
|
35
|
+
export { WidevineDRM } from './systems/WidevineDRM';
|
|
36
|
+
export { FairPlayDRM } from './systems/FairPlayDRM';
|
|
37
|
+
export { PlayReadyDRM } from './systems/PlayReadyDRM';
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bunny.net DRM Provider
|
|
3
|
+
* Helper utilities for Bunny.net MediaCage DRM integration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DRMType, DRMConfig } from '@unified-video/core';
|
|
7
|
+
import { ExtendedDRMConfig } from '../types/DRMTypes';
|
|
8
|
+
import { BunnyNetConfig, BUNNY_NET_ENDPOINTS } from '../types/BunnyNetTypes';
|
|
9
|
+
|
|
10
|
+
export class BunnyNetProvider {
|
|
11
|
+
/**
|
|
12
|
+
* Generate complete DRM config from Bunny.net credentials
|
|
13
|
+
*/
|
|
14
|
+
static createDRMConfig(config: BunnyNetConfig): ExtendedDRMConfig {
|
|
15
|
+
const { libraryId, videoId, drmType = DRMType.WIDEVINE } = config;
|
|
16
|
+
|
|
17
|
+
const drmConfig: ExtendedDRMConfig = {
|
|
18
|
+
type: drmType,
|
|
19
|
+
licenseUrl: this.getLicenseUrl(drmType, libraryId, videoId),
|
|
20
|
+
provider: 'bunny',
|
|
21
|
+
libraryId,
|
|
22
|
+
videoId,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Add FairPlay-specific settings
|
|
26
|
+
if (drmType === DRMType.FAIRPLAY) {
|
|
27
|
+
drmConfig.certificateUrl = this.getCertificateUrl(libraryId);
|
|
28
|
+
drmConfig.fairplayOptions = {
|
|
29
|
+
certificateUrl: this.getCertificateUrl(libraryId),
|
|
30
|
+
licenseUrl: this.getLicenseUrl(drmType, libraryId, videoId),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Add Widevine-specific settings
|
|
35
|
+
if (drmType === DRMType.WIDEVINE) {
|
|
36
|
+
drmConfig.widevineOptions = {
|
|
37
|
+
licenseUrl: this.getLicenseUrl(drmType, libraryId, videoId),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Add PlayReady-specific settings
|
|
42
|
+
if (drmType === DRMType.PLAYREADY) {
|
|
43
|
+
drmConfig.playreadyOptions = {
|
|
44
|
+
licenseUrl: this.getLicenseUrl(drmType, libraryId, videoId),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return drmConfig;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get license URL for specific DRM type
|
|
53
|
+
*/
|
|
54
|
+
static getLicenseUrl(drmType: DRMType, libraryId: string, videoId: string): string {
|
|
55
|
+
switch (drmType) {
|
|
56
|
+
case DRMType.WIDEVINE:
|
|
57
|
+
return BUNNY_NET_ENDPOINTS.WIDEVINE_LICENSE(libraryId, videoId);
|
|
58
|
+
|
|
59
|
+
case DRMType.FAIRPLAY:
|
|
60
|
+
return BUNNY_NET_ENDPOINTS.FAIRPLAY_LICENSE(libraryId, videoId);
|
|
61
|
+
|
|
62
|
+
case DRMType.PLAYREADY:
|
|
63
|
+
return BUNNY_NET_ENDPOINTS.PLAYREADY_LICENSE(libraryId, videoId);
|
|
64
|
+
|
|
65
|
+
default:
|
|
66
|
+
throw new Error(`Unsupported DRM type for Bunny.net: ${drmType}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get certificate URL (FairPlay only)
|
|
72
|
+
*/
|
|
73
|
+
static getCertificateUrl(libraryId: string): string {
|
|
74
|
+
return BUNNY_NET_ENDPOINTS.FAIRPLAY_CERTIFICATE(libraryId);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Validate Bunny.net DRM configuration
|
|
79
|
+
*/
|
|
80
|
+
static validateConfig(config: BunnyNetConfig): { valid: boolean; error?: string } {
|
|
81
|
+
if (!config.libraryId || !config.videoId) {
|
|
82
|
+
return {
|
|
83
|
+
valid: false,
|
|
84
|
+
error: 'Bunny.net DRM requires libraryId and videoId',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!/^\d+$/.test(config.libraryId)) {
|
|
89
|
+
return {
|
|
90
|
+
valid: false,
|
|
91
|
+
error: 'libraryId must be a numeric string',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return { valid: true };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Auto-detect if a DRM config is for Bunny.net
|
|
100
|
+
*/
|
|
101
|
+
static isBunnyNetConfig(config: DRMConfig): boolean {
|
|
102
|
+
return !!(
|
|
103
|
+
config.licenseUrl?.includes('bunnycdn.com') ||
|
|
104
|
+
config.certificateUrl?.includes('bunnycdn.com') ||
|
|
105
|
+
config.fairplayOptions?.licenseUrl?.includes('bunnycdn.com') ||
|
|
106
|
+
config.fairplayOptions?.certificateUrl?.includes('bunnycdn.com') ||
|
|
107
|
+
config.widevineOptions?.licenseUrl?.includes('bunnycdn.com') ||
|
|
108
|
+
config.playreadyOptions?.licenseUrl?.includes('bunnycdn.com')
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Extract library and video IDs from Bunny.net URLs
|
|
114
|
+
*/
|
|
115
|
+
static extractIdsFromUrl(url: string): { libraryId?: string; videoId?: string } {
|
|
116
|
+
if (!url) return {};
|
|
117
|
+
|
|
118
|
+
const widevineMatch = url.match(/WidevineLicense\/([^/]+)\/([^/?]+)/);
|
|
119
|
+
const fairplayLicenseMatch = url.match(/FairPlay\/([^/]+)\/license.*videoId=([^&]+)/);
|
|
120
|
+
const fairplayCertMatch = url.match(/FairPlay\/([^/]+)\/certificate/);
|
|
121
|
+
const playreadyMatch = url.match(/PlayReadyLicense\/([^/]+)\/([^/?]+)/);
|
|
122
|
+
|
|
123
|
+
if (widevineMatch) {
|
|
124
|
+
return { libraryId: widevineMatch[1], videoId: widevineMatch[2] };
|
|
125
|
+
}
|
|
126
|
+
if (fairplayLicenseMatch) {
|
|
127
|
+
return { libraryId: fairplayLicenseMatch[1], videoId: fairplayLicenseMatch[2] };
|
|
128
|
+
}
|
|
129
|
+
if (fairplayCertMatch) {
|
|
130
|
+
return { libraryId: fairplayCertMatch[1] };
|
|
131
|
+
}
|
|
132
|
+
if (playreadyMatch) {
|
|
133
|
+
return { libraryId: playreadyMatch[1], videoId: playreadyMatch[2] };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Create multi-DRM config for Bunny.net (auto-detects browser)
|
|
141
|
+
*/
|
|
142
|
+
static createMultiDRMConfig(libraryId: string, videoId: string): ExtendedDRMConfig {
|
|
143
|
+
// Primary Widevine config with FairPlay fallback
|
|
144
|
+
const config: ExtendedDRMConfig = {
|
|
145
|
+
type: DRMType.WIDEVINE,
|
|
146
|
+
licenseUrl: this.getLicenseUrl(DRMType.WIDEVINE, libraryId, videoId),
|
|
147
|
+
provider: 'bunny',
|
|
148
|
+
libraryId,
|
|
149
|
+
videoId,
|
|
150
|
+
|
|
151
|
+
// Widevine options
|
|
152
|
+
widevineOptions: {
|
|
153
|
+
licenseUrl: this.getLicenseUrl(DRMType.WIDEVINE, libraryId, videoId),
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
// FairPlay options (for Safari fallback)
|
|
157
|
+
fairplayOptions: {
|
|
158
|
+
certificateUrl: this.getCertificateUrl(libraryId),
|
|
159
|
+
licenseUrl: this.getLicenseUrl(DRMType.FAIRPLAY, libraryId, videoId),
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
// PlayReady options (for Edge fallback)
|
|
163
|
+
playreadyOptions: {
|
|
164
|
+
licenseUrl: this.getLicenseUrl(DRMType.PLAYREADY, libraryId, videoId),
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
return config;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic DRM Provider
|
|
3
|
+
* Helper utilities for generic DRM configurations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DRMType, DRMConfig } from '@unified-video/core';
|
|
7
|
+
import { ExtendedDRMConfig } from '../types/DRMTypes';
|
|
8
|
+
|
|
9
|
+
export class GenericProvider {
|
|
10
|
+
/**
|
|
11
|
+
* Create generic DRM config with sensible defaults
|
|
12
|
+
*/
|
|
13
|
+
static createDRMConfig(config: {
|
|
14
|
+
drmType: DRMType;
|
|
15
|
+
licenseUrl: string;
|
|
16
|
+
certificateUrl?: string;
|
|
17
|
+
headers?: Record<string, string>;
|
|
18
|
+
customData?: string;
|
|
19
|
+
}): ExtendedDRMConfig {
|
|
20
|
+
const drmConfig: ExtendedDRMConfig = {
|
|
21
|
+
type: config.drmType,
|
|
22
|
+
licenseUrl: config.licenseUrl,
|
|
23
|
+
certificateUrl: config.certificateUrl,
|
|
24
|
+
headers: config.headers,
|
|
25
|
+
customData: config.customData,
|
|
26
|
+
provider: 'generic',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Add type-specific options
|
|
30
|
+
switch (config.drmType) {
|
|
31
|
+
case DRMType.FAIRPLAY:
|
|
32
|
+
if (config.certificateUrl) {
|
|
33
|
+
drmConfig.fairplayOptions = {
|
|
34
|
+
certificateUrl: config.certificateUrl,
|
|
35
|
+
licenseUrl: config.licenseUrl,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
break;
|
|
39
|
+
|
|
40
|
+
case DRMType.WIDEVINE:
|
|
41
|
+
drmConfig.widevineOptions = {
|
|
42
|
+
licenseUrl: config.licenseUrl,
|
|
43
|
+
};
|
|
44
|
+
break;
|
|
45
|
+
|
|
46
|
+
case DRMType.PLAYREADY:
|
|
47
|
+
drmConfig.playreadyOptions = {
|
|
48
|
+
licenseUrl: config.licenseUrl,
|
|
49
|
+
customData: config.customData,
|
|
50
|
+
};
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return drmConfig;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Validate generic DRM configuration
|
|
59
|
+
*/
|
|
60
|
+
static validateConfig(config: DRMConfig): { valid: boolean; error?: string } {
|
|
61
|
+
if (!config.type) {
|
|
62
|
+
return {
|
|
63
|
+
valid: false,
|
|
64
|
+
error: 'DRM type is required',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!config.licenseUrl) {
|
|
69
|
+
return {
|
|
70
|
+
valid: false,
|
|
71
|
+
error: 'License URL is required',
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// FairPlay requires certificate URL
|
|
76
|
+
if (config.type === DRMType.FAIRPLAY) {
|
|
77
|
+
const certificateUrl = config.certificateUrl || config.fairplayOptions?.certificateUrl;
|
|
78
|
+
if (!certificateUrl) {
|
|
79
|
+
return {
|
|
80
|
+
valid: false,
|
|
81
|
+
error: 'FairPlay requires certificateUrl',
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Validate URL format
|
|
87
|
+
try {
|
|
88
|
+
new URL(config.licenseUrl);
|
|
89
|
+
} catch (e) {
|
|
90
|
+
return {
|
|
91
|
+
valid: false,
|
|
92
|
+
error: 'Invalid license URL format',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (config.certificateUrl) {
|
|
97
|
+
try {
|
|
98
|
+
new URL(config.certificateUrl);
|
|
99
|
+
} catch (e) {
|
|
100
|
+
return {
|
|
101
|
+
valid: false,
|
|
102
|
+
error: 'Invalid certificate URL format',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return { valid: true };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Create multi-DRM config for generic provider
|
|
112
|
+
*/
|
|
113
|
+
static createMultiDRMConfig(config: {
|
|
114
|
+
widevine: { licenseUrl: string };
|
|
115
|
+
fairplay: { licenseUrl: string; certificateUrl: string };
|
|
116
|
+
playready?: { licenseUrl: string; customData?: string };
|
|
117
|
+
headers?: Record<string, string>;
|
|
118
|
+
}): ExtendedDRMConfig {
|
|
119
|
+
// Primary Widevine config with FairPlay and PlayReady fallbacks
|
|
120
|
+
const drmConfig: ExtendedDRMConfig = {
|
|
121
|
+
type: DRMType.WIDEVINE,
|
|
122
|
+
licenseUrl: config.widevine.licenseUrl,
|
|
123
|
+
provider: 'generic',
|
|
124
|
+
headers: config.headers,
|
|
125
|
+
|
|
126
|
+
// Widevine options
|
|
127
|
+
widevineOptions: {
|
|
128
|
+
licenseUrl: config.widevine.licenseUrl,
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
// FairPlay options
|
|
132
|
+
fairplayOptions: {
|
|
133
|
+
certificateUrl: config.fairplay.certificateUrl,
|
|
134
|
+
licenseUrl: config.fairplay.licenseUrl,
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
// PlayReady options (optional)
|
|
138
|
+
playreadyOptions: config.playready
|
|
139
|
+
? {
|
|
140
|
+
licenseUrl: config.playready.licenseUrl,
|
|
141
|
+
customData: config.playready.customData,
|
|
142
|
+
}
|
|
143
|
+
: undefined,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return drmConfig;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base DRM System
|
|
3
|
+
* Abstract base class for all DRM system implementations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ExtendedDRMConfig, LicenseRequest, LicenseResponse } from '../types/DRMTypes';
|
|
7
|
+
import { LicenseRequestHandler } from '../utils/LicenseRequestHandler';
|
|
8
|
+
|
|
9
|
+
export abstract class BaseDRM {
|
|
10
|
+
protected videoElement: HTMLVideoElement;
|
|
11
|
+
protected config: ExtendedDRMConfig;
|
|
12
|
+
protected debug: boolean;
|
|
13
|
+
protected licenseHandler: LicenseRequestHandler;
|
|
14
|
+
protected mediaKeys: MediaKeys | null = null;
|
|
15
|
+
protected keySession: MediaKeySession | null = null;
|
|
16
|
+
|
|
17
|
+
constructor(videoElement: HTMLVideoElement, config: ExtendedDRMConfig, debug: boolean = false) {
|
|
18
|
+
this.videoElement = videoElement;
|
|
19
|
+
this.config = config;
|
|
20
|
+
this.debug = debug;
|
|
21
|
+
this.licenseHandler = new LicenseRequestHandler(config, debug);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Initialize DRM system
|
|
26
|
+
*/
|
|
27
|
+
abstract initialize(): Promise<void>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get key system identifier
|
|
31
|
+
*/
|
|
32
|
+
abstract getKeySystem(): string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get HLS.js configuration
|
|
36
|
+
*/
|
|
37
|
+
abstract getHLSConfig(): any;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get dash.js protection data
|
|
41
|
+
*/
|
|
42
|
+
abstract getDashProtectionData(): any;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Cleanup resources
|
|
46
|
+
*/
|
|
47
|
+
async destroy(): Promise<void> {
|
|
48
|
+
if (this.keySession) {
|
|
49
|
+
try {
|
|
50
|
+
await this.keySession.close();
|
|
51
|
+
} catch (error) {
|
|
52
|
+
this.log('Error closing key session:', error);
|
|
53
|
+
}
|
|
54
|
+
this.keySession = null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.mediaKeys = null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Debug logging
|
|
62
|
+
*/
|
|
63
|
+
protected log(...args: any[]): void {
|
|
64
|
+
if (this.debug) {
|
|
65
|
+
console.log(`[${this.constructor.name}]`, ...args);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|