unified-video-framework 1.4.381 → 1.4.383
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.d.ts +9 -2
- package/packages/core/dist/interfaces.d.ts.map +1 -1
- package/packages/core/dist/interfaces.js +0 -1
- package/packages/core/dist/interfaces.js.map +1 -1
- package/packages/core/src/interfaces.ts +14 -3
- package/packages/react-native/dist/drm/AndroidDRMProtection.js +1 -1
- package/packages/react-native/dist/drm/iOSDRMProtection.js +1 -1
- package/packages/web/dist/WebPlayer.d.ts +2 -0
- package/packages/web/dist/WebPlayer.d.ts.map +1 -1
- package/packages/web/dist/WebPlayer.js +9709 -9644
- package/packages/web/dist/WebPlayer.js.map +1 -1
- package/packages/web/dist/index.d.ts +0 -1
- package/packages/web/dist/index.d.ts.map +1 -1
- package/packages/web/dist/index.js +6 -7
- package/packages/web/dist/index.js.map +1 -1
- package/packages/web/dist/react/WebPlayerView.d.ts +3 -0
- package/packages/web/dist/react/WebPlayerView.d.ts.map +1 -1
- package/packages/web/dist/react/WebPlayerView.js.map +1 -1
- package/packages/web/dist/security/ScreenProtectionController.d.ts +31 -0
- package/packages/web/dist/security/ScreenProtectionController.d.ts.map +1 -0
- package/packages/web/dist/security/ScreenProtectionController.js +193 -0
- package/packages/web/dist/security/ScreenProtectionController.js.map +1 -0
- package/packages/web/src/WebPlayer.ts +87 -2
- package/packages/web/src/index.ts +0 -3
- package/packages/web/src/react/WebPlayerView.tsx +5 -0
- package/packages/web/src/security/ScreenProtectionController.ts +276 -0
- package/scripts/fix-imports.js +22 -12
- package/packages/core/src/interfaces/IDRMProtection.ts +0 -285
- package/packages/react-native/src/drm/AndroidDRMProtection.ts +0 -419
- package/packages/react-native/src/drm/iOSDRMProtection.ts +0 -415
- package/packages/web/src/drm/WebDRMProtection.ts +0 -596
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
} from './chapters/types/ChapterTypes';
|
|
23
23
|
import type { FlashNewsTickerConfig } from './react/types/FlashNewsTickerTypes';
|
|
24
24
|
import YouTubeExtractor from './utils/YouTubeExtractor';
|
|
25
|
+
import { ScreenProtectionController } from './security/ScreenProtectionController';
|
|
26
|
+
import type { ScreenCaptureEvent } from '../../core/dist';
|
|
25
27
|
|
|
26
28
|
// Dynamic imports for streaming libraries
|
|
27
29
|
declare global {
|
|
@@ -93,6 +95,9 @@ export class WebPlayer extends BasePlayer {
|
|
|
93
95
|
// Paywall
|
|
94
96
|
private paywallController: any = null;
|
|
95
97
|
|
|
98
|
+
// Screen protection
|
|
99
|
+
private screenProtectionController: ScreenProtectionController | null = null;
|
|
100
|
+
|
|
96
101
|
// Play/pause coordination to prevent race conditions
|
|
97
102
|
private _playPromise: Promise<void> | null = null;
|
|
98
103
|
private _deferredPause = false;
|
|
@@ -353,6 +358,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
353
358
|
this.setupFullscreenListeners();
|
|
354
359
|
this.setupUserInteractionTracking();
|
|
355
360
|
|
|
361
|
+
// Setup screen protection if enabled
|
|
362
|
+
if ((this.config as any).screenProtection === true) {
|
|
363
|
+
this.initializeScreenProtection();
|
|
364
|
+
}
|
|
365
|
+
|
|
356
366
|
// Initialize chapter manager if enabled
|
|
357
367
|
if (this.chapterConfig.enabled && this.video) {
|
|
358
368
|
this.setupChapterManager();
|
|
@@ -8110,10 +8120,27 @@ export class WebPlayer extends BasePlayer {
|
|
|
8110
8120
|
}
|
|
8111
8121
|
|
|
8112
8122
|
// Render the watermark
|
|
8113
|
-
|
|
8123
|
+
// If screen protection is active, render multiple watermarks
|
|
8124
|
+
if ((this.config as any).screenProtection && this.screenProtectionController) {
|
|
8125
|
+
const positions = [
|
|
8126
|
+
{ x: 20, y: 40 },
|
|
8127
|
+
{ x: Math.max(20, this.watermarkCanvas!.width - 200), y: 40 },
|
|
8128
|
+
{ x: 20, y: Math.max(40, this.watermarkCanvas!.height - 60) },
|
|
8129
|
+
{ x: Math.max(20, this.watermarkCanvas!.width - 200), y: Math.max(40, this.watermarkCanvas!.height - 60) }
|
|
8130
|
+
];
|
|
8131
|
+
|
|
8132
|
+
positions.forEach((pos) => {
|
|
8133
|
+
const offsetX = Math.random() * 100 - 50;
|
|
8134
|
+
const offsetY = Math.random() * 50 - 25;
|
|
8135
|
+
ctx.fillText(text, pos.x + offsetX, pos.y + offsetY);
|
|
8136
|
+
});
|
|
8137
|
+
} else {
|
|
8138
|
+
// Normal single watermark rendering
|
|
8139
|
+
ctx.fillText(text, x, y);
|
|
8140
|
+
}
|
|
8114
8141
|
ctx.restore();
|
|
8115
8142
|
|
|
8116
|
-
this.debugLog('Watermark rendered:', { text, x, y });
|
|
8143
|
+
this.debugLog('Watermark rendered:', { text, x, y, multiWatermark: !!((this.config as any).screenProtection && this.screenProtectionController) });
|
|
8117
8144
|
};
|
|
8118
8145
|
|
|
8119
8146
|
// Set up interval with configured frequency
|
|
@@ -8123,6 +8150,58 @@ export class WebPlayer extends BasePlayer {
|
|
|
8123
8150
|
this.debugLog('Watermark setup complete with update interval:', config.updateInterval + 'ms');
|
|
8124
8151
|
}
|
|
8125
8152
|
|
|
8153
|
+
private initializeScreenProtection(): void {
|
|
8154
|
+
if (!this.video || !this.container) return;
|
|
8155
|
+
|
|
8156
|
+
console.warn(
|
|
8157
|
+
'[Unified Video Framework] Screen Protection Enabled\n' +
|
|
8158
|
+
'Note: Browsers cannot completely block screen recording. ' +
|
|
8159
|
+
'This provides detection and deterrence through watermarking and monitoring.\n' +
|
|
8160
|
+
'For DRM-level protection, use encrypted content with Widevine/FairPlay.'
|
|
8161
|
+
);
|
|
8162
|
+
|
|
8163
|
+
// Enhance watermark for protection
|
|
8164
|
+
if (!(this.config as any).watermark) {
|
|
8165
|
+
(this.config as any).watermark = {
|
|
8166
|
+
enabled: true,
|
|
8167
|
+
text: 'PROTECTED CONTENT',
|
|
8168
|
+
randomPosition: true,
|
|
8169
|
+
updateInterval: 1000 // Rapid updates
|
|
8170
|
+
};
|
|
8171
|
+
} else if ((this.config as any).watermark.enabled !== false) {
|
|
8172
|
+
// Force aggressive watermarking
|
|
8173
|
+
(this.config as any).watermark.randomPosition = true;
|
|
8174
|
+
(this.config as any).watermark.updateInterval = Math.min(
|
|
8175
|
+
(this.config as any).watermark.updateInterval || 5000,
|
|
8176
|
+
1000
|
|
8177
|
+
);
|
|
8178
|
+
}
|
|
8179
|
+
this.setupWatermark(); // Re-setup with new config
|
|
8180
|
+
|
|
8181
|
+
// Create protection controller
|
|
8182
|
+
this.screenProtectionController = new ScreenProtectionController({
|
|
8183
|
+
videoElement: this.video,
|
|
8184
|
+
containerElement: this.container,
|
|
8185
|
+
watermarkConfig: (this.config as any).watermark,
|
|
8186
|
+
onDetection: (event: ScreenCaptureEvent) => {
|
|
8187
|
+
console.warn('[Screen Protection] Detection:', event);
|
|
8188
|
+
this.emit('onScreenCaptureDetected', event);
|
|
8189
|
+
if ((this.config as any).onScreenCaptureDetected) {
|
|
8190
|
+
(this.config as any).onScreenCaptureDetected(event);
|
|
8191
|
+
}
|
|
8192
|
+
},
|
|
8193
|
+
onDevToolsDetected: () => {
|
|
8194
|
+
console.warn('[Screen Protection] DevTools detected');
|
|
8195
|
+
this.emit('onDevToolsDetected');
|
|
8196
|
+
if ((this.config as any).onDevToolsDetected) {
|
|
8197
|
+
(this.config as any).onDevToolsDetected();
|
|
8198
|
+
}
|
|
8199
|
+
}
|
|
8200
|
+
});
|
|
8201
|
+
|
|
8202
|
+
this.screenProtectionController.activate();
|
|
8203
|
+
}
|
|
8204
|
+
|
|
8126
8205
|
public setPaywallConfig(config: any) {
|
|
8127
8206
|
try {
|
|
8128
8207
|
if (!config) return;
|
|
@@ -11278,6 +11357,12 @@ export class WebPlayer extends BasePlayer {
|
|
|
11278
11357
|
this.paywallController = null;
|
|
11279
11358
|
}
|
|
11280
11359
|
|
|
11360
|
+
// Destroy screen protection controller
|
|
11361
|
+
if (this.screenProtectionController) {
|
|
11362
|
+
this.screenProtectionController.destroy();
|
|
11363
|
+
this.screenProtectionController = null;
|
|
11364
|
+
}
|
|
11365
|
+
|
|
11281
11366
|
// Destroy chapter managers
|
|
11282
11367
|
if (this.chapterManager && typeof this.chapterManager.destroy === 'function') {
|
|
11283
11368
|
this.chapterManager.destroy();
|
|
@@ -11,9 +11,6 @@ export { WebPlayer } from './WebPlayer';
|
|
|
11
11
|
export { WebPlayerView } from './react/WebPlayerView';
|
|
12
12
|
export { SecureVideoPlayer } from './SecureVideoPlayer';
|
|
13
13
|
|
|
14
|
-
// Export DRM Protection
|
|
15
|
-
export { WebDRMProtection } from './drm/WebDRMProtection';
|
|
16
|
-
|
|
17
14
|
// Export EPG (Electronic Program Guide) components
|
|
18
15
|
export * from './react/EPG';
|
|
19
16
|
|
|
@@ -432,6 +432,11 @@ export type WebPlayerViewProps = {
|
|
|
432
432
|
|
|
433
433
|
// Flash News Ticker
|
|
434
434
|
flashNewsTicker?: FlashNewsTickerConfig; // Flash news ticker configuration
|
|
435
|
+
|
|
436
|
+
// Screen Protection
|
|
437
|
+
screenProtection?: boolean; // Enable screen recording/screenshot prevention (default: false)
|
|
438
|
+
onScreenCaptureDetected?: (event: any) => void; // Called when potential screen capture detected
|
|
439
|
+
onDevToolsDetected?: () => void; // Called when DevTools opened
|
|
435
440
|
};
|
|
436
441
|
|
|
437
442
|
export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { ScreenCaptureEvent } from '@unified-video/core';
|
|
2
|
+
|
|
3
|
+
export interface ScreenProtectionOptions {
|
|
4
|
+
videoElement: HTMLVideoElement;
|
|
5
|
+
containerElement: HTMLElement;
|
|
6
|
+
watermarkConfig?: any;
|
|
7
|
+
onDetection?: (event: ScreenCaptureEvent) => void;
|
|
8
|
+
onDevToolsDetected?: () => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class ScreenProtectionController {
|
|
12
|
+
private opts: ScreenProtectionOptions;
|
|
13
|
+
private overlayElement: HTMLElement | null = null;
|
|
14
|
+
private detectionIntervals: NodeJS.Timeout[] = [];
|
|
15
|
+
private eventListeners: Array<{ target: any; event: string; handler: any }> = [];
|
|
16
|
+
private isActive: boolean = false;
|
|
17
|
+
private devToolsOpen: boolean = false;
|
|
18
|
+
private originalGetDisplayMedia: any = null;
|
|
19
|
+
|
|
20
|
+
constructor(opts: ScreenProtectionOptions) {
|
|
21
|
+
this.opts = opts;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public activate(): void {
|
|
25
|
+
if (this.isActive) return;
|
|
26
|
+
|
|
27
|
+
console.log('[ScreenProtectionController] Activating screen protection...');
|
|
28
|
+
|
|
29
|
+
// Apply video element protection
|
|
30
|
+
this.applyVideoProtection();
|
|
31
|
+
|
|
32
|
+
// Create interference overlay
|
|
33
|
+
this.createInterferenceOverlay();
|
|
34
|
+
|
|
35
|
+
// Setup behavioral detection
|
|
36
|
+
this.setupDetection();
|
|
37
|
+
|
|
38
|
+
this.isActive = true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public deactivate(): void {
|
|
42
|
+
if (!this.isActive) return;
|
|
43
|
+
|
|
44
|
+
console.log('[ScreenProtectionController] Deactivating screen protection...');
|
|
45
|
+
|
|
46
|
+
// Remove overlay
|
|
47
|
+
if (this.overlayElement && this.overlayElement.parentElement) {
|
|
48
|
+
this.overlayElement.parentElement.removeChild(this.overlayElement);
|
|
49
|
+
this.overlayElement = null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Clear all intervals
|
|
53
|
+
this.detectionIntervals.forEach(interval => clearInterval(interval));
|
|
54
|
+
this.detectionIntervals = [];
|
|
55
|
+
|
|
56
|
+
// Remove all event listeners
|
|
57
|
+
this.eventListeners.forEach(({ target, event, handler }) => {
|
|
58
|
+
target.removeEventListener(event, handler);
|
|
59
|
+
});
|
|
60
|
+
this.eventListeners = [];
|
|
61
|
+
|
|
62
|
+
// Restore original getDisplayMedia
|
|
63
|
+
if (this.originalGetDisplayMedia && navigator.mediaDevices) {
|
|
64
|
+
navigator.mediaDevices.getDisplayMedia = this.originalGetDisplayMedia;
|
|
65
|
+
this.originalGetDisplayMedia = null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.isActive = false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public destroy(): void {
|
|
72
|
+
this.deactivate();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private applyVideoProtection(): void {
|
|
76
|
+
const { videoElement, containerElement } = this.opts;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
// Prevent Picture-in-Picture
|
|
80
|
+
videoElement.disablePictureInPicture = true;
|
|
81
|
+
(videoElement as any).autoPictureInPicture = false;
|
|
82
|
+
} catch (e) {
|
|
83
|
+
console.warn('[ScreenProtection] Could not disable PiP:', e);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
// Set controlsList to hide download button (if supported)
|
|
88
|
+
if ('controlsList' in videoElement) {
|
|
89
|
+
(videoElement as any).controlsList.add('nodownload');
|
|
90
|
+
}
|
|
91
|
+
} catch (e) {
|
|
92
|
+
console.warn('[ScreenProtection] controlsList not supported:', e);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Prevent text selection
|
|
96
|
+
containerElement.style.userSelect = 'none';
|
|
97
|
+
containerElement.style.webkitUserSelect = 'none';
|
|
98
|
+
(containerElement.style as any).webkitTouchCallout = 'none';
|
|
99
|
+
|
|
100
|
+
// Prevent right-click context menu
|
|
101
|
+
const contextMenuHandler = (e: Event): boolean => {
|
|
102
|
+
if (containerElement.contains(e.target as Node)) {
|
|
103
|
+
e.preventDefault();
|
|
104
|
+
e.stopPropagation();
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
};
|
|
109
|
+
document.addEventListener('contextmenu', contextMenuHandler);
|
|
110
|
+
this.eventListeners.push({ target: document, event: 'contextmenu', handler: contextMenuHandler });
|
|
111
|
+
|
|
112
|
+
console.log('[ScreenProtection] Video protection applied');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private createInterferenceOverlay(): void {
|
|
116
|
+
const { containerElement } = this.opts;
|
|
117
|
+
|
|
118
|
+
// Create transparent overlay
|
|
119
|
+
this.overlayElement = document.createElement('div');
|
|
120
|
+
this.overlayElement.className = 'uvf-screen-protection-overlay';
|
|
121
|
+
this.overlayElement.setAttribute('aria-hidden', 'true');
|
|
122
|
+
|
|
123
|
+
// Style the overlay
|
|
124
|
+
Object.assign(this.overlayElement.style, {
|
|
125
|
+
position: 'absolute',
|
|
126
|
+
top: '0',
|
|
127
|
+
left: '0',
|
|
128
|
+
width: '100%',
|
|
129
|
+
height: '100%',
|
|
130
|
+
pointerEvents: 'none', // Don't block user interactions
|
|
131
|
+
zIndex: '3', // Between video (1) and watermark (5)
|
|
132
|
+
mixBlendMode: 'screen',
|
|
133
|
+
opacity: '0.01',
|
|
134
|
+
background: 'transparent',
|
|
135
|
+
// Add subtle noise pattern
|
|
136
|
+
backgroundImage: 'url("data:image/svg+xml,%3Csvg viewBox=\'0 0 256 256\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cfilter id=\'noise\'%3E%3CfeTurbulence type=\'fractalNoise\' baseFrequency=\'0.9\' numOctaves=\'4\' stitchTiles=\'stitch\'/%3E%3C/filter%3E%3Crect width=\'100%25\' height=\'100%25\' filter=\'url(%23noise)\' opacity=\'0.05\'/%3E%3C/svg%3E")',
|
|
137
|
+
transition: 'opacity 0.3s ease-in-out'
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Insert into container
|
|
141
|
+
const watermarkCanvas = containerElement.querySelector('.uvf-watermark-layer');
|
|
142
|
+
if (watermarkCanvas) {
|
|
143
|
+
containerElement.insertBefore(this.overlayElement, watermarkCanvas);
|
|
144
|
+
} else {
|
|
145
|
+
containerElement.appendChild(this.overlayElement);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Start overlay animation
|
|
149
|
+
this.animateOverlay();
|
|
150
|
+
|
|
151
|
+
console.log('[ScreenProtection] Interference overlay created');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private animateOverlay(): void {
|
|
155
|
+
if (!this.overlayElement || !this.isActive) return;
|
|
156
|
+
|
|
157
|
+
// Subtle opacity pulsing
|
|
158
|
+
const baseOpacity = 0.01;
|
|
159
|
+
const variation = baseOpacity * 0.5;
|
|
160
|
+
const newOpacity = baseOpacity + (Math.random() * variation - variation / 2);
|
|
161
|
+
|
|
162
|
+
this.overlayElement.style.opacity = newOpacity.toString();
|
|
163
|
+
|
|
164
|
+
// Continue animation
|
|
165
|
+
setTimeout(() => this.animateOverlay(), 3000 + Math.random() * 2000);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private setupDetection(): void {
|
|
169
|
+
// DevTools detection
|
|
170
|
+
this.detectDevTools();
|
|
171
|
+
|
|
172
|
+
// Focus loss detection
|
|
173
|
+
this.detectFocusLoss();
|
|
174
|
+
|
|
175
|
+
// Visibility change detection
|
|
176
|
+
this.detectVisibilityChange();
|
|
177
|
+
|
|
178
|
+
// Screen Capture API detection
|
|
179
|
+
this.detectScreenCaptureAPI();
|
|
180
|
+
|
|
181
|
+
console.log('[ScreenProtection] Detection mechanisms active');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private detectDevTools(): void {
|
|
185
|
+
const threshold = 160;
|
|
186
|
+
let previousState = false;
|
|
187
|
+
|
|
188
|
+
const checkDevTools = () => {
|
|
189
|
+
if (!this.isActive) return;
|
|
190
|
+
|
|
191
|
+
const widthDiff = window.outerWidth - window.innerWidth;
|
|
192
|
+
const heightDiff = window.outerHeight - window.innerHeight;
|
|
193
|
+
|
|
194
|
+
const isOpen = widthDiff > threshold || heightDiff > threshold;
|
|
195
|
+
|
|
196
|
+
if (isOpen && !previousState) {
|
|
197
|
+
this.devToolsOpen = true;
|
|
198
|
+
this.handleDetection({
|
|
199
|
+
type: 'devtools',
|
|
200
|
+
timestamp: Date.now(),
|
|
201
|
+
details: { widthDiff, heightDiff }
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
if (this.opts.onDevToolsDetected) {
|
|
205
|
+
this.opts.onDevToolsDetected();
|
|
206
|
+
}
|
|
207
|
+
} else if (!isOpen && previousState) {
|
|
208
|
+
this.devToolsOpen = false;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
previousState = isOpen;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const interval = setInterval(checkDevTools, 500);
|
|
215
|
+
this.detectionIntervals.push(interval);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private detectFocusLoss(): void {
|
|
219
|
+
const blurHandler = () => {
|
|
220
|
+
this.handleDetection({
|
|
221
|
+
type: 'focus-loss',
|
|
222
|
+
timestamp: Date.now(),
|
|
223
|
+
details: { documentHidden: document.hidden }
|
|
224
|
+
});
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
window.addEventListener('blur', blurHandler);
|
|
228
|
+
this.eventListeners.push({ target: window, event: 'blur', handler: blurHandler });
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private detectVisibilityChange(): void {
|
|
232
|
+
const visibilityHandler = () => {
|
|
233
|
+
if (document.hidden) {
|
|
234
|
+
this.handleDetection({
|
|
235
|
+
type: 'visibility-change',
|
|
236
|
+
timestamp: Date.now(),
|
|
237
|
+
details: { visibilityState: document.visibilityState }
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
document.addEventListener('visibilitychange', visibilityHandler);
|
|
243
|
+
this.eventListeners.push({ target: document, event: 'visibilitychange', handler: visibilityHandler });
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private detectScreenCaptureAPI(): void {
|
|
247
|
+
// Monkey-patch getDisplayMedia if it exists
|
|
248
|
+
if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
|
|
249
|
+
this.originalGetDisplayMedia = navigator.mediaDevices.getDisplayMedia.bind(
|
|
250
|
+
navigator.mediaDevices
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
const self = this;
|
|
254
|
+
navigator.mediaDevices.getDisplayMedia = async function (...args: any[]) {
|
|
255
|
+
// Detected screen capture attempt
|
|
256
|
+
self.handleDetection({
|
|
257
|
+
type: 'screen-capture-api',
|
|
258
|
+
timestamp: Date.now(),
|
|
259
|
+
details: { api: 'getDisplayMedia', args: args.length }
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Still allow the call to proceed (can't block it)
|
|
263
|
+
return self.originalGetDisplayMedia(...args);
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private handleDetection(event: ScreenCaptureEvent): void {
|
|
269
|
+
console.warn('[ScreenProtection] Detection event:', event);
|
|
270
|
+
|
|
271
|
+
// Trigger callback
|
|
272
|
+
if (this.opts.onDetection) {
|
|
273
|
+
this.opts.onDetection(event);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
package/scripts/fix-imports.js
CHANGED
|
@@ -10,17 +10,22 @@ const path = require('path');
|
|
|
10
10
|
|
|
11
11
|
function fixImports(filePath) {
|
|
12
12
|
let content = fs.readFileSync(filePath, 'utf8');
|
|
13
|
-
|
|
14
|
-
//
|
|
13
|
+
|
|
14
|
+
// Replace @unified-video/core imports with relative paths
|
|
15
15
|
if (filePath.includes(path.join('packages', 'web', 'dist'))) {
|
|
16
|
-
//
|
|
16
|
+
// Fix CommonJS require statements
|
|
17
|
+
content = content.replace(
|
|
18
|
+
/require\(["']@unified-video\/core["']\)/g,
|
|
19
|
+
'require("../../core/dist")'
|
|
20
|
+
);
|
|
21
|
+
// Fix ES module import statements
|
|
17
22
|
content = content.replace(
|
|
18
|
-
/from\s+["']
|
|
19
|
-
'from "../../core/dist/index"'
|
|
23
|
+
/from\s+["']@unified-video\/core["']/g,
|
|
24
|
+
'from "../../core/dist/index.js"'
|
|
20
25
|
);
|
|
21
26
|
content = content.replace(
|
|
22
|
-
/import\s+["']
|
|
23
|
-
'import "../../core/dist/index"'
|
|
27
|
+
/import\s+["']@unified-video\/core["']/g,
|
|
28
|
+
'import "../../core/dist/index.js"'
|
|
24
29
|
);
|
|
25
30
|
|
|
26
31
|
// Fix relative imports within the same package to include .js extension
|
|
@@ -38,14 +43,19 @@ function fixImports(filePath) {
|
|
|
38
43
|
}
|
|
39
44
|
);
|
|
40
45
|
} else if (filePath.includes(path.join('packages', 'react-native', 'dist'))) {
|
|
41
|
-
//
|
|
46
|
+
// Fix CommonJS require statements
|
|
47
|
+
content = content.replace(
|
|
48
|
+
/require\(["']@unified-video\/core["']\)/g,
|
|
49
|
+
'require("../../core/dist")'
|
|
50
|
+
);
|
|
51
|
+
// Fix ES module import statements
|
|
42
52
|
content = content.replace(
|
|
43
|
-
/from\s+["']
|
|
44
|
-
'from "../../core/dist/index"'
|
|
53
|
+
/from\s+["']@unified-video\/core["']/g,
|
|
54
|
+
'from "../../core/dist/index.js"'
|
|
45
55
|
);
|
|
46
56
|
content = content.replace(
|
|
47
|
-
/import\s+["']
|
|
48
|
-
'import "../../core/dist/index"'
|
|
57
|
+
/import\s+["']@unified-video\/core["']/g,
|
|
58
|
+
'import "../../core/dist/index.js"'
|
|
49
59
|
);
|
|
50
60
|
|
|
51
61
|
// Fix relative imports within the same package to include .js extension
|