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.
- package/package.json +1 -1
- package/packages/web/dist/WebPlayer.d.ts +7 -0
- package/packages/web/dist/WebPlayer.d.ts.map +1 -1
- package/packages/web/dist/WebPlayer.js +40 -0
- package/packages/web/dist/WebPlayer.js.map +1 -1
- package/packages/web/dist/ads/GoogleAdsManager.d.ts +1 -0
- package/packages/web/dist/ads/GoogleAdsManager.d.ts.map +1 -1
- package/packages/web/dist/ads/GoogleAdsManager.js +17 -0
- 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 +21 -35
- package/packages/web/dist/react/WebPlayerView.js.map +1 -1
- package/packages/web/src/WebPlayer.ts +67 -0
- package/packages/web/src/ads/GoogleAdsManager.ts +25 -0
- package/packages/web/src/react/WebPlayerView.tsx +32 -49
|
@@ -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
|
-
|
|
1278
|
-
|
|
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
|
-
|
|
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);
|