unified-video-framework 1.4.373 → 1.4.375
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 +107 -21
- 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 +151 -48
|
@@ -3469,7 +3469,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
3469
3469
|
position: relative;
|
|
3470
3470
|
width: 100%;
|
|
3471
3471
|
background: #000;
|
|
3472
|
-
overflow: hidden
|
|
3472
|
+
overflow: visible; /* Allow ads to render on top - changed from hidden */
|
|
3473
3473
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
3474
3474
|
/* Theme variables (can be overridden at runtime) */
|
|
3475
3475
|
--uvf-accent-1: #ff0000;
|
|
@@ -5607,7 +5607,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
5607
5607
|
left: 0 !important;
|
|
5608
5608
|
width: 100vw !important;
|
|
5609
5609
|
height: 100vh !important;
|
|
5610
|
-
z-index: 2147483647
|
|
5610
|
+
z-index: 2147483646; /* One below ad container (2147483647) */
|
|
5611
5611
|
background: #000;
|
|
5612
5612
|
}
|
|
5613
5613
|
|
|
@@ -5617,6 +5617,8 @@ export class WebPlayer extends BasePlayer {
|
|
|
5617
5617
|
max-width: none !important;
|
|
5618
5618
|
max-height: none !important;
|
|
5619
5619
|
aspect-ratio: unset !important;
|
|
5620
|
+
z-index: 1; /* Below everything else */
|
|
5621
|
+
overflow: hidden; /* Keep overflow hidden for video clipping */
|
|
5620
5622
|
}
|
|
5621
5623
|
|
|
5622
5624
|
.uvf-player-wrapper.uvf-fullscreen .uvf-video {
|
|
@@ -5669,7 +5671,23 @@ export class WebPlayer extends BasePlayer {
|
|
|
5669
5671
|
.uvf-player-wrapper.uvf-fullscreen .uvf-controls-bar,
|
|
5670
5672
|
.uvf-player-wrapper.uvf-fullscreen .uvf-top-gradient,
|
|
5671
5673
|
.uvf-player-wrapper.uvf-fullscreen .uvf-controls-gradient {
|
|
5672
|
-
z-index:
|
|
5674
|
+
z-index: 2147483645; /* Below ads (2147483647), above video */
|
|
5675
|
+
}
|
|
5676
|
+
|
|
5677
|
+
/* Ensure ad container is always on top in fullscreen */
|
|
5678
|
+
.uvf-player-wrapper.uvf-fullscreen .uvf-ad-container {
|
|
5679
|
+
position: fixed !important;
|
|
5680
|
+
top: 0 !important;
|
|
5681
|
+
left: 0 !important;
|
|
5682
|
+
width: 100vw !important;
|
|
5683
|
+
height: 100vh !important;
|
|
5684
|
+
z-index: 2147483647 !important; /* Maximum - always on top */
|
|
5685
|
+
pointer-events: auto;
|
|
5686
|
+
}
|
|
5687
|
+
|
|
5688
|
+
/* Ensure video container doesn't block ads */
|
|
5689
|
+
.uvf-player-wrapper.uvf-fullscreen .uvf-video-container {
|
|
5690
|
+
z-index: 1; /* Below everything */
|
|
5673
5691
|
}
|
|
5674
5692
|
|
|
5675
5693
|
.uvf-player-wrapper.uvf-fullscreen .uvf-controls-bar {
|
|
@@ -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,53 @@ 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 and add to DOM
|
|
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
|
+
|
|
558
|
+
// Temporarily add to container until player wrapper is ready
|
|
559
|
+
if (containerRef.current) {
|
|
560
|
+
containerRef.current.appendChild(adContainer);
|
|
561
|
+
console.log('✅ Ad container element created and added to DOM');
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
return () => {
|
|
565
|
+
// Cleanup: remove ad container on unmount
|
|
566
|
+
if (adContainer.parentElement) {
|
|
567
|
+
adContainer.parentElement.removeChild(adContainer);
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
}, [props.googleAds]);
|
|
571
|
+
|
|
572
|
+
// Update ad container visibility when isAdPlaying changes
|
|
573
|
+
useEffect(() => {
|
|
574
|
+
if (!adContainerRef.current) return;
|
|
575
|
+
|
|
576
|
+
adContainerRef.current.style.pointerEvents = isAdPlaying ? 'auto' : 'none';
|
|
577
|
+
adContainerRef.current.style.visibility = isAdPlaying ? 'visible' : 'hidden';
|
|
578
|
+
adContainerRef.current.style.opacity = isAdPlaying ? '1' : '0';
|
|
579
|
+
}, [isAdPlaying]);
|
|
580
|
+
|
|
537
581
|
/**
|
|
538
582
|
* Generate ad chapter segments from googleAds cue points
|
|
539
583
|
* Handles pre-roll (0), mid-rolls, and post-roll (-1)
|
|
@@ -1165,27 +1209,56 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
|
|
|
1165
1209
|
if (props.onBuffering && typeof (player as any).on === 'function') {
|
|
1166
1210
|
(player as any).on('onBuffering', props.onBuffering);
|
|
1167
1211
|
}
|
|
1168
|
-
if (
|
|
1212
|
+
if (typeof (player as any).on === 'function') {
|
|
1169
1213
|
(player as any).on('onFullscreenChanged', (isFullscreen: boolean) => {
|
|
1214
|
+
console.log(`🔄 Fullscreen changed: ${isFullscreen}`);
|
|
1215
|
+
|
|
1216
|
+
// Force ad container positioning for fullscreen
|
|
1217
|
+
if (adContainerRef.current) {
|
|
1218
|
+
const adContainer = adContainerRef.current;
|
|
1219
|
+
|
|
1220
|
+
if (isFullscreen) {
|
|
1221
|
+
// Fullscreen: use fixed positioning
|
|
1222
|
+
adContainer.style.position = 'fixed';
|
|
1223
|
+
adContainer.style.top = '0';
|
|
1224
|
+
adContainer.style.left = '0';
|
|
1225
|
+
adContainer.style.width = '100vw';
|
|
1226
|
+
adContainer.style.height = '100vh';
|
|
1227
|
+
adContainer.style.zIndex = '2147483647';
|
|
1228
|
+
console.log('✅ Ad container: fullscreen positioning applied');
|
|
1229
|
+
} else {
|
|
1230
|
+
// Normal: use absolute positioning
|
|
1231
|
+
adContainer.style.position = 'absolute';
|
|
1232
|
+
adContainer.style.top = '0';
|
|
1233
|
+
adContainer.style.left = '0';
|
|
1234
|
+
adContainer.style.right = '0';
|
|
1235
|
+
adContainer.style.bottom = '0';
|
|
1236
|
+
adContainer.style.width = '';
|
|
1237
|
+
adContainer.style.height = '';
|
|
1238
|
+
adContainer.style.zIndex = '999999999';
|
|
1239
|
+
console.log('✅ Ad container: normal positioning restored');
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1170
1243
|
// Resize Google Ads if active
|
|
1171
1244
|
if (adsManagerRef.current) {
|
|
1172
1245
|
const google = (window as any).google;
|
|
1173
1246
|
if (google && google.ima) {
|
|
1174
|
-
const
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
? google.ima.ViewMode.FULLSCREEN
|
|
1247
|
+
const width = isFullscreen
|
|
1248
|
+
? window.screen.width
|
|
1249
|
+
: (containerRef.current?.clientWidth || window.innerWidth);
|
|
1250
|
+
const height = isFullscreen
|
|
1251
|
+
? window.screen.height
|
|
1252
|
+
: (containerRef.current?.clientHeight || window.innerHeight);
|
|
1253
|
+
const viewMode = isFullscreen
|
|
1254
|
+
? google.ima.ViewMode.FULLSCREEN
|
|
1183
1255
|
: google.ima.ViewMode.NORMAL;
|
|
1184
|
-
|
|
1256
|
+
|
|
1185
1257
|
adsManagerRef.current.resize(width, height, viewMode);
|
|
1258
|
+
console.log(`✅ Ads resized: ${width}x${height}, ViewMode: ${viewMode === google.ima.ViewMode.FULLSCREEN ? 'FULLSCREEN' : 'NORMAL'}`);
|
|
1186
1259
|
}
|
|
1187
1260
|
}
|
|
1188
|
-
|
|
1261
|
+
|
|
1189
1262
|
// Call user's fullscreen callback
|
|
1190
1263
|
props.onFullscreenChange?.(isFullscreen);
|
|
1191
1264
|
});
|
|
@@ -1199,26 +1272,43 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
|
|
|
1199
1272
|
// Small delay to ensure ad container is properly mounted in DOM
|
|
1200
1273
|
setTimeout(async () => {
|
|
1201
1274
|
try {
|
|
1275
|
+
// Ensure ad container exists
|
|
1276
|
+
if (!adContainerRef.current) {
|
|
1277
|
+
console.error('❌ Ad container ref is null - cannot initialize ads');
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1202
1281
|
const adContainer = adContainerRef.current;
|
|
1203
1282
|
const videoElement = (player as any).video || (player as any).getVideoElement?.();
|
|
1204
|
-
|
|
1283
|
+
|
|
1205
1284
|
// Validate both elements exist and are in the DOM
|
|
1206
1285
|
if (!adContainer) {
|
|
1207
|
-
console.
|
|
1286
|
+
console.error('❌ Ad container element not found');
|
|
1208
1287
|
return;
|
|
1209
1288
|
}
|
|
1210
|
-
|
|
1289
|
+
|
|
1211
1290
|
if (!videoElement) {
|
|
1212
|
-
console.
|
|
1291
|
+
console.error('❌ Video element not found');
|
|
1213
1292
|
return;
|
|
1214
1293
|
}
|
|
1215
|
-
|
|
1294
|
+
|
|
1216
1295
|
if (!document.body.contains(adContainer)) {
|
|
1217
|
-
console.warn('Ad container not attached to DOM');
|
|
1218
|
-
|
|
1296
|
+
console.warn('⚠️ Ad container not attached to DOM, attempting to attach...');
|
|
1297
|
+
// Try to add it to container if not in DOM
|
|
1298
|
+
if (containerRef.current) {
|
|
1299
|
+
containerRef.current.appendChild(adContainer);
|
|
1300
|
+
console.log('✅ Ad container attached to DOM');
|
|
1301
|
+
} else {
|
|
1302
|
+
console.error('❌ Cannot attach ad container - containerRef is null');
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1219
1305
|
}
|
|
1220
|
-
|
|
1221
|
-
console.log('Initializing Google Ads...', {
|
|
1306
|
+
|
|
1307
|
+
console.log('✅ Initializing Google Ads...', {
|
|
1308
|
+
adContainer: adContainer.className,
|
|
1309
|
+
videoElement: videoElement.tagName,
|
|
1310
|
+
adContainerInDOM: document.body.contains(adContainer)
|
|
1311
|
+
});
|
|
1222
1312
|
|
|
1223
1313
|
const adsManager = new GoogleAdsManager(
|
|
1224
1314
|
videoElement,
|
|
@@ -1271,18 +1361,47 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
|
|
|
1271
1361
|
|
|
1272
1362
|
await adsManager.initialize();
|
|
1273
1363
|
adsManagerRef.current = adsManager;
|
|
1274
|
-
|
|
1364
|
+
|
|
1275
1365
|
console.log('Google Ads initialized successfully');
|
|
1276
|
-
|
|
1277
|
-
//
|
|
1366
|
+
|
|
1367
|
+
// Inject ad container into player wrapper (not as sibling)
|
|
1368
|
+
const playerWrapper = containerRef.current?.querySelector('.uvf-player-wrapper');
|
|
1369
|
+
if (playerWrapper && adContainerRef.current) {
|
|
1370
|
+
// Move ad container INSIDE player wrapper so it inherits fullscreen context
|
|
1371
|
+
playerWrapper.appendChild(adContainerRef.current);
|
|
1372
|
+
console.log('✅ Ad container injected into player wrapper');
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
// Initialize ad display container on first user interaction
|
|
1376
|
+
// Chrome requires this to be called on a user gesture
|
|
1377
|
+
let adContainerInitialized = false;
|
|
1378
|
+
|
|
1379
|
+
const initAdsOnUserGesture = () => {
|
|
1380
|
+
if (!adContainerInitialized && adsManagerRef.current) {
|
|
1381
|
+
console.log('Initializing ad container on user gesture');
|
|
1382
|
+
try {
|
|
1383
|
+
adsManagerRef.current.initAdDisplayContainer();
|
|
1384
|
+
adContainerInitialized = true;
|
|
1385
|
+
} catch (err) {
|
|
1386
|
+
console.warn('Ad container init error:', err);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
|
|
1278
1391
|
const handleFirstPlay = () => {
|
|
1279
|
-
|
|
1280
|
-
|
|
1392
|
+
initAdsOnUserGesture();
|
|
1393
|
+
if (adsManagerRef.current && adContainerInitialized) {
|
|
1281
1394
|
adsManagerRef.current.requestAds();
|
|
1282
1395
|
}
|
|
1283
1396
|
videoElement.removeEventListener('play', handleFirstPlay);
|
|
1397
|
+
videoElement.removeEventListener('click', initAdsOnUserGesture);
|
|
1398
|
+
adContainerRef.current?.removeEventListener('click', initAdsOnUserGesture);
|
|
1284
1399
|
};
|
|
1400
|
+
|
|
1401
|
+
// Listen for both play and click events to ensure we catch user gesture
|
|
1285
1402
|
videoElement.addEventListener('play', handleFirstPlay, { once: true });
|
|
1403
|
+
videoElement.addEventListener('click', initAdsOnUserGesture, { once: true });
|
|
1404
|
+
adContainerRef.current?.addEventListener('click', initAdsOnUserGesture, { once: true });
|
|
1286
1405
|
} catch (adsError) {
|
|
1287
1406
|
console.error('Failed to initialize Google Ads:', adsError);
|
|
1288
1407
|
props.googleAds?.onAdError?.(adsError);
|
|
@@ -1619,29 +1738,13 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
|
|
|
1619
1738
|
}}
|
|
1620
1739
|
>
|
|
1621
1740
|
{/* Video Player */}
|
|
1622
|
-
<div
|
|
1623
|
-
ref={containerRef}
|
|
1741
|
+
<div
|
|
1742
|
+
ref={containerRef}
|
|
1624
1743
|
className={`uvf-responsive-container ${props.className || ''}`}
|
|
1625
1744
|
style={responsiveStyle}
|
|
1626
1745
|
/>
|
|
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
|
-
)}
|
|
1746
|
+
|
|
1747
|
+
{/* Ad container will be programmatically injected into player wrapper */}
|
|
1645
1748
|
|
|
1646
1749
|
|
|
1647
1750
|
{/* EPG Overlay - Full-screen glassmorphic overlay */}
|