stormcloud-video-player 0.2.5 → 0.2.7
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/README.md +15 -16
- package/dist/stormcloud-vp.min.js +1 -1
- package/lib/index.cjs +84 -375
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +0 -30
- package/lib/index.d.ts +0 -30
- package/lib/index.js +84 -375
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +32 -24
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +0 -1
- package/lib/players/HlsPlayer.cjs +32 -24
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/index.cjs +32 -24
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/ima.cjs +3 -3
- package/lib/sdk/ima.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.cjs +32 -24
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/utils/tracking.cjs +26 -7
- package/lib/utils/tracking.cjs.map +1 -1
- package/package.json +1 -1
- package/lib/types-DOcCdwQI.d.cts +0 -78
package/lib/index.cjs
CHANGED
|
@@ -127,8 +127,6 @@ function createImaController(video, options) {
|
|
|
127
127
|
let adsLoadedPromise;
|
|
128
128
|
let adsLoadedResolve;
|
|
129
129
|
let adsLoadedReject;
|
|
130
|
-
let currentAdDuration = 0;
|
|
131
|
-
let preloadedAds = [];
|
|
132
130
|
function makeAdsRequest(google, vastTagUrl) {
|
|
133
131
|
const adsRequest = new google.ima.AdsRequest();
|
|
134
132
|
adsRequest.adTagUrl = vastTagUrl;
|
|
@@ -235,7 +233,6 @@ function createImaController(video, options) {
|
|
|
235
233
|
} catch {
|
|
236
234
|
}
|
|
237
235
|
adPlaying = false;
|
|
238
|
-
currentAdDuration = 0;
|
|
239
236
|
video.muted = originalMutedState;
|
|
240
237
|
if (adContainerEl)
|
|
241
238
|
adContainerEl.style.pointerEvents = "none";
|
|
@@ -261,7 +258,7 @@ function createImaController(video, options) {
|
|
|
261
258
|
);
|
|
262
259
|
emit("ad_error");
|
|
263
260
|
if (!options?.continueLiveStreamDuringAds) {
|
|
264
|
-
video.play()
|
|
261
|
+
video.play()?.catch(() => {
|
|
265
262
|
});
|
|
266
263
|
}
|
|
267
264
|
}
|
|
@@ -296,7 +293,7 @@ function createImaController(video, options) {
|
|
|
296
293
|
if (adContainerEl)
|
|
297
294
|
adContainerEl.style.pointerEvents = "none";
|
|
298
295
|
if (!options?.continueLiveStreamDuringAds) {
|
|
299
|
-
video.play()
|
|
296
|
+
video.play()?.catch(() => {
|
|
300
297
|
});
|
|
301
298
|
console.log("[IMA] Video resumed (VOD mode)");
|
|
302
299
|
} else {
|
|
@@ -307,22 +304,9 @@ function createImaController(video, options) {
|
|
|
307
304
|
emit("content_resume");
|
|
308
305
|
}
|
|
309
306
|
);
|
|
310
|
-
adsManager.addEventListener(AdEvent.STARTED, (adEvent) => {
|
|
311
|
-
console.log("[IMA] Ad started");
|
|
312
|
-
try {
|
|
313
|
-
const ad = adEvent.getAd();
|
|
314
|
-
if (ad && ad.getDuration) {
|
|
315
|
-
currentAdDuration = ad.getDuration();
|
|
316
|
-
console.log(`[IMA] Ad duration: ${currentAdDuration}s`);
|
|
317
|
-
}
|
|
318
|
-
} catch (error) {
|
|
319
|
-
console.warn("[IMA] Could not get ad duration:", error);
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
307
|
adsManager.addEventListener(AdEvent.ALL_ADS_COMPLETED, () => {
|
|
323
308
|
console.log("[IMA] All ads completed");
|
|
324
309
|
adPlaying = false;
|
|
325
|
-
currentAdDuration = 0;
|
|
326
310
|
video.muted = originalMutedState;
|
|
327
311
|
if (adContainerEl) adContainerEl.style.pointerEvents = "none";
|
|
328
312
|
if (!options?.continueLiveStreamDuringAds) {
|
|
@@ -422,7 +406,7 @@ function createImaController(video, options) {
|
|
|
422
406
|
console.error("[IMA] Error starting ad playback:", error);
|
|
423
407
|
adPlaying = false;
|
|
424
408
|
if (!options?.continueLiveStreamDuringAds) {
|
|
425
|
-
video.play()
|
|
409
|
+
video.play()?.catch(() => {
|
|
426
410
|
});
|
|
427
411
|
}
|
|
428
412
|
return Promise.reject(error);
|
|
@@ -507,84 +491,6 @@ function createImaController(video, options) {
|
|
|
507
491
|
}
|
|
508
492
|
}
|
|
509
493
|
return 1;
|
|
510
|
-
},
|
|
511
|
-
getAdDuration() {
|
|
512
|
-
return currentAdDuration;
|
|
513
|
-
},
|
|
514
|
-
async preloadAds(vastTagUrls) {
|
|
515
|
-
console.log(`[IMA] Preloading ${vastTagUrls.length} ads`);
|
|
516
|
-
const adInfos = [];
|
|
517
|
-
for (const vastTagUrl of vastTagUrls) {
|
|
518
|
-
try {
|
|
519
|
-
await ensureImaLoaded();
|
|
520
|
-
const google = window.google;
|
|
521
|
-
const tempAdsLoader = new google.ima.AdsLoader(adDisplayContainer);
|
|
522
|
-
const adInfo = await new Promise((resolve, reject) => {
|
|
523
|
-
const timeout = setTimeout(() => {
|
|
524
|
-
reject(new Error("Preload timeout"));
|
|
525
|
-
}, 5e3);
|
|
526
|
-
tempAdsLoader.addEventListener(
|
|
527
|
-
google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
|
|
528
|
-
(evt) => {
|
|
529
|
-
clearTimeout(timeout);
|
|
530
|
-
try {
|
|
531
|
-
const tempAdsManager = evt.getAdsManager(video);
|
|
532
|
-
let duration = 30;
|
|
533
|
-
try {
|
|
534
|
-
const ads = tempAdsManager.getCuePoints?.() || [];
|
|
535
|
-
if (ads.length > 0) {
|
|
536
|
-
duration = 15;
|
|
537
|
-
}
|
|
538
|
-
} catch {
|
|
539
|
-
}
|
|
540
|
-
tempAdsManager.destroy();
|
|
541
|
-
resolve({
|
|
542
|
-
duration,
|
|
543
|
-
vastTagUrl,
|
|
544
|
-
isPreloaded: true
|
|
545
|
-
});
|
|
546
|
-
} catch (error) {
|
|
547
|
-
clearTimeout(timeout);
|
|
548
|
-
reject(error);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
);
|
|
552
|
-
tempAdsLoader.addEventListener(
|
|
553
|
-
google.ima.AdErrorEvent.Type.AD_ERROR,
|
|
554
|
-
(errorEvent) => {
|
|
555
|
-
clearTimeout(timeout);
|
|
556
|
-
console.warn(
|
|
557
|
-
`[IMA] Preload error for ${vastTagUrl}:`,
|
|
558
|
-
errorEvent.getError()
|
|
559
|
-
);
|
|
560
|
-
resolve({
|
|
561
|
-
duration: 30,
|
|
562
|
-
vastTagUrl,
|
|
563
|
-
isPreloaded: false
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
);
|
|
567
|
-
const adsRequest = new google.ima.AdsRequest();
|
|
568
|
-
adsRequest.adTagUrl = vastTagUrl;
|
|
569
|
-
tempAdsLoader.requestAds(adsRequest);
|
|
570
|
-
});
|
|
571
|
-
adInfos.push(adInfo);
|
|
572
|
-
tempAdsLoader.destroy();
|
|
573
|
-
} catch (error) {
|
|
574
|
-
console.warn(`[IMA] Failed to preload ad ${vastTagUrl}:`, error);
|
|
575
|
-
adInfos.push({
|
|
576
|
-
duration: 30,
|
|
577
|
-
vastTagUrl,
|
|
578
|
-
isPreloaded: false
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
preloadedAds = adInfos;
|
|
583
|
-
console.log(
|
|
584
|
-
`[IMA] Preloaded ${adInfos.length} ads with total duration:`,
|
|
585
|
-
adInfos.reduce((sum, ad) => sum + ad.duration, 0)
|
|
586
|
-
);
|
|
587
|
-
return adInfos;
|
|
588
494
|
}
|
|
589
495
|
};
|
|
590
496
|
}
|
|
@@ -733,13 +639,32 @@ function getClientInfo() {
|
|
|
733
639
|
}
|
|
734
640
|
async function getBrowserID(clientInfo) {
|
|
735
641
|
const fingerprintString = JSON.stringify(clientInfo);
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
642
|
+
if (typeof crypto !== "undefined" && crypto.subtle && crypto.subtle.digest) {
|
|
643
|
+
try {
|
|
644
|
+
const hashBuffer = await crypto.subtle.digest(
|
|
645
|
+
"SHA-256",
|
|
646
|
+
new TextEncoder().encode(fingerprintString)
|
|
647
|
+
);
|
|
648
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
649
|
+
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
650
|
+
return hashHex;
|
|
651
|
+
} catch (error) {
|
|
652
|
+
console.warn(
|
|
653
|
+
"[StormcloudVideoPlayer] crypto.subtle.digest failed, using fallback hash:",
|
|
654
|
+
error
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
let hash = 0;
|
|
659
|
+
for (let i = 0; i < fingerprintString.length; i++) {
|
|
660
|
+
const char = fingerprintString.charCodeAt(i);
|
|
661
|
+
hash = (hash << 5) - hash + char;
|
|
662
|
+
hash = hash & hash;
|
|
663
|
+
}
|
|
664
|
+
const fallbackHash = Math.abs(hash).toString(16).padStart(8, "0");
|
|
665
|
+
const timestamp = Date.now().toString(16).padStart(12, "0");
|
|
666
|
+
const random = Math.random().toString(16).substring(2, 14).padStart(12, "0");
|
|
667
|
+
return (fallbackHash + timestamp + random).padEnd(64, "0");
|
|
743
668
|
}
|
|
744
669
|
async function sendInitialTracking(licenseKey) {
|
|
745
670
|
try {
|
|
@@ -817,8 +742,6 @@ var StormcloudVideoPlayer = class {
|
|
|
817
742
|
this.totalAdsInBreak = 0;
|
|
818
743
|
this.showAds = false;
|
|
819
744
|
this.isLiveStream = false;
|
|
820
|
-
this.preloadedAdInfo = [];
|
|
821
|
-
this.cumulativeAdDurationMs = 0;
|
|
822
745
|
this.config = config;
|
|
823
746
|
this.video = config.videoElement;
|
|
824
747
|
this.ima = createImaController(this.video, {
|
|
@@ -859,7 +782,7 @@ var StormcloudVideoPlayer = class {
|
|
|
859
782
|
});
|
|
860
783
|
this.ima.initialize();
|
|
861
784
|
if (this.config.autoplay) {
|
|
862
|
-
await this.video.play()
|
|
785
|
+
await this.video.play()?.catch(() => {
|
|
863
786
|
});
|
|
864
787
|
}
|
|
865
788
|
return;
|
|
@@ -893,7 +816,7 @@ var StormcloudVideoPlayer = class {
|
|
|
893
816
|
});
|
|
894
817
|
this.ima.initialize();
|
|
895
818
|
if (this.config.autoplay) {
|
|
896
|
-
await this.video.play()
|
|
819
|
+
await this.video.play()?.catch(() => {
|
|
897
820
|
});
|
|
898
821
|
}
|
|
899
822
|
});
|
|
@@ -990,28 +913,16 @@ var StormcloudVideoPlayer = class {
|
|
|
990
913
|
this.ima.initialize();
|
|
991
914
|
this.ima.on("all_ads_completed", () => {
|
|
992
915
|
if (!this.inAdBreak) return;
|
|
993
|
-
const actualAdDuration = this.ima.getAdDuration();
|
|
994
|
-
if (actualAdDuration > 0) {
|
|
995
|
-
this.cumulativeAdDurationMs += actualAdDuration * 1e3;
|
|
996
|
-
if (this.config.debugAdTiming) {
|
|
997
|
-
console.log(
|
|
998
|
-
`[StormcloudVideoPlayer] Ad completed. Duration: ${actualAdDuration}s, Cumulative: ${this.cumulativeAdDurationMs}ms`
|
|
999
|
-
);
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
916
|
const remaining = this.getRemainingAdMs();
|
|
1003
|
-
|
|
1004
|
-
if (shouldContinue && this.adPodQueue.length > 0) {
|
|
917
|
+
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
1005
918
|
const next = this.adPodQueue.shift();
|
|
1006
919
|
this.currentAdIndex++;
|
|
1007
920
|
this.playSingleAd(next).catch(() => {
|
|
1008
921
|
});
|
|
1009
|
-
} else if (shouldContinue && this.canRequestMoreAds()) {
|
|
1010
|
-
this.requestAdditionalAds().catch(() => {
|
|
1011
|
-
this.endAdBreak();
|
|
1012
|
-
});
|
|
1013
922
|
} else {
|
|
1014
|
-
this.
|
|
923
|
+
this.currentAdIndex = 0;
|
|
924
|
+
this.totalAdsInBreak = 0;
|
|
925
|
+
this.showAds = false;
|
|
1015
926
|
}
|
|
1016
927
|
});
|
|
1017
928
|
this.ima.on("ad_error", () => {
|
|
@@ -1020,16 +931,11 @@ var StormcloudVideoPlayer = class {
|
|
|
1020
931
|
}
|
|
1021
932
|
if (!this.inAdBreak) return;
|
|
1022
933
|
const remaining = this.getRemainingAdMs();
|
|
1023
|
-
|
|
1024
|
-
if (shouldContinue && this.adPodQueue.length > 0) {
|
|
934
|
+
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
1025
935
|
const next = this.adPodQueue.shift();
|
|
1026
936
|
this.currentAdIndex++;
|
|
1027
937
|
this.playSingleAd(next).catch(() => {
|
|
1028
938
|
});
|
|
1029
|
-
} else if (shouldContinue && this.canRequestMoreAds()) {
|
|
1030
|
-
this.requestAdditionalAds().catch(() => {
|
|
1031
|
-
this.handleAdFailure();
|
|
1032
|
-
});
|
|
1033
939
|
} else {
|
|
1034
940
|
this.handleAdFailure();
|
|
1035
941
|
}
|
|
@@ -1520,25 +1426,6 @@ var StormcloudVideoPlayer = class {
|
|
|
1520
1426
|
isShowingAds() {
|
|
1521
1427
|
return this.showAds;
|
|
1522
1428
|
}
|
|
1523
|
-
getAdBreakStats() {
|
|
1524
|
-
const remainingDurationMs = this.currentAdBreakTargetDurationMs != null ? Math.max(
|
|
1525
|
-
0,
|
|
1526
|
-
this.currentAdBreakTargetDurationMs - this.cumulativeAdDurationMs
|
|
1527
|
-
) : void 0;
|
|
1528
|
-
const estimatedFillRate = this.currentAdBreakTargetDurationMs != null && this.currentAdBreakTargetDurationMs > 0 ? this.cumulativeAdDurationMs / this.currentAdBreakTargetDurationMs * 100 : void 0;
|
|
1529
|
-
return {
|
|
1530
|
-
isInAdBreak: this.inAdBreak,
|
|
1531
|
-
currentAdIndex: this.currentAdIndex,
|
|
1532
|
-
totalAdsInBreak: this.totalAdsInBreak,
|
|
1533
|
-
targetDurationMs: this.currentAdBreakTargetDurationMs,
|
|
1534
|
-
cumulativeDurationMs: this.cumulativeAdDurationMs,
|
|
1535
|
-
estimatedFillRate,
|
|
1536
|
-
remainingDurationMs
|
|
1537
|
-
};
|
|
1538
|
-
}
|
|
1539
|
-
getPreloadedAdInfo() {
|
|
1540
|
-
return [...this.preloadedAdInfo];
|
|
1541
|
-
}
|
|
1542
1429
|
getStreamType() {
|
|
1543
1430
|
const url = this.config.src.toLowerCase();
|
|
1544
1431
|
if (url.includes(".m3u8") || url.includes("/hls/") || url.includes("application/vnd.apple.mpegurl")) {
|
|
@@ -1562,70 +1449,59 @@ var StormcloudVideoPlayer = class {
|
|
|
1562
1449
|
}
|
|
1563
1450
|
return true;
|
|
1564
1451
|
}
|
|
1565
|
-
async
|
|
1566
|
-
const usp = new URLSearchParams(params || {});
|
|
1567
|
-
const url = `${adstormApiUrl}?${usp.toString()}`;
|
|
1568
|
-
const res = await fetch(url);
|
|
1569
|
-
if (!res.ok) throw new Error(`Failed to fetch adstorm ads: ${res.status}`);
|
|
1570
|
-
const data = await res.json();
|
|
1571
|
-
const tag = data?.adTagUrl || data?.vastTagUrl || data?.tagUrl;
|
|
1572
|
-
if (typeof tag === "string" && tag.length > 0) {
|
|
1573
|
-
this.apiVastTagUrl = tag;
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
async handleAdStart(marker) {
|
|
1452
|
+
async handleAdStart(_marker) {
|
|
1577
1453
|
const scheduled = this.findCurrentOrNextBreak(
|
|
1578
1454
|
this.video.currentTime * 1e3
|
|
1579
1455
|
);
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
"[StormcloudVideoPlayer] Starting ad break with target duration:",
|
|
1592
|
-
{
|
|
1593
|
-
targetDurationMs,
|
|
1594
|
-
scte35Duration: marker.durationSeconds,
|
|
1595
|
-
scheduledDuration: scheduled?.durationMs
|
|
1456
|
+
const tags = this.selectVastTagsForBreak(scheduled);
|
|
1457
|
+
let vastTagUrl;
|
|
1458
|
+
let adsNumber = 1;
|
|
1459
|
+
if (this.apiVastTagUrl) {
|
|
1460
|
+
vastTagUrl = this.apiVastTagUrl;
|
|
1461
|
+
if (this.vastConfig) {
|
|
1462
|
+
const isHls = this.config.src.includes(".m3u8") || this.config.src.includes("hls");
|
|
1463
|
+
if (isHls && this.vastConfig.cue_tones?.number_ads) {
|
|
1464
|
+
adsNumber = this.vastConfig.cue_tones.number_ads;
|
|
1465
|
+
} else if (!isHls && this.vastConfig.timer_vod?.number_ads) {
|
|
1466
|
+
adsNumber = this.vastConfig.timer_vod.number_ads;
|
|
1596
1467
|
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1468
|
+
}
|
|
1469
|
+
this.adPodQueue = new Array(adsNumber - 1).fill(vastTagUrl);
|
|
1470
|
+
this.currentAdIndex = 0;
|
|
1471
|
+
this.totalAdsInBreak = adsNumber;
|
|
1472
|
+
if (this.config.debugAdTiming) {
|
|
1473
|
+
console.log(
|
|
1474
|
+
`[StormcloudVideoPlayer] Using API VAST tag with ${adsNumber} ads:`,
|
|
1475
|
+
vastTagUrl
|
|
1476
|
+
);
|
|
1477
|
+
}
|
|
1478
|
+
} else if (tags && tags.length > 0) {
|
|
1479
|
+
vastTagUrl = tags[0];
|
|
1480
|
+
const rest = tags.slice(1);
|
|
1481
|
+
this.adPodQueue = rest;
|
|
1482
|
+
this.currentAdIndex = 0;
|
|
1483
|
+
this.totalAdsInBreak = tags.length;
|
|
1484
|
+
if (this.config.debugAdTiming) {
|
|
1485
|
+
console.log(
|
|
1486
|
+
"[StormcloudVideoPlayer] Using scheduled VAST tag:",
|
|
1487
|
+
vastTagUrl
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1490
|
+
} else {
|
|
1601
1491
|
if (this.config.debugAdTiming) {
|
|
1602
|
-
console.log("[StormcloudVideoPlayer] No
|
|
1492
|
+
console.log("[StormcloudVideoPlayer] No VAST tag available for ad");
|
|
1603
1493
|
}
|
|
1604
1494
|
return;
|
|
1605
1495
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
this.showAds = true;
|
|
1611
|
-
if (this.config.debugAdTiming) {
|
|
1612
|
-
const totalEstimatedDuration = this.preloadedAdInfo.reduce(
|
|
1613
|
-
(sum, ad) => sum + ad.duration,
|
|
1614
|
-
0
|
|
1615
|
-
);
|
|
1616
|
-
console.log("[StormcloudVideoPlayer] Ad queue built:", {
|
|
1617
|
-
totalAds: adQueue.length,
|
|
1618
|
-
estimatedTotalDuration: totalEstimatedDuration,
|
|
1619
|
-
targetDuration: targetDurationMs ? targetDurationMs / 1e3 : "unknown",
|
|
1620
|
-
fillRate: targetDurationMs ? totalEstimatedDuration * 1e3 / targetDurationMs : "unknown"
|
|
1621
|
-
});
|
|
1496
|
+
if (vastTagUrl) {
|
|
1497
|
+
this.showAds = true;
|
|
1498
|
+
this.currentAdIndex++;
|
|
1499
|
+
await this.playSingleAd(vastTagUrl);
|
|
1622
1500
|
}
|
|
1623
|
-
this.
|
|
1624
|
-
|
|
1625
|
-
if (targetDurationMs != null) {
|
|
1626
|
-
this.expectedAdBreakDurationMs = targetDurationMs;
|
|
1501
|
+
if (this.expectedAdBreakDurationMs == null && scheduled?.durationMs != null) {
|
|
1502
|
+
this.expectedAdBreakDurationMs = scheduled.durationMs;
|
|
1627
1503
|
this.currentAdBreakStartWallClockMs = this.currentAdBreakStartWallClockMs ?? Date.now();
|
|
1628
|
-
this.scheduleAdStopCountdown(
|
|
1504
|
+
this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
|
|
1629
1505
|
}
|
|
1630
1506
|
}
|
|
1631
1507
|
findCurrentOrNextBreak(nowMs) {
|
|
@@ -1742,89 +1618,25 @@ var StormcloudVideoPlayer = class {
|
|
|
1742
1618
|
"[StormcloudVideoPlayer] Handling ad failure - resuming content"
|
|
1743
1619
|
);
|
|
1744
1620
|
}
|
|
1745
|
-
this.endAdBreak();
|
|
1746
|
-
if (this.video.paused) {
|
|
1747
|
-
this.video.play().catch(() => {
|
|
1748
|
-
if (this.config.debugAdTiming) {
|
|
1749
|
-
console.error(
|
|
1750
|
-
"[StormcloudVideoPlayer] Failed to resume video after ad failure"
|
|
1751
|
-
);
|
|
1752
|
-
}
|
|
1753
|
-
});
|
|
1754
|
-
}
|
|
1755
|
-
}
|
|
1756
|
-
endAdBreak() {
|
|
1757
|
-
if (this.config.debugAdTiming) {
|
|
1758
|
-
const targetDuration = this.currentAdBreakTargetDurationMs ? this.currentAdBreakTargetDurationMs / 1e3 : "unknown";
|
|
1759
|
-
const actualDuration = this.cumulativeAdDurationMs / 1e3;
|
|
1760
|
-
const fillRate = this.currentAdBreakTargetDurationMs ? (this.cumulativeAdDurationMs / this.currentAdBreakTargetDurationMs * 100).toFixed(1) : "unknown";
|
|
1761
|
-
console.log("[StormcloudVideoPlayer] Ad break ended:", {
|
|
1762
|
-
targetDurationSeconds: targetDuration,
|
|
1763
|
-
actualDurationSeconds: actualDuration,
|
|
1764
|
-
fillRate: `${fillRate}%`,
|
|
1765
|
-
totalAdsPlayed: this.currentAdIndex
|
|
1766
|
-
});
|
|
1767
|
-
}
|
|
1768
1621
|
this.inAdBreak = false;
|
|
1769
1622
|
this.expectedAdBreakDurationMs = void 0;
|
|
1770
1623
|
this.currentAdBreakStartWallClockMs = void 0;
|
|
1771
|
-
this.currentAdBreakTargetDurationMs = void 0;
|
|
1772
|
-
this.cumulativeAdDurationMs = 0;
|
|
1773
1624
|
this.clearAdStartTimer();
|
|
1774
1625
|
this.clearAdStopTimer();
|
|
1775
1626
|
this.clearAdFailsafeTimer();
|
|
1776
1627
|
this.adPodQueue = [];
|
|
1777
|
-
this.preloadedAdInfo = [];
|
|
1778
1628
|
this.showAds = false;
|
|
1779
1629
|
this.currentAdIndex = 0;
|
|
1780
1630
|
this.totalAdsInBreak = 0;
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
if (remainingMs > 500) {
|
|
1784
|
-
return true;
|
|
1785
|
-
}
|
|
1786
|
-
if (this.currentAdBreakTargetDurationMs) {
|
|
1787
|
-
const targetRemainingMs = this.currentAdBreakTargetDurationMs - this.cumulativeAdDurationMs;
|
|
1788
|
-
const minAdDuration = this.config.minAdDurationMs ?? 5e3;
|
|
1789
|
-
if (targetRemainingMs > minAdDuration) {
|
|
1631
|
+
if (this.video.paused) {
|
|
1632
|
+
this.video.play()?.catch(() => {
|
|
1790
1633
|
if (this.config.debugAdTiming) {
|
|
1791
|
-
console.
|
|
1792
|
-
|
|
1634
|
+
console.error(
|
|
1635
|
+
"[StormcloudVideoPlayer] Failed to resume video after ad failure"
|
|
1793
1636
|
);
|
|
1794
1637
|
}
|
|
1795
|
-
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
return false;
|
|
1799
|
-
}
|
|
1800
|
-
canRequestMoreAds() {
|
|
1801
|
-
const maxAdsPerBreak = this.config.maxAdsPerBreak ?? 10;
|
|
1802
|
-
if (this.currentAdIndex >= maxAdsPerBreak) {
|
|
1803
|
-
return false;
|
|
1804
|
-
}
|
|
1805
|
-
return !!this.apiVastTagUrl;
|
|
1806
|
-
}
|
|
1807
|
-
async requestAdditionalAds() {
|
|
1808
|
-
if (!this.currentAdBreakTargetDurationMs || !this.apiVastTagUrl) {
|
|
1809
|
-
throw new Error(
|
|
1810
|
-
"Cannot request additional ads without target duration and VAST URL"
|
|
1811
|
-
);
|
|
1812
|
-
}
|
|
1813
|
-
const remainingDurationMs = this.currentAdBreakTargetDurationMs - this.cumulativeAdDurationMs;
|
|
1814
|
-
const estimatedAdDurationMs = this.getEstimatedAdDuration() * 1e3;
|
|
1815
|
-
if (remainingDurationMs < estimatedAdDurationMs * 0.5) {
|
|
1816
|
-
throw new Error("Not enough time remaining for additional ads");
|
|
1817
|
-
}
|
|
1818
|
-
if (this.config.debugAdTiming) {
|
|
1819
|
-
console.log(
|
|
1820
|
-
`[StormcloudVideoPlayer] Requesting additional ads for remaining ${remainingDurationMs}ms`
|
|
1821
|
-
);
|
|
1638
|
+
});
|
|
1822
1639
|
}
|
|
1823
|
-
this.adPodQueue.push(this.apiVastTagUrl);
|
|
1824
|
-
this.totalAdsInBreak++;
|
|
1825
|
-
const next = this.adPodQueue.shift();
|
|
1826
|
-
this.currentAdIndex++;
|
|
1827
|
-
await this.playSingleAd(next);
|
|
1828
1640
|
}
|
|
1829
1641
|
startAdFailsafeTimer() {
|
|
1830
1642
|
this.clearAdFailsafeTimer();
|
|
@@ -1858,109 +1670,6 @@ var StormcloudVideoPlayer = class {
|
|
|
1858
1670
|
}
|
|
1859
1671
|
return [b.vastTagUrl];
|
|
1860
1672
|
}
|
|
1861
|
-
async buildAdQueueForDuration(targetDurationMs) {
|
|
1862
|
-
const adQueue = [];
|
|
1863
|
-
let baseVastTagUrl = this.apiVastTagUrl;
|
|
1864
|
-
if (!baseVastTagUrl) {
|
|
1865
|
-
const scheduled = this.findCurrentOrNextBreak(
|
|
1866
|
-
this.video.currentTime * 1e3
|
|
1867
|
-
);
|
|
1868
|
-
const scheduledTags = this.selectVastTagsForBreak(scheduled);
|
|
1869
|
-
if (scheduledTags && scheduledTags.length > 0) {
|
|
1870
|
-
baseVastTagUrl = scheduledTags[0];
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
if (!baseVastTagUrl) {
|
|
1874
|
-
return adQueue;
|
|
1875
|
-
}
|
|
1876
|
-
if (!targetDurationMs) {
|
|
1877
|
-
let adsNumber = 1;
|
|
1878
|
-
if (this.vastConfig) {
|
|
1879
|
-
const isHls = this.config.src.includes(".m3u8") || this.config.src.includes("hls");
|
|
1880
|
-
if (isHls && this.vastConfig.cue_tones?.number_ads) {
|
|
1881
|
-
adsNumber = this.vastConfig.cue_tones.number_ads;
|
|
1882
|
-
} else if (!isHls && this.vastConfig.timer_vod?.number_ads) {
|
|
1883
|
-
adsNumber = this.vastConfig.timer_vod.number_ads;
|
|
1884
|
-
}
|
|
1885
|
-
}
|
|
1886
|
-
return new Array(adsNumber).fill(baseVastTagUrl);
|
|
1887
|
-
}
|
|
1888
|
-
const targetDurationSeconds = targetDurationMs / 1e3;
|
|
1889
|
-
let cumulativeDurationSeconds = 0;
|
|
1890
|
-
const maxAdsToTry = 10;
|
|
1891
|
-
let adsAdded = 0;
|
|
1892
|
-
if (this.config.debugAdTiming) {
|
|
1893
|
-
console.log(
|
|
1894
|
-
`[StormcloudVideoPlayer] Attempting to fill ${targetDurationSeconds}s with ads`
|
|
1895
|
-
);
|
|
1896
|
-
}
|
|
1897
|
-
while (cumulativeDurationSeconds < targetDurationSeconds && adsAdded < maxAdsToTry) {
|
|
1898
|
-
adQueue.push(baseVastTagUrl);
|
|
1899
|
-
adsAdded++;
|
|
1900
|
-
const estimatedAdDuration = this.getEstimatedAdDuration();
|
|
1901
|
-
cumulativeDurationSeconds += estimatedAdDuration;
|
|
1902
|
-
if (this.config.debugAdTiming) {
|
|
1903
|
-
console.log(
|
|
1904
|
-
`[StormcloudVideoPlayer] Added ad ${adsAdded}, cumulative duration: ${cumulativeDurationSeconds}s`
|
|
1905
|
-
);
|
|
1906
|
-
}
|
|
1907
|
-
const remainingDuration = targetDurationSeconds - cumulativeDurationSeconds;
|
|
1908
|
-
const toleranceSeconds = (this.config.adBreakGapToleranceMs ?? 2e3) / 1e3;
|
|
1909
|
-
if (remainingDuration < estimatedAdDuration && remainingDuration >= -toleranceSeconds) {
|
|
1910
|
-
if (this.config.debugAdTiming) {
|
|
1911
|
-
console.log(
|
|
1912
|
-
`[StormcloudVideoPlayer] Within tolerance, adding final ad. Overage: ${-remainingDuration}s`
|
|
1913
|
-
);
|
|
1914
|
-
}
|
|
1915
|
-
break;
|
|
1916
|
-
}
|
|
1917
|
-
if (remainingDuration < estimatedAdDuration && remainingDuration < -toleranceSeconds) {
|
|
1918
|
-
if (this.config.debugAdTiming) {
|
|
1919
|
-
console.log(
|
|
1920
|
-
`[StormcloudVideoPlayer] Would exceed duration by too much, stopping at ${adsAdded} ads`
|
|
1921
|
-
);
|
|
1922
|
-
}
|
|
1923
|
-
break;
|
|
1924
|
-
}
|
|
1925
|
-
}
|
|
1926
|
-
return adQueue;
|
|
1927
|
-
}
|
|
1928
|
-
getEstimatedAdDuration() {
|
|
1929
|
-
if (this.vastConfig) {
|
|
1930
|
-
return 15;
|
|
1931
|
-
}
|
|
1932
|
-
return 30;
|
|
1933
|
-
}
|
|
1934
|
-
async getAdInfoForQueue(adQueue) {
|
|
1935
|
-
if (this.config.enableAdPreloading === false) {
|
|
1936
|
-
if (this.config.debugAdTiming) {
|
|
1937
|
-
console.log(
|
|
1938
|
-
"[StormcloudVideoPlayer] Ad preloading disabled, using estimates"
|
|
1939
|
-
);
|
|
1940
|
-
}
|
|
1941
|
-
return adQueue.map((vastTagUrl) => ({
|
|
1942
|
-
duration: this.getEstimatedAdDuration(),
|
|
1943
|
-
vastTagUrl,
|
|
1944
|
-
isPreloaded: false
|
|
1945
|
-
}));
|
|
1946
|
-
}
|
|
1947
|
-
try {
|
|
1948
|
-
const adInfos = await this.ima.preloadAds(adQueue);
|
|
1949
|
-
return adInfos;
|
|
1950
|
-
} catch (error) {
|
|
1951
|
-
if (this.config.debugAdTiming) {
|
|
1952
|
-
console.warn(
|
|
1953
|
-
"[StormcloudVideoPlayer] Failed to preload ads, using estimates:",
|
|
1954
|
-
error
|
|
1955
|
-
);
|
|
1956
|
-
}
|
|
1957
|
-
return adQueue.map((vastTagUrl) => ({
|
|
1958
|
-
duration: this.getEstimatedAdDuration(),
|
|
1959
|
-
vastTagUrl,
|
|
1960
|
-
isPreloaded: false
|
|
1961
|
-
}));
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
1673
|
getRemainingAdMs() {
|
|
1965
1674
|
if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null)
|
|
1966
1675
|
return 0;
|