unified-video-framework 1.4.390 → 1.4.391

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.
@@ -26,6 +26,7 @@ export interface GoogleAdsConfig {
26
26
  }>;
27
27
 
28
28
  // Callbacks
29
+ onPrerollAdsReady?: (hasPreroll: boolean) => void; // Called when pre-roll status is determined
29
30
  onAdStart?: () => void;
30
31
  onAdEnd?: () => void;
31
32
  onAdError?: (error: any) => void;
@@ -222,13 +223,32 @@ export class GoogleAdsManager {
222
223
 
223
224
  console.log('📍 Ad cue points detected (pre/mid/post):', allCuePoints);
224
225
 
226
+ // Check for pre-roll ad (cue point at time 0)
227
+ const hasPreroll = allCuePoints.includes(0);
228
+ console.log(`🎬 Pre-roll ad detected: ${hasPreroll}`);
229
+
230
+ // Notify pre-roll callback immediately
231
+ if (this.config.onPrerollAdsReady) {
232
+ this.config.onPrerollAdsReady(hasPreroll);
233
+ }
234
+
225
235
  // Notify callback with all cue points
226
236
  if (this.config.onAdCuePoints) {
227
237
  this.config.onAdCuePoints(allCuePoints);
228
238
  }
239
+ } else {
240
+ // No cue points = no pre-roll
241
+ console.log('📍 No ad cue points - no pre-roll ads');
242
+ if (this.config.onPrerollAdsReady) {
243
+ this.config.onPrerollAdsReady(false);
244
+ }
229
245
  }
230
246
  } catch (error) {
231
247
  console.warn('Could not extract ad cue points:', error);
248
+ // Assume no pre-roll on error
249
+ if (this.config.onPrerollAdsReady) {
250
+ this.config.onPrerollAdsReady(false);
251
+ }
232
252
  }
233
253
 
234
254
  // Setup ads manager event listeners
@@ -391,6 +411,11 @@ export class GoogleAdsManager {
391
411
  const error = event.getError?.();
392
412
  console.error('Ad error:', error?.getMessage?.() || error);
393
413
 
414
+ // Notify pre-roll callback on error (no ads available)
415
+ if (this.config.onPrerollAdsReady) {
416
+ this.config.onPrerollAdsReady(false);
417
+ }
418
+
394
419
  this.config.onAdError?.(error);
395
420
 
396
421
  // Destroy ads manager on error
@@ -533,19 +533,6 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
533
533
  const adsManagerRef = useRef<GoogleAdsManager | null>(null);
534
534
  const adContainerRef = useRef<HTMLDivElement | null>(null);
535
535
  const [isAdPlaying, setIsAdPlaying] = useState(false);
536
- const [isLoading, setIsLoading] = useState(false);
537
-
538
- // Control loading spinner for pre-roll ads
539
- useEffect(() => {
540
- const loadingElement = document.getElementById('uvf-loading');
541
- if (loadingElement) {
542
- if (isLoading) {
543
- loadingElement.classList.add('active');
544
- } else {
545
- loadingElement.classList.remove('active');
546
- }
547
- }
548
- }, [isLoading]);
549
536
 
550
537
  /**
551
538
  * Generate ad chapter segments from googleAds cue points
@@ -1274,17 +1261,15 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
1274
1261
  adTagUrl: props.googleAds.adTagUrl,
1275
1262
  midrollTimes: props.googleAds.midrollTimes,
1276
1263
  companionAdSlots: props.googleAds.companionAdSlots,
1277
- onAdStart: () => {
1278
- // Clear the fallback timeout since a pre-roll ad is actually starting
1279
- if (prerollFallbackTimeout) {
1280
- clearTimeout(prerollFallbackTimeout);
1281
- prerollFallbackTimeout = null;
1282
- console.log('✅ Pre-roll ad started, fallback timeout cleared');
1283
- }
1284
-
1285
- // Hide loading since ad is now playing
1286
- setIsLoading(false);
1264
+ onPrerollAdsReady: (hasPreroll: boolean) => {
1265
+ console.log(`🎬 Pre-roll status: ${hasPreroll ? 'EXISTS' : 'NONE'}`);
1287
1266
 
1267
+ // Notify player to unblock playback
1268
+ if (typeof (player as any).onPrerollAdsResolved === 'function') {
1269
+ (player as any).onPrerollAdsResolved(hasPreroll);
1270
+ }
1271
+ },
1272
+ onAdStart: () => {
1288
1273
  setIsAdPlaying(true);
1289
1274
  // Notify player to block keyboard controls
1290
1275
  if (typeof (player as any).setAdPlaying === 'function') {
@@ -1301,15 +1286,6 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
1301
1286
  props.googleAds?.onAdEnd?.();
1302
1287
  },
1303
1288
  onAdError: (error) => {
1304
- // Clear the fallback timeout on ad error
1305
- if (prerollFallbackTimeout) {
1306
- clearTimeout(prerollFallbackTimeout);
1307
- prerollFallbackTimeout = null;
1308
- }
1309
-
1310
- // Hide loading on error
1311
- setIsLoading(false);
1312
-
1313
1289
  setIsAdPlaying(false);
1314
1290
  // Notify player to unblock keyboard controls
1315
1291
  if (typeof (player as any).setAdPlaying === 'function') {
@@ -1351,7 +1327,6 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
1351
1327
  // Initialize ad display container on first user interaction
1352
1328
  // Chrome requires this to be called on a user gesture
1353
1329
  let adContainerInitialized = false;
1354
- let prerollFallbackTimeout: NodeJS.Timeout | null = null;
1355
1330
 
1356
1331
  const initAdsOnUserGesture = () => {
1357
1332
  if (!adContainerInitialized && adsManagerRef.current) {
@@ -1366,25 +1341,33 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
1366
1341
  };
1367
1342
 
1368
1343
  const handleFirstPlay = () => {
1344
+ console.log('🎬 handleFirstPlay triggered');
1345
+
1369
1346
  initAdsOnUserGesture();
1370
- if (adsManagerRef.current && adContainerInitialized) {
1371
- // Show loading while pre-roll ads are being fetched and prepared
1372
- console.log('⏳ Loading pre-roll ads...');
1373
- setIsLoading(true);
1374
- videoElement.pause();
1375
-
1376
- // Safety mechanism: If no pre-roll ad starts within 2 seconds, hide loading and resume video
1377
- // This handles cases where only mid-roll or post-roll ads are configured
1378
- prerollFallbackTimeout = setTimeout(() => {
1379
- if (!adsManagerRef.current?.isPlayingAd()) {
1380
- console.log('⏩ No pre-roll ad detected, resuming normal playback');
1381
- setIsLoading(false);
1382
- videoElement.play().catch(() => {});
1383
- }
1384
- }, 2000);
1385
1347
 
1386
- adsManagerRef.current.requestAds();
1348
+ if (adsManagerRef.current && adContainerInitialized) {
1349
+ // Check if player supports pre-roll blocking
1350
+ if (typeof (player as any).waitForPrerollAds === 'function') {
1351
+ console.log('⏳ Blocking play, waiting for pre-roll ads...');
1352
+
1353
+ // Block playback until ads are ready
1354
+ (player as any).waitForPrerollAds(() => {
1355
+ console.log('✅ Pre-roll resolved, resuming playback');
1356
+ videoElement.play().catch(err => {
1357
+ console.error('Error playing after ads resolved:', err);
1358
+ });
1359
+ });
1360
+
1361
+ // Request ads (async - will trigger onPrerollAdsReady callback)
1362
+ adsManagerRef.current.requestAds();
1363
+ } else {
1364
+ // Fallback: legacy behavior if method not available
1365
+ console.warn('⚠️ waitForPrerollAds not available, using legacy flow');
1366
+ adsManagerRef.current.requestAds();
1367
+ }
1387
1368
  }
1369
+
1370
+ // Remove listeners
1388
1371
  videoElement.removeEventListener('play', handleFirstPlay);
1389
1372
  videoElement.removeEventListener('click', initAdsOnUserGesture);
1390
1373
  adContainerRef.current?.removeEventListener('click', initAdsOnUserGesture);