unified-video-framework 1.4.411 → 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.
Files changed (87) hide show
  1. package/package.json +1 -1
  2. package/packages/web/dist/WebPlayer.d.ts.map +1 -1
  3. package/packages/web/dist/WebPlayer.js +62 -6
  4. package/packages/web/dist/WebPlayer.js.map +1 -1
  5. package/packages/web/dist/drm/BunnyCDNDRMProvider.d.ts +13 -0
  6. package/packages/web/dist/drm/BunnyCDNDRMProvider.d.ts.map +1 -0
  7. package/packages/web/dist/drm/BunnyCDNDRMProvider.js +65 -0
  8. package/packages/web/dist/drm/BunnyCDNDRMProvider.js.map +1 -0
  9. package/packages/web/dist/drm/DRMManager.d.ts +20 -0
  10. package/packages/web/dist/drm/DRMManager.d.ts.map +1 -0
  11. package/packages/web/dist/drm/DRMManager.js +126 -0
  12. package/packages/web/dist/drm/DRMManager.js.map +1 -0
  13. package/packages/web/dist/drm/FairPlayDRMHandler.d.ts +24 -0
  14. package/packages/web/dist/drm/FairPlayDRMHandler.d.ts.map +1 -0
  15. package/packages/web/dist/drm/FairPlayDRMHandler.js +190 -0
  16. package/packages/web/dist/drm/FairPlayDRMHandler.js.map +1 -0
  17. package/packages/web/dist/drm/WidevineDRMHandler.d.ts +21 -0
  18. package/packages/web/dist/drm/WidevineDRMHandler.d.ts.map +1 -0
  19. package/packages/web/dist/drm/WidevineDRMHandler.js +143 -0
  20. package/packages/web/dist/drm/WidevineDRMHandler.js.map +1 -0
  21. package/packages/web/dist/drm/index.d.ts +15 -0
  22. package/packages/web/dist/drm/index.d.ts.map +1 -0
  23. package/packages/web/dist/drm/index.js +13 -0
  24. package/packages/web/dist/drm/index.js.map +1 -0
  25. package/packages/web/dist/drm/providers/BunnyNetProvider.d.ts +19 -0
  26. package/packages/web/dist/drm/providers/BunnyNetProvider.d.ts.map +1 -0
  27. package/packages/web/dist/drm/providers/BunnyNetProvider.js +112 -0
  28. package/packages/web/dist/drm/providers/BunnyNetProvider.js.map +1 -0
  29. package/packages/web/dist/drm/providers/GenericProvider.d.ts +30 -0
  30. package/packages/web/dist/drm/providers/GenericProvider.d.ts.map +1 -0
  31. package/packages/web/dist/drm/providers/GenericProvider.js +102 -0
  32. package/packages/web/dist/drm/providers/GenericProvider.js.map +1 -0
  33. package/packages/web/dist/drm/systems/BaseDRM.d.ts +18 -0
  34. package/packages/web/dist/drm/systems/BaseDRM.d.ts.map +1 -0
  35. package/packages/web/dist/drm/systems/BaseDRM.js +29 -0
  36. package/packages/web/dist/drm/systems/BaseDRM.js.map +1 -0
  37. package/packages/web/dist/drm/systems/FairPlayDRM.d.ts +32 -0
  38. package/packages/web/dist/drm/systems/FairPlayDRM.d.ts.map +1 -0
  39. package/packages/web/dist/drm/systems/FairPlayDRM.js +198 -0
  40. package/packages/web/dist/drm/systems/FairPlayDRM.js.map +1 -0
  41. package/packages/web/dist/drm/systems/PlayReadyDRM.d.ts +9 -0
  42. package/packages/web/dist/drm/systems/PlayReadyDRM.d.ts.map +1 -0
  43. package/packages/web/dist/drm/systems/PlayReadyDRM.js +92 -0
  44. package/packages/web/dist/drm/systems/PlayReadyDRM.js.map +1 -0
  45. package/packages/web/dist/drm/systems/WidevineDRM.d.ts +9 -0
  46. package/packages/web/dist/drm/systems/WidevineDRM.d.ts.map +1 -0
  47. package/packages/web/dist/drm/systems/WidevineDRM.js +73 -0
  48. package/packages/web/dist/drm/systems/WidevineDRM.js.map +1 -0
  49. package/packages/web/dist/drm/types/BunnyNetTypes.d.ts +20 -0
  50. package/packages/web/dist/drm/types/BunnyNetTypes.d.ts.map +1 -0
  51. package/packages/web/dist/drm/types/BunnyNetTypes.js +8 -0
  52. package/packages/web/dist/drm/types/BunnyNetTypes.js.map +1 -0
  53. package/packages/web/dist/drm/types/DRMTypes.d.ts +59 -0
  54. package/packages/web/dist/drm/types/DRMTypes.d.ts.map +1 -0
  55. package/packages/web/dist/drm/types/DRMTypes.js +21 -0
  56. package/packages/web/dist/drm/types/DRMTypes.js.map +1 -0
  57. package/packages/web/dist/drm/utils/BrowserDetector.d.ts +20 -0
  58. package/packages/web/dist/drm/utils/BrowserDetector.d.ts.map +1 -0
  59. package/packages/web/dist/drm/utils/BrowserDetector.js +168 -0
  60. package/packages/web/dist/drm/utils/BrowserDetector.js.map +1 -0
  61. package/packages/web/dist/drm/utils/CertificateManager.d.ts +15 -0
  62. package/packages/web/dist/drm/utils/CertificateManager.d.ts.map +1 -0
  63. package/packages/web/dist/drm/utils/CertificateManager.js +46 -0
  64. package/packages/web/dist/drm/utils/CertificateManager.js.map +1 -0
  65. package/packages/web/dist/drm/utils/DRMErrorHandler.d.ts +7 -0
  66. package/packages/web/dist/drm/utils/DRMErrorHandler.d.ts.map +1 -0
  67. package/packages/web/dist/drm/utils/DRMErrorHandler.js +49 -0
  68. package/packages/web/dist/drm/utils/DRMErrorHandler.js.map +1 -0
  69. package/packages/web/dist/drm/utils/LicenseRequestHandler.d.ts +15 -0
  70. package/packages/web/dist/drm/utils/LicenseRequestHandler.d.ts.map +1 -0
  71. package/packages/web/dist/drm/utils/LicenseRequestHandler.js +110 -0
  72. package/packages/web/dist/drm/utils/LicenseRequestHandler.js.map +1 -0
  73. package/packages/web/src/WebPlayer.ts +80 -12
  74. package/packages/web/src/drm/DRMManager.ts +213 -0
  75. package/packages/web/src/drm/index.ts +37 -0
  76. package/packages/web/src/drm/providers/BunnyNetProvider.ts +170 -0
  77. package/packages/web/src/drm/providers/GenericProvider.ts +148 -0
  78. package/packages/web/src/drm/systems/BaseDRM.ts +68 -0
  79. package/packages/web/src/drm/systems/FairPlayDRM.ts +305 -0
  80. package/packages/web/src/drm/systems/PlayReadyDRM.ts +131 -0
  81. package/packages/web/src/drm/systems/WidevineDRM.ts +105 -0
  82. package/packages/web/src/drm/types/BunnyNetTypes.ts +35 -0
  83. package/packages/web/src/drm/types/DRMTypes.ts +91 -0
  84. package/packages/web/src/drm/utils/BrowserDetector.ts +232 -0
  85. package/packages/web/src/drm/utils/CertificateManager.ts +86 -0
  86. package/packages/web/src/drm/utils/DRMErrorHandler.ts +84 -0
  87. package/packages/web/src/drm/utils/LicenseRequestHandler.ts +180 -0
@@ -0,0 +1,131 @@
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 { DRMType } from '@unified-video/core';
9
+ import { DRMErrorHandler } from '../utils/DRMErrorHandler';
10
+
11
+ export class PlayReadyDRM extends BaseDRM {
12
+ async initialize(): Promise<void> {
13
+ this.log('Initializing PlayReady DRM...');
14
+
15
+ // PlayReady is primarily configured through HLS.js and dash.js
16
+ // Basic EME setup for validation
17
+ try {
18
+ const keySystemAccess = await navigator.requestMediaKeySystemAccess(
19
+ this.getKeySystem(),
20
+ this.getKeySystemConfiguration()
21
+ );
22
+
23
+ this.log('PlayReady key system access granted');
24
+ this.mediaKeys = await keySystemAccess.createMediaKeys();
25
+
26
+ // For HLS.js and dash.js, they handle the actual EME setup
27
+ // We just validate that the system is available
28
+
29
+ } catch (error: any) {
30
+ // Try fallback key system
31
+ try {
32
+ const fallbackKeySystem = KEY_SYSTEMS[DRMType.PLAYREADY][1];
33
+ const keySystemAccess = await navigator.requestMediaKeySystemAccess(
34
+ fallbackKeySystem,
35
+ this.getKeySystemConfiguration()
36
+ );
37
+
38
+ this.log('PlayReady fallback key system access granted');
39
+ this.mediaKeys = await keySystemAccess.createMediaKeys();
40
+ } catch (fallbackError: any) {
41
+ this.log('PlayReady initialization failed:', fallbackError);
42
+ throw DRMErrorHandler.createError(
43
+ DRMErrorCode.KEY_SYSTEM_ACCESS_DENIED,
44
+ 'Failed to access PlayReady DRM system',
45
+ true,
46
+ fallbackError
47
+ );
48
+ }
49
+ }
50
+
51
+ this.log('PlayReady DRM initialized');
52
+ }
53
+
54
+ getKeySystem(): string {
55
+ return KEY_SYSTEMS[DRMType.PLAYREADY][0];
56
+ }
57
+
58
+ getHLSConfig(): any {
59
+ this.log('Generating HLS.js config for PlayReady');
60
+
61
+ return {
62
+ emeEnabled: true,
63
+ drmSystems: {
64
+ [this.getKeySystem()]: {
65
+ licenseUrl: this.config.licenseUrl || this.config.playreadyOptions?.licenseUrl,
66
+ customData: this.config.customData || this.config.playreadyOptions?.customData,
67
+ },
68
+ // Add fallback key system
69
+ [KEY_SYSTEMS[DRMType.PLAYREADY][1]]: {
70
+ licenseUrl: this.config.licenseUrl || this.config.playreadyOptions?.licenseUrl,
71
+ customData: this.config.customData || this.config.playreadyOptions?.customData,
72
+ },
73
+ },
74
+ requestMediaKeySystemAccessFunc: navigator.requestMediaKeySystemAccess.bind(navigator),
75
+ };
76
+ }
77
+
78
+ getDashProtectionData(): any {
79
+ this.log('Generating dash.js protection data for PlayReady');
80
+
81
+ const licenseUrl = this.config.licenseUrl || this.config.playreadyOptions?.licenseUrl;
82
+
83
+ const protectionData: any = {
84
+ [this.getKeySystem()]: {
85
+ serverURL: licenseUrl,
86
+ },
87
+ // Add fallback key system
88
+ [KEY_SYSTEMS[DRMType.PLAYREADY][1]]: {
89
+ serverURL: licenseUrl,
90
+ },
91
+ };
92
+
93
+ // Add custom headers if provided
94
+ if (this.config.headers && Object.keys(this.config.headers).length > 0) {
95
+ protectionData[this.getKeySystem()].httpRequestHeaders = this.config.headers;
96
+ protectionData[KEY_SYSTEMS[DRMType.PLAYREADY][1]].httpRequestHeaders = this.config.headers;
97
+ }
98
+
99
+ // Add custom data if provided
100
+ const customData = this.config.customData || this.config.playreadyOptions?.customData;
101
+ if (customData) {
102
+ protectionData[this.getKeySystem()].customData = customData;
103
+ protectionData[KEY_SYSTEMS[DRMType.PLAYREADY][1]].customData = customData;
104
+ }
105
+
106
+ return protectionData;
107
+ }
108
+
109
+ private getKeySystemConfiguration(): MediaKeySystemConfiguration[] {
110
+ return [{
111
+ initDataTypes: ['cenc'],
112
+ audioCapabilities: [{
113
+ contentType: 'audio/mp4; codecs="mp4a.40.2"',
114
+ robustness: 'SW_SECURE_CRYPTO',
115
+ }],
116
+ videoCapabilities: [
117
+ {
118
+ contentType: 'video/mp4; codecs="avc1.42E01E"',
119
+ robustness: 'SW_SECURE_CRYPTO',
120
+ },
121
+ {
122
+ contentType: 'video/mp4; codecs="avc1.4d401f"',
123
+ robustness: 'SW_SECURE_CRYPTO',
124
+ },
125
+ ],
126
+ distinctiveIdentifier: 'not-allowed',
127
+ persistentState: 'not-allowed',
128
+ sessionTypes: ['temporary'],
129
+ }];
130
+ }
131
+ }
@@ -0,0 +1,105 @@
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 { DRMType } from '@unified-video/core';
9
+ import { DRMErrorHandler } from '../utils/DRMErrorHandler';
10
+
11
+ export class WidevineDRM extends BaseDRM {
12
+ async initialize(): Promise<void> {
13
+ this.log('Initializing Widevine DRM...');
14
+
15
+ // Widevine is primarily configured through HLS.js and dash.js
16
+ // Basic EME setup for validation
17
+ try {
18
+ const keySystemAccess = await navigator.requestMediaKeySystemAccess(
19
+ this.getKeySystem(),
20
+ this.getKeySystemConfiguration()
21
+ );
22
+
23
+ this.log('Widevine key system access granted');
24
+ this.mediaKeys = await keySystemAccess.createMediaKeys();
25
+
26
+ // For HLS.js and dash.js, they handle the actual EME setup
27
+ // We just validate that the system is available
28
+
29
+ } catch (error: any) {
30
+ this.log('Widevine initialization failed:', error);
31
+ throw DRMErrorHandler.createError(
32
+ DRMErrorCode.KEY_SYSTEM_ACCESS_DENIED,
33
+ 'Failed to access Widevine DRM system',
34
+ true,
35
+ error
36
+ );
37
+ }
38
+
39
+ this.log('Widevine DRM initialized');
40
+ }
41
+
42
+ getKeySystem(): string {
43
+ return KEY_SYSTEMS[DRMType.WIDEVINE][0];
44
+ }
45
+
46
+ getHLSConfig(): any {
47
+ this.log('Generating HLS.js config for Widevine');
48
+
49
+ return {
50
+ emeEnabled: true,
51
+ drmSystems: {
52
+ [this.getKeySystem()]: {
53
+ licenseUrl: this.config.licenseUrl || this.config.widevineOptions?.licenseUrl,
54
+ serverCertificate: this.config.widevineOptions?.serverCertificate,
55
+ },
56
+ },
57
+ requestMediaKeySystemAccessFunc: navigator.requestMediaKeySystemAccess.bind(navigator),
58
+ };
59
+ }
60
+
61
+ getDashProtectionData(): any {
62
+ this.log('Generating dash.js protection data for Widevine');
63
+
64
+ const protectionData: any = {
65
+ [this.getKeySystem()]: {
66
+ serverURL: this.config.licenseUrl || this.config.widevineOptions?.licenseUrl,
67
+ },
68
+ };
69
+
70
+ // Add custom headers if provided
71
+ if (this.config.headers && Object.keys(this.config.headers).length > 0) {
72
+ protectionData[this.getKeySystem()].httpRequestHeaders = this.config.headers;
73
+ }
74
+
75
+ // Add server certificate if provided
76
+ if (this.config.widevineOptions?.serverCertificate) {
77
+ protectionData[this.getKeySystem()].serverCertificate = this.config.widevineOptions.serverCertificate;
78
+ }
79
+
80
+ return protectionData;
81
+ }
82
+
83
+ private getKeySystemConfiguration(): MediaKeySystemConfiguration[] {
84
+ return [{
85
+ initDataTypes: ['cenc'],
86
+ audioCapabilities: [{
87
+ contentType: 'audio/mp4; codecs="mp4a.40.2"',
88
+ robustness: 'SW_SECURE_CRYPTO',
89
+ }],
90
+ videoCapabilities: [
91
+ {
92
+ contentType: 'video/mp4; codecs="avc1.42E01E"',
93
+ robustness: 'SW_SECURE_CRYPTO',
94
+ },
95
+ {
96
+ contentType: 'video/mp4; codecs="avc1.4d401f"',
97
+ robustness: 'SW_SECURE_CRYPTO',
98
+ },
99
+ ],
100
+ distinctiveIdentifier: 'not-allowed',
101
+ persistentState: 'not-allowed',
102
+ sessionTypes: ['temporary'],
103
+ }];
104
+ }
105
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Bunny.net DRM Type Definitions
3
+ * Type definitions specific to Bunny.net MediaCage DRM integration
4
+ */
5
+
6
+ import { 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
+ };
@@ -0,0 +1,91 @@
1
+ /**
2
+ * DRM Type Definitions
3
+ * Core type definitions and enums for DRM functionality
4
+ */
5
+
6
+ import { DRMConfig, DRMType } from '@unified-video/core';
7
+
8
+ // Extended DRM configuration for internal use
9
+ export interface ExtendedDRMConfig extends DRMConfig {
10
+ // Provider-specific settings
11
+ provider?: 'bunny' | 'generic' | string;
12
+
13
+ // Library/Video IDs for Bunny.net
14
+ libraryId?: string;
15
+ videoId?: string;
16
+
17
+ // Advanced options
18
+ retryConfig?: {
19
+ maxRetries: number;
20
+ retryDelay: number;
21
+ };
22
+
23
+ // Debugging
24
+ debug?: boolean;
25
+ }
26
+
27
+ // Browser DRM capabilities
28
+ export interface DRMCapabilities {
29
+ widevine: boolean;
30
+ fairplay: boolean;
31
+ playready: boolean;
32
+ supportedType: DRMType | null;
33
+ }
34
+
35
+ // License request/response types
36
+ export interface LicenseRequest {
37
+ url: string;
38
+ method: 'POST' | 'GET';
39
+ headers: Record<string, string>;
40
+ body: ArrayBuffer | string;
41
+ responseType: 'arraybuffer' | 'text' | 'json';
42
+ }
43
+
44
+ export interface LicenseResponse {
45
+ license: ArrayBuffer;
46
+ metadata?: any;
47
+ }
48
+
49
+ // EME event types
50
+ export interface EMEMessageEvent {
51
+ messageType: string;
52
+ message: ArrayBuffer;
53
+ }
54
+
55
+ // DRM initialization result
56
+ export interface DRMInitResult {
57
+ success: boolean;
58
+ drmType: DRMType;
59
+ keySystem: string;
60
+ error?: DRMError;
61
+ }
62
+
63
+ // DRM-specific errors
64
+ export interface DRMError {
65
+ code: DRMErrorCode;
66
+ message: string;
67
+ fatal: boolean;
68
+ details?: any;
69
+ systemError?: any;
70
+ }
71
+
72
+ export enum DRMErrorCode {
73
+ UNSUPPORTED_BROWSER = 'DRM_UNSUPPORTED_BROWSER',
74
+ CERTIFICATE_LOAD_FAILED = 'DRM_CERTIFICATE_LOAD_FAILED',
75
+ LICENSE_REQUEST_FAILED = 'DRM_LICENSE_REQUEST_FAILED',
76
+ KEY_SYSTEM_ACCESS_DENIED = 'DRM_KEY_SYSTEM_ACCESS_DENIED',
77
+ MEDIA_KEYS_CREATION_FAILED = 'DRM_MEDIA_KEYS_CREATION_FAILED',
78
+ SESSION_CREATION_FAILED = 'DRM_SESSION_CREATION_FAILED',
79
+ LICENSE_INVALID = 'DRM_LICENSE_INVALID',
80
+ CONFIGURATION_ERROR = 'DRM_CONFIGURATION_ERROR',
81
+ NETWORK_ERROR = 'DRM_NETWORK_ERROR',
82
+ TIMEOUT = 'DRM_TIMEOUT',
83
+ }
84
+
85
+ // Key system mappings
86
+ export const KEY_SYSTEMS: Record<DRMType, string[]> = {
87
+ [DRMType.WIDEVINE]: ['com.widevine.alpha'],
88
+ [DRMType.PLAYREADY]: ['com.microsoft.playready', 'com.microsoft.playready.recommendation'],
89
+ [DRMType.FAIRPLAY]: ['com.apple.fps.1_0', 'com.apple.fps'],
90
+ [DRMType.CLEARKEY]: ['webkit-org.w3.clearkey', 'org.w3.clearkey'],
91
+ };
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Browser DRM Detector
3
+ * Detects browser capabilities and determines optimal DRM system
4
+ */
5
+
6
+ import { DRMType } from '@unified-video/core';
7
+ import { DRMCapabilities, KEY_SYSTEMS } from '../types/DRMTypes';
8
+
9
+ export class BrowserDetector {
10
+ private static instance: BrowserDetector;
11
+ private capabilities: DRMCapabilities | null = null;
12
+
13
+ private constructor() {}
14
+
15
+ static getInstance(): BrowserDetector {
16
+ if (!BrowserDetector.instance) {
17
+ BrowserDetector.instance = new BrowserDetector();
18
+ }
19
+ return BrowserDetector.instance;
20
+ }
21
+
22
+ /**
23
+ * Detect DRM capabilities of current browser
24
+ */
25
+ async detectCapabilities(): Promise<DRMCapabilities> {
26
+ if (this.capabilities) {
27
+ return this.capabilities;
28
+ }
29
+
30
+ const capabilities: DRMCapabilities = {
31
+ widevine: false,
32
+ fairplay: false,
33
+ playready: false,
34
+ supportedType: null,
35
+ };
36
+
37
+ // Check for FairPlay (Safari/iOS) - highest priority on Apple devices
38
+ if (this.isSafari() || this.isWebKitBased()) {
39
+ capabilities.fairplay = await this.checkFairPlaySupport();
40
+ if (capabilities.fairplay) {
41
+ capabilities.supportedType = DRMType.FAIRPLAY;
42
+ }
43
+ }
44
+
45
+ // Check for Widevine (Chrome, Firefox, Edge)
46
+ if (!capabilities.supportedType) {
47
+ capabilities.widevine = await this.checkWidevineSupport();
48
+ if (capabilities.widevine) {
49
+ capabilities.supportedType = DRMType.WIDEVINE;
50
+ }
51
+ }
52
+
53
+ // Check for PlayReady (Edge, IE)
54
+ if (!capabilities.supportedType && this.isEdge()) {
55
+ capabilities.playready = await this.checkPlayReadySupport();
56
+ if (capabilities.playready) {
57
+ capabilities.supportedType = DRMType.PLAYREADY;
58
+ }
59
+ }
60
+
61
+ this.capabilities = capabilities;
62
+ return capabilities;
63
+ }
64
+
65
+ /**
66
+ * Get recommended DRM type for current browser
67
+ */
68
+ async getRecommendedDRMType(): Promise<DRMType | null> {
69
+ const caps = await this.detectCapabilities();
70
+ return caps.supportedType;
71
+ }
72
+
73
+ /**
74
+ * Check if specific DRM type is supported
75
+ */
76
+ async isDRMTypeSupported(drmType: DRMType): Promise<boolean> {
77
+ const caps = await this.detectCapabilities();
78
+ switch (drmType) {
79
+ case DRMType.WIDEVINE:
80
+ return caps.widevine;
81
+ case DRMType.FAIRPLAY:
82
+ return caps.fairplay;
83
+ case DRMType.PLAYREADY:
84
+ return caps.playready;
85
+ default:
86
+ return false;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Check if browser is Safari
92
+ */
93
+ private isSafari(): boolean {
94
+ const ua = navigator.userAgent.toLowerCase();
95
+ return ua.includes('safari') && !ua.includes('chrome') && !ua.includes('chromium');
96
+ }
97
+
98
+ /**
99
+ * Check if browser is WebKit-based
100
+ */
101
+ private isWebKitBased(): boolean {
102
+ return 'WebKitMediaKeys' in window;
103
+ }
104
+
105
+ /**
106
+ * Check if browser is Edge
107
+ */
108
+ private isEdge(): boolean {
109
+ const ua = navigator.userAgent.toLowerCase();
110
+ return ua.includes('edg/');
111
+ }
112
+
113
+ /**
114
+ * Check FairPlay support
115
+ */
116
+ private async checkFairPlaySupport(): Promise<boolean> {
117
+ try {
118
+ // Check for WebKit-specific FairPlay API
119
+ if ('WebKitMediaKeys' in window) {
120
+ return true;
121
+ }
122
+
123
+ // Check via EME API
124
+ if (navigator.requestMediaKeySystemAccess) {
125
+ const config: MediaKeySystemConfiguration[] = [{
126
+ initDataTypes: ['sinf', 'skd'],
127
+ videoCapabilities: [{ contentType: 'application/vnd.apple.mpegurl' }],
128
+ }];
129
+
130
+ for (const keySystem of KEY_SYSTEMS[DRMType.FAIRPLAY]) {
131
+ try {
132
+ await navigator.requestMediaKeySystemAccess(keySystem, config);
133
+ return true;
134
+ } catch (e) {
135
+ // Try next key system
136
+ continue;
137
+ }
138
+ }
139
+ }
140
+
141
+ return false;
142
+ } catch {
143
+ return false;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Check Widevine support
149
+ */
150
+ private async checkWidevineSupport(): Promise<boolean> {
151
+ try {
152
+ if (!navigator.requestMediaKeySystemAccess) {
153
+ return false;
154
+ }
155
+
156
+ const config: MediaKeySystemConfiguration[] = [{
157
+ initDataTypes: ['cenc'],
158
+ videoCapabilities: [{
159
+ contentType: 'video/mp4; codecs="avc1.42E01E"',
160
+ }],
161
+ }];
162
+
163
+ for (const keySystem of KEY_SYSTEMS[DRMType.WIDEVINE]) {
164
+ try {
165
+ await navigator.requestMediaKeySystemAccess(keySystem, config);
166
+ return true;
167
+ } catch (e) {
168
+ // Try next key system
169
+ continue;
170
+ }
171
+ }
172
+
173
+ return false;
174
+ } catch {
175
+ return false;
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Check PlayReady support
181
+ */
182
+ private async checkPlayReadySupport(): Promise<boolean> {
183
+ try {
184
+ if (!navigator.requestMediaKeySystemAccess) {
185
+ return false;
186
+ }
187
+
188
+ const config: MediaKeySystemConfiguration[] = [{
189
+ initDataTypes: ['cenc'],
190
+ videoCapabilities: [{
191
+ contentType: 'video/mp4; codecs="avc1.42E01E"',
192
+ }],
193
+ }];
194
+
195
+ for (const keySystem of KEY_SYSTEMS[DRMType.PLAYREADY]) {
196
+ try {
197
+ await navigator.requestMediaKeySystemAccess(keySystem, config);
198
+ return true;
199
+ } catch (e) {
200
+ // Try next key system
201
+ continue;
202
+ }
203
+ }
204
+
205
+ return false;
206
+ } catch {
207
+ return false;
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Get user-friendly browser name
213
+ */
214
+ getBrowserName(): string {
215
+ const ua = navigator.userAgent.toLowerCase();
216
+
217
+ if (ua.includes('edg/')) return 'Edge';
218
+ if (ua.includes('chrome') || ua.includes('chromium')) return 'Chrome';
219
+ if (ua.includes('firefox')) return 'Firefox';
220
+ if (ua.includes('safari')) return 'Safari';
221
+ if (ua.includes('opera') || ua.includes('opr/')) return 'Opera';
222
+
223
+ return 'Unknown';
224
+ }
225
+
226
+ /**
227
+ * Reset cached capabilities (useful for testing)
228
+ */
229
+ resetCapabilities(): void {
230
+ this.capabilities = null;
231
+ }
232
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Certificate Manager
3
+ * Manages FairPlay DRM certificates with in-memory caching
4
+ */
5
+
6
+ export class CertificateManager {
7
+ private static instance: CertificateManager;
8
+ private cache: Map<string, ArrayBuffer>;
9
+ private maxCacheSize: number = 10; // Max number of certificates to cache
10
+
11
+ private constructor() {
12
+ this.cache = new Map();
13
+ }
14
+
15
+ static getInstance(): CertificateManager {
16
+ if (!CertificateManager.instance) {
17
+ CertificateManager.instance = new CertificateManager();
18
+ }
19
+ return CertificateManager.instance;
20
+ }
21
+
22
+ /**
23
+ * Get certificate from cache
24
+ */
25
+ getCertificate(url: string): ArrayBuffer | null {
26
+ return this.cache.get(url) || null;
27
+ }
28
+
29
+ /**
30
+ * Store certificate in cache
31
+ */
32
+ setCertificate(url: string, certificate: ArrayBuffer): void {
33
+ // Implement LRU eviction if cache is full
34
+ if (this.cache.size >= this.maxCacheSize && !this.cache.has(url)) {
35
+ const firstKey = this.cache.keys().next().value;
36
+ if (firstKey) {
37
+ this.cache.delete(firstKey);
38
+ }
39
+ }
40
+
41
+ this.cache.set(url, certificate);
42
+ }
43
+
44
+ /**
45
+ * Check if certificate is cached
46
+ */
47
+ hasCertificate(url: string): boolean {
48
+ return this.cache.has(url);
49
+ }
50
+
51
+ /**
52
+ * Clear all cached certificates
53
+ */
54
+ clearCache(): void {
55
+ this.cache.clear();
56
+ }
57
+
58
+ /**
59
+ * Remove specific certificate from cache
60
+ */
61
+ removeCertificate(url: string): boolean {
62
+ return this.cache.delete(url);
63
+ }
64
+
65
+ /**
66
+ * Get cache size
67
+ */
68
+ getCacheSize(): number {
69
+ return this.cache.size;
70
+ }
71
+
72
+ /**
73
+ * Set max cache size
74
+ */
75
+ setMaxCacheSize(size: number): void {
76
+ this.maxCacheSize = size;
77
+
78
+ // Evict old entries if current size exceeds new max
79
+ while (this.cache.size > this.maxCacheSize) {
80
+ const firstKey = this.cache.keys().next().value;
81
+ if (firstKey) {
82
+ this.cache.delete(firstKey);
83
+ }
84
+ }
85
+ }
86
+ }