unified-video-framework 1.4.373 → 1.4.374
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/web/dist/WebPlayer.d.ts.map +1 -1
- package/packages/web/dist/WebPlayer.js +21 -3
- package/packages/web/dist/WebPlayer.js.map +1 -1
- package/packages/web/dist/ads/GoogleAdsManager.d.ts.map +1 -1
- package/packages/web/dist/ads/GoogleAdsManager.js +20 -3
- package/packages/web/dist/ads/GoogleAdsManager.js.map +1 -1
- package/packages/web/dist/react/WebPlayerView.d.ts.map +1 -1
- package/packages/web/dist/react/WebPlayerView.js +79 -16
- package/packages/web/dist/react/WebPlayerView.js.map +1 -1
- package/packages/web/src/WebPlayer.ts +21 -3
- package/packages/web/src/ads/GoogleAdsManager.ts +31 -5
- package/packages/web/src/react/WebPlayerView.tsx +113 -39
|
@@ -166,10 +166,13 @@ export class GoogleAdsManager {
|
|
|
166
166
|
const companionAdSlots = this.config.companionAdSlots.map(slot => {
|
|
167
167
|
return new google.ima.CompanionAdSelectionSettings();
|
|
168
168
|
});
|
|
169
|
-
adsRequest.setAdWillAutoPlay(true);
|
|
170
|
-
adsRequest.setAdWillPlayMuted(false);
|
|
171
169
|
}
|
|
172
170
|
|
|
171
|
+
// Chrome autoplay policy: ads must start muted for autoplay to work
|
|
172
|
+
// User can unmute after ad starts
|
|
173
|
+
adsRequest.setAdWillAutoPlay(true);
|
|
174
|
+
adsRequest.setAdWillPlayMuted(true); // Start muted for Chrome compatibility
|
|
175
|
+
|
|
173
176
|
// Request ads
|
|
174
177
|
this.adsLoader.requestAds(adsRequest);
|
|
175
178
|
} catch (error) {
|
|
@@ -199,7 +202,8 @@ export class GoogleAdsManager {
|
|
|
199
202
|
const adsRenderingSettings = new google.ima.AdsRenderingSettings();
|
|
200
203
|
adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;
|
|
201
204
|
adsRenderingSettings.enablePreloading = true;
|
|
202
|
-
|
|
205
|
+
// Start muted for Chrome autoplay compatibility - user can unmute via button
|
|
206
|
+
adsRenderingSettings.mute = this.video.muted;
|
|
203
207
|
|
|
204
208
|
// Get the ads manager
|
|
205
209
|
this.adsManager = event.getAdsManager(this.video, adsRenderingSettings);
|
|
@@ -260,6 +264,15 @@ export class GoogleAdsManager {
|
|
|
260
264
|
this.isAdPlaying = true;
|
|
261
265
|
this.video.pause();
|
|
262
266
|
|
|
267
|
+
// Force ad container visibility and z-index
|
|
268
|
+
if (this.adContainer) {
|
|
269
|
+
this.adContainer.style.visibility = 'visible';
|
|
270
|
+
this.adContainer.style.opacity = '1';
|
|
271
|
+
this.adContainer.style.pointerEvents = 'auto';
|
|
272
|
+
this.adContainer.style.zIndex = '2147483647';
|
|
273
|
+
console.log('✅ Ad container visibility enforced');
|
|
274
|
+
}
|
|
275
|
+
|
|
263
276
|
// Strict enforcement: Prevent video from playing during ads
|
|
264
277
|
const preventPlayDuringAd = (e: Event) => {
|
|
265
278
|
if (this.isAdPlaying) {
|
|
@@ -418,13 +431,26 @@ export class GoogleAdsManager {
|
|
|
418
431
|
}
|
|
419
432
|
|
|
420
433
|
/**
|
|
421
|
-
* Resize ads
|
|
434
|
+
* Resize ads - with enhanced fullscreen support
|
|
422
435
|
*/
|
|
423
436
|
resize(width: number, height: number, viewMode?: any): void {
|
|
424
437
|
const google = (window as any).google;
|
|
425
438
|
if (this.adsManager && google && google.ima) {
|
|
426
|
-
// Use provided viewMode or default to NORMAL
|
|
427
439
|
const mode = viewMode || google.ima.ViewMode.NORMAL;
|
|
440
|
+
|
|
441
|
+
console.log(`📐 Resizing ads: ${width}x${height}, ViewMode: ${mode === google.ima.ViewMode.FULLSCREEN ? 'FULLSCREEN' : 'NORMAL'}`);
|
|
442
|
+
|
|
443
|
+
// Force ad container dimensions in fullscreen
|
|
444
|
+
if (this.adContainer && mode === google.ima.ViewMode.FULLSCREEN) {
|
|
445
|
+
this.adContainer.style.position = 'fixed';
|
|
446
|
+
this.adContainer.style.top = '0';
|
|
447
|
+
this.adContainer.style.left = '0';
|
|
448
|
+
this.adContainer.style.width = `${width}px`;
|
|
449
|
+
this.adContainer.style.height = `${height}px`;
|
|
450
|
+
this.adContainer.style.zIndex = '2147483647';
|
|
451
|
+
console.log('✅ Ad container forced to fullscreen dimensions');
|
|
452
|
+
}
|
|
453
|
+
|
|
428
454
|
this.adsManager.resize(width, height, mode);
|
|
429
455
|
}
|
|
430
456
|
}
|
|
@@ -531,9 +531,41 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
|
|
|
531
531
|
|
|
532
532
|
// Google Ads state
|
|
533
533
|
const adsManagerRef = useRef<GoogleAdsManager | null>(null);
|
|
534
|
-
const adContainerRef = useRef<HTMLDivElement>(null);
|
|
534
|
+
const adContainerRef = useRef<HTMLDivElement | null>(null);
|
|
535
535
|
const [isAdPlaying, setIsAdPlaying] = useState(false);
|
|
536
|
-
|
|
536
|
+
|
|
537
|
+
// Create ad container element programmatically
|
|
538
|
+
useEffect(() => {
|
|
539
|
+
if (!props.googleAds || adContainerRef.current) return;
|
|
540
|
+
|
|
541
|
+
const adContainer = document.createElement('div');
|
|
542
|
+
adContainer.className = 'uvf-ad-container';
|
|
543
|
+
adContainer.style.cssText = `
|
|
544
|
+
position: absolute;
|
|
545
|
+
top: 0;
|
|
546
|
+
left: 0;
|
|
547
|
+
right: 0;
|
|
548
|
+
bottom: 0;
|
|
549
|
+
z-index: 999999999;
|
|
550
|
+
pointer-events: none;
|
|
551
|
+
visibility: hidden;
|
|
552
|
+
opacity: 0;
|
|
553
|
+
transition: opacity 0.2s ease, visibility 0.2s ease;
|
|
554
|
+
`;
|
|
555
|
+
|
|
556
|
+
adContainerRef.current = adContainer;
|
|
557
|
+
console.log('✅ Ad container element created');
|
|
558
|
+
}, [props.googleAds]);
|
|
559
|
+
|
|
560
|
+
// Update ad container visibility when isAdPlaying changes
|
|
561
|
+
useEffect(() => {
|
|
562
|
+
if (!adContainerRef.current) return;
|
|
563
|
+
|
|
564
|
+
adContainerRef.current.style.pointerEvents = isAdPlaying ? 'auto' : 'none';
|
|
565
|
+
adContainerRef.current.style.visibility = isAdPlaying ? 'visible' : 'hidden';
|
|
566
|
+
adContainerRef.current.style.opacity = isAdPlaying ? '1' : '0';
|
|
567
|
+
}, [isAdPlaying]);
|
|
568
|
+
|
|
537
569
|
/**
|
|
538
570
|
* Generate ad chapter segments from googleAds cue points
|
|
539
571
|
* Handles pre-roll (0), mid-rolls, and post-roll (-1)
|
|
@@ -1165,27 +1197,56 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
|
|
|
1165
1197
|
if (props.onBuffering && typeof (player as any).on === 'function') {
|
|
1166
1198
|
(player as any).on('onBuffering', props.onBuffering);
|
|
1167
1199
|
}
|
|
1168
|
-
if (
|
|
1200
|
+
if (typeof (player as any).on === 'function') {
|
|
1169
1201
|
(player as any).on('onFullscreenChanged', (isFullscreen: boolean) => {
|
|
1202
|
+
console.log(`🔄 Fullscreen changed: ${isFullscreen}`);
|
|
1203
|
+
|
|
1204
|
+
// Force ad container positioning for fullscreen
|
|
1205
|
+
if (adContainerRef.current) {
|
|
1206
|
+
const adContainer = adContainerRef.current;
|
|
1207
|
+
|
|
1208
|
+
if (isFullscreen) {
|
|
1209
|
+
// Fullscreen: use fixed positioning
|
|
1210
|
+
adContainer.style.position = 'fixed';
|
|
1211
|
+
adContainer.style.top = '0';
|
|
1212
|
+
adContainer.style.left = '0';
|
|
1213
|
+
adContainer.style.width = '100vw';
|
|
1214
|
+
adContainer.style.height = '100vh';
|
|
1215
|
+
adContainer.style.zIndex = '2147483647';
|
|
1216
|
+
console.log('✅ Ad container: fullscreen positioning applied');
|
|
1217
|
+
} else {
|
|
1218
|
+
// Normal: use absolute positioning
|
|
1219
|
+
adContainer.style.position = 'absolute';
|
|
1220
|
+
adContainer.style.top = '0';
|
|
1221
|
+
adContainer.style.left = '0';
|
|
1222
|
+
adContainer.style.right = '0';
|
|
1223
|
+
adContainer.style.bottom = '0';
|
|
1224
|
+
adContainer.style.width = '';
|
|
1225
|
+
adContainer.style.height = '';
|
|
1226
|
+
adContainer.style.zIndex = '999999999';
|
|
1227
|
+
console.log('✅ Ad container: normal positioning restored');
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1170
1231
|
// Resize Google Ads if active
|
|
1171
1232
|
if (adsManagerRef.current) {
|
|
1172
1233
|
const google = (window as any).google;
|
|
1173
1234
|
if (google && google.ima) {
|
|
1174
|
-
const
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
? google.ima.ViewMode.FULLSCREEN
|
|
1235
|
+
const width = isFullscreen
|
|
1236
|
+
? window.screen.width
|
|
1237
|
+
: (containerRef.current?.clientWidth || window.innerWidth);
|
|
1238
|
+
const height = isFullscreen
|
|
1239
|
+
? window.screen.height
|
|
1240
|
+
: (containerRef.current?.clientHeight || window.innerHeight);
|
|
1241
|
+
const viewMode = isFullscreen
|
|
1242
|
+
? google.ima.ViewMode.FULLSCREEN
|
|
1183
1243
|
: google.ima.ViewMode.NORMAL;
|
|
1184
|
-
|
|
1244
|
+
|
|
1185
1245
|
adsManagerRef.current.resize(width, height, viewMode);
|
|
1246
|
+
console.log(`✅ Ads resized: ${width}x${height}, ViewMode: ${viewMode === google.ima.ViewMode.FULLSCREEN ? 'FULLSCREEN' : 'NORMAL'}`);
|
|
1186
1247
|
}
|
|
1187
1248
|
}
|
|
1188
|
-
|
|
1249
|
+
|
|
1189
1250
|
// Call user's fullscreen callback
|
|
1190
1251
|
props.onFullscreenChange?.(isFullscreen);
|
|
1191
1252
|
});
|
|
@@ -1271,18 +1332,47 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
|
|
|
1271
1332
|
|
|
1272
1333
|
await adsManager.initialize();
|
|
1273
1334
|
adsManagerRef.current = adsManager;
|
|
1274
|
-
|
|
1335
|
+
|
|
1275
1336
|
console.log('Google Ads initialized successfully');
|
|
1276
|
-
|
|
1277
|
-
//
|
|
1337
|
+
|
|
1338
|
+
// Inject ad container into player wrapper (not as sibling)
|
|
1339
|
+
const playerWrapper = containerRef.current?.querySelector('.uvf-player-wrapper');
|
|
1340
|
+
if (playerWrapper && adContainerRef.current) {
|
|
1341
|
+
// Move ad container INSIDE player wrapper so it inherits fullscreen context
|
|
1342
|
+
playerWrapper.appendChild(adContainerRef.current);
|
|
1343
|
+
console.log('✅ Ad container injected into player wrapper');
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
// Initialize ad display container on first user interaction
|
|
1347
|
+
// Chrome requires this to be called on a user gesture
|
|
1348
|
+
let adContainerInitialized = false;
|
|
1349
|
+
|
|
1350
|
+
const initAdsOnUserGesture = () => {
|
|
1351
|
+
if (!adContainerInitialized && adsManagerRef.current) {
|
|
1352
|
+
console.log('Initializing ad container on user gesture');
|
|
1353
|
+
try {
|
|
1354
|
+
adsManagerRef.current.initAdDisplayContainer();
|
|
1355
|
+
adContainerInitialized = true;
|
|
1356
|
+
} catch (err) {
|
|
1357
|
+
console.warn('Ad container init error:', err);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
};
|
|
1361
|
+
|
|
1278
1362
|
const handleFirstPlay = () => {
|
|
1279
|
-
|
|
1280
|
-
|
|
1363
|
+
initAdsOnUserGesture();
|
|
1364
|
+
if (adsManagerRef.current && adContainerInitialized) {
|
|
1281
1365
|
adsManagerRef.current.requestAds();
|
|
1282
1366
|
}
|
|
1283
1367
|
videoElement.removeEventListener('play', handleFirstPlay);
|
|
1368
|
+
videoElement.removeEventListener('click', initAdsOnUserGesture);
|
|
1369
|
+
adContainerRef.current?.removeEventListener('click', initAdsOnUserGesture);
|
|
1284
1370
|
};
|
|
1371
|
+
|
|
1372
|
+
// Listen for both play and click events to ensure we catch user gesture
|
|
1285
1373
|
videoElement.addEventListener('play', handleFirstPlay, { once: true });
|
|
1374
|
+
videoElement.addEventListener('click', initAdsOnUserGesture, { once: true });
|
|
1375
|
+
adContainerRef.current?.addEventListener('click', initAdsOnUserGesture, { once: true });
|
|
1286
1376
|
} catch (adsError) {
|
|
1287
1377
|
console.error('Failed to initialize Google Ads:', adsError);
|
|
1288
1378
|
props.googleAds?.onAdError?.(adsError);
|
|
@@ -1619,29 +1709,13 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
|
|
|
1619
1709
|
}}
|
|
1620
1710
|
>
|
|
1621
1711
|
{/* Video Player */}
|
|
1622
|
-
<div
|
|
1623
|
-
ref={containerRef}
|
|
1712
|
+
<div
|
|
1713
|
+
ref={containerRef}
|
|
1624
1714
|
className={`uvf-responsive-container ${props.className || ''}`}
|
|
1625
1715
|
style={responsiveStyle}
|
|
1626
1716
|
/>
|
|
1627
|
-
|
|
1628
|
-
{/*
|
|
1629
|
-
{props.googleAds && (
|
|
1630
|
-
<div
|
|
1631
|
-
ref={adContainerRef}
|
|
1632
|
-
className="uvf-ad-container"
|
|
1633
|
-
style={{
|
|
1634
|
-
position: 'fixed',
|
|
1635
|
-
top: 0,
|
|
1636
|
-
left: 0,
|
|
1637
|
-
right: 0,
|
|
1638
|
-
bottom: 0,
|
|
1639
|
-
zIndex: 2147483647, // Maximum z-index - ads must be on top of everything
|
|
1640
|
-
pointerEvents: isAdPlaying ? 'auto' : 'none', // Allow interaction only when ad is playing
|
|
1641
|
-
display: isAdPlaying ? 'block' : 'none', // Hide container when no ad
|
|
1642
|
-
}}
|
|
1643
|
-
/>
|
|
1644
|
-
)}
|
|
1717
|
+
|
|
1718
|
+
{/* Ad container will be programmatically injected into player wrapper */}
|
|
1645
1719
|
|
|
1646
1720
|
|
|
1647
1721
|
{/* EPG Overlay - Full-screen glassmorphic overlay */}
|