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.
@@ -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: 2147483647; /* Maximum z-index value */
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
- adsRenderingSettings.mute = false; // Enable audio for ads
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 (props.onFullscreenChange && typeof (player as any).on === 'function') {
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 container = containerRef.current;
1175
- const width = isFullscreen
1176
- ? window.screen.width
1177
- : (container?.clientWidth || window.innerWidth);
1178
- const height = isFullscreen
1179
- ? window.screen.height
1180
- : (container?.clientHeight || window.innerHeight);
1181
- const viewMode = isFullscreen
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.warn('Ad container element not found');
1286
+ console.error('Ad container element not found');
1208
1287
  return;
1209
1288
  }
1210
-
1289
+
1211
1290
  if (!videoElement) {
1212
- console.warn('Video element not found');
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
- return;
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...', { adContainer, videoElement });
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
- // Initialize ad display container on first play
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
- if (adsManagerRef.current) {
1280
- adsManagerRef.current.initAdDisplayContainer();
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
- {/* Google Ads Container - positioned over the video player and controls */}
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 */}