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.js
CHANGED
|
@@ -68,8 +68,6 @@ function createImaController(video, options) {
|
|
|
68
68
|
let adsLoadedPromise;
|
|
69
69
|
let adsLoadedResolve;
|
|
70
70
|
let adsLoadedReject;
|
|
71
|
-
let currentAdDuration = 0;
|
|
72
|
-
let preloadedAds = [];
|
|
73
71
|
function makeAdsRequest(google, vastTagUrl) {
|
|
74
72
|
const adsRequest = new google.ima.AdsRequest();
|
|
75
73
|
adsRequest.adTagUrl = vastTagUrl;
|
|
@@ -176,7 +174,6 @@ function createImaController(video, options) {
|
|
|
176
174
|
} catch {
|
|
177
175
|
}
|
|
178
176
|
adPlaying = false;
|
|
179
|
-
currentAdDuration = 0;
|
|
180
177
|
video.muted = originalMutedState;
|
|
181
178
|
if (adContainerEl)
|
|
182
179
|
adContainerEl.style.pointerEvents = "none";
|
|
@@ -202,7 +199,7 @@ function createImaController(video, options) {
|
|
|
202
199
|
);
|
|
203
200
|
emit("ad_error");
|
|
204
201
|
if (!options?.continueLiveStreamDuringAds) {
|
|
205
|
-
video.play()
|
|
202
|
+
video.play()?.catch(() => {
|
|
206
203
|
});
|
|
207
204
|
}
|
|
208
205
|
}
|
|
@@ -237,7 +234,7 @@ function createImaController(video, options) {
|
|
|
237
234
|
if (adContainerEl)
|
|
238
235
|
adContainerEl.style.pointerEvents = "none";
|
|
239
236
|
if (!options?.continueLiveStreamDuringAds) {
|
|
240
|
-
video.play()
|
|
237
|
+
video.play()?.catch(() => {
|
|
241
238
|
});
|
|
242
239
|
console.log("[IMA] Video resumed (VOD mode)");
|
|
243
240
|
} else {
|
|
@@ -248,22 +245,9 @@ function createImaController(video, options) {
|
|
|
248
245
|
emit("content_resume");
|
|
249
246
|
}
|
|
250
247
|
);
|
|
251
|
-
adsManager.addEventListener(AdEvent.STARTED, (adEvent) => {
|
|
252
|
-
console.log("[IMA] Ad started");
|
|
253
|
-
try {
|
|
254
|
-
const ad = adEvent.getAd();
|
|
255
|
-
if (ad && ad.getDuration) {
|
|
256
|
-
currentAdDuration = ad.getDuration();
|
|
257
|
-
console.log(`[IMA] Ad duration: ${currentAdDuration}s`);
|
|
258
|
-
}
|
|
259
|
-
} catch (error) {
|
|
260
|
-
console.warn("[IMA] Could not get ad duration:", error);
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
248
|
adsManager.addEventListener(AdEvent.ALL_ADS_COMPLETED, () => {
|
|
264
249
|
console.log("[IMA] All ads completed");
|
|
265
250
|
adPlaying = false;
|
|
266
|
-
currentAdDuration = 0;
|
|
267
251
|
video.muted = originalMutedState;
|
|
268
252
|
if (adContainerEl) adContainerEl.style.pointerEvents = "none";
|
|
269
253
|
if (!options?.continueLiveStreamDuringAds) {
|
|
@@ -363,7 +347,7 @@ function createImaController(video, options) {
|
|
|
363
347
|
console.error("[IMA] Error starting ad playback:", error);
|
|
364
348
|
adPlaying = false;
|
|
365
349
|
if (!options?.continueLiveStreamDuringAds) {
|
|
366
|
-
video.play()
|
|
350
|
+
video.play()?.catch(() => {
|
|
367
351
|
});
|
|
368
352
|
}
|
|
369
353
|
return Promise.reject(error);
|
|
@@ -448,84 +432,6 @@ function createImaController(video, options) {
|
|
|
448
432
|
}
|
|
449
433
|
}
|
|
450
434
|
return 1;
|
|
451
|
-
},
|
|
452
|
-
getAdDuration() {
|
|
453
|
-
return currentAdDuration;
|
|
454
|
-
},
|
|
455
|
-
async preloadAds(vastTagUrls) {
|
|
456
|
-
console.log(`[IMA] Preloading ${vastTagUrls.length} ads`);
|
|
457
|
-
const adInfos = [];
|
|
458
|
-
for (const vastTagUrl of vastTagUrls) {
|
|
459
|
-
try {
|
|
460
|
-
await ensureImaLoaded();
|
|
461
|
-
const google = window.google;
|
|
462
|
-
const tempAdsLoader = new google.ima.AdsLoader(adDisplayContainer);
|
|
463
|
-
const adInfo = await new Promise((resolve, reject) => {
|
|
464
|
-
const timeout = setTimeout(() => {
|
|
465
|
-
reject(new Error("Preload timeout"));
|
|
466
|
-
}, 5e3);
|
|
467
|
-
tempAdsLoader.addEventListener(
|
|
468
|
-
google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
|
|
469
|
-
(evt) => {
|
|
470
|
-
clearTimeout(timeout);
|
|
471
|
-
try {
|
|
472
|
-
const tempAdsManager = evt.getAdsManager(video);
|
|
473
|
-
let duration = 30;
|
|
474
|
-
try {
|
|
475
|
-
const ads = tempAdsManager.getCuePoints?.() || [];
|
|
476
|
-
if (ads.length > 0) {
|
|
477
|
-
duration = 15;
|
|
478
|
-
}
|
|
479
|
-
} catch {
|
|
480
|
-
}
|
|
481
|
-
tempAdsManager.destroy();
|
|
482
|
-
resolve({
|
|
483
|
-
duration,
|
|
484
|
-
vastTagUrl,
|
|
485
|
-
isPreloaded: true
|
|
486
|
-
});
|
|
487
|
-
} catch (error) {
|
|
488
|
-
clearTimeout(timeout);
|
|
489
|
-
reject(error);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
);
|
|
493
|
-
tempAdsLoader.addEventListener(
|
|
494
|
-
google.ima.AdErrorEvent.Type.AD_ERROR,
|
|
495
|
-
(errorEvent) => {
|
|
496
|
-
clearTimeout(timeout);
|
|
497
|
-
console.warn(
|
|
498
|
-
`[IMA] Preload error for ${vastTagUrl}:`,
|
|
499
|
-
errorEvent.getError()
|
|
500
|
-
);
|
|
501
|
-
resolve({
|
|
502
|
-
duration: 30,
|
|
503
|
-
vastTagUrl,
|
|
504
|
-
isPreloaded: false
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
);
|
|
508
|
-
const adsRequest = new google.ima.AdsRequest();
|
|
509
|
-
adsRequest.adTagUrl = vastTagUrl;
|
|
510
|
-
tempAdsLoader.requestAds(adsRequest);
|
|
511
|
-
});
|
|
512
|
-
adInfos.push(adInfo);
|
|
513
|
-
tempAdsLoader.destroy();
|
|
514
|
-
} catch (error) {
|
|
515
|
-
console.warn(`[IMA] Failed to preload ad ${vastTagUrl}:`, error);
|
|
516
|
-
adInfos.push({
|
|
517
|
-
duration: 30,
|
|
518
|
-
vastTagUrl,
|
|
519
|
-
isPreloaded: false
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
preloadedAds = adInfos;
|
|
524
|
-
console.log(
|
|
525
|
-
`[IMA] Preloaded ${adInfos.length} ads with total duration:`,
|
|
526
|
-
adInfos.reduce((sum, ad) => sum + ad.duration, 0)
|
|
527
|
-
);
|
|
528
|
-
return adInfos;
|
|
529
435
|
}
|
|
530
436
|
};
|
|
531
437
|
}
|
|
@@ -674,13 +580,32 @@ function getClientInfo() {
|
|
|
674
580
|
}
|
|
675
581
|
async function getBrowserID(clientInfo) {
|
|
676
582
|
const fingerprintString = JSON.stringify(clientInfo);
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
583
|
+
if (typeof crypto !== "undefined" && crypto.subtle && crypto.subtle.digest) {
|
|
584
|
+
try {
|
|
585
|
+
const hashBuffer = await crypto.subtle.digest(
|
|
586
|
+
"SHA-256",
|
|
587
|
+
new TextEncoder().encode(fingerprintString)
|
|
588
|
+
);
|
|
589
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
590
|
+
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
591
|
+
return hashHex;
|
|
592
|
+
} catch (error) {
|
|
593
|
+
console.warn(
|
|
594
|
+
"[StormcloudVideoPlayer] crypto.subtle.digest failed, using fallback hash:",
|
|
595
|
+
error
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
let hash = 0;
|
|
600
|
+
for (let i = 0; i < fingerprintString.length; i++) {
|
|
601
|
+
const char = fingerprintString.charCodeAt(i);
|
|
602
|
+
hash = (hash << 5) - hash + char;
|
|
603
|
+
hash = hash & hash;
|
|
604
|
+
}
|
|
605
|
+
const fallbackHash = Math.abs(hash).toString(16).padStart(8, "0");
|
|
606
|
+
const timestamp = Date.now().toString(16).padStart(12, "0");
|
|
607
|
+
const random = Math.random().toString(16).substring(2, 14).padStart(12, "0");
|
|
608
|
+
return (fallbackHash + timestamp + random).padEnd(64, "0");
|
|
684
609
|
}
|
|
685
610
|
async function sendInitialTracking(licenseKey) {
|
|
686
611
|
try {
|
|
@@ -758,8 +683,6 @@ var StormcloudVideoPlayer = class {
|
|
|
758
683
|
this.totalAdsInBreak = 0;
|
|
759
684
|
this.showAds = false;
|
|
760
685
|
this.isLiveStream = false;
|
|
761
|
-
this.preloadedAdInfo = [];
|
|
762
|
-
this.cumulativeAdDurationMs = 0;
|
|
763
686
|
this.config = config;
|
|
764
687
|
this.video = config.videoElement;
|
|
765
688
|
this.ima = createImaController(this.video, {
|
|
@@ -800,7 +723,7 @@ var StormcloudVideoPlayer = class {
|
|
|
800
723
|
});
|
|
801
724
|
this.ima.initialize();
|
|
802
725
|
if (this.config.autoplay) {
|
|
803
|
-
await this.video.play()
|
|
726
|
+
await this.video.play()?.catch(() => {
|
|
804
727
|
});
|
|
805
728
|
}
|
|
806
729
|
return;
|
|
@@ -834,7 +757,7 @@ var StormcloudVideoPlayer = class {
|
|
|
834
757
|
});
|
|
835
758
|
this.ima.initialize();
|
|
836
759
|
if (this.config.autoplay) {
|
|
837
|
-
await this.video.play()
|
|
760
|
+
await this.video.play()?.catch(() => {
|
|
838
761
|
});
|
|
839
762
|
}
|
|
840
763
|
});
|
|
@@ -931,28 +854,16 @@ var StormcloudVideoPlayer = class {
|
|
|
931
854
|
this.ima.initialize();
|
|
932
855
|
this.ima.on("all_ads_completed", () => {
|
|
933
856
|
if (!this.inAdBreak) return;
|
|
934
|
-
const actualAdDuration = this.ima.getAdDuration();
|
|
935
|
-
if (actualAdDuration > 0) {
|
|
936
|
-
this.cumulativeAdDurationMs += actualAdDuration * 1e3;
|
|
937
|
-
if (this.config.debugAdTiming) {
|
|
938
|
-
console.log(
|
|
939
|
-
`[StormcloudVideoPlayer] Ad completed. Duration: ${actualAdDuration}s, Cumulative: ${this.cumulativeAdDurationMs}ms`
|
|
940
|
-
);
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
857
|
const remaining = this.getRemainingAdMs();
|
|
944
|
-
|
|
945
|
-
if (shouldContinue && this.adPodQueue.length > 0) {
|
|
858
|
+
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
946
859
|
const next = this.adPodQueue.shift();
|
|
947
860
|
this.currentAdIndex++;
|
|
948
861
|
this.playSingleAd(next).catch(() => {
|
|
949
862
|
});
|
|
950
|
-
} else if (shouldContinue && this.canRequestMoreAds()) {
|
|
951
|
-
this.requestAdditionalAds().catch(() => {
|
|
952
|
-
this.endAdBreak();
|
|
953
|
-
});
|
|
954
863
|
} else {
|
|
955
|
-
this.
|
|
864
|
+
this.currentAdIndex = 0;
|
|
865
|
+
this.totalAdsInBreak = 0;
|
|
866
|
+
this.showAds = false;
|
|
956
867
|
}
|
|
957
868
|
});
|
|
958
869
|
this.ima.on("ad_error", () => {
|
|
@@ -961,16 +872,11 @@ var StormcloudVideoPlayer = class {
|
|
|
961
872
|
}
|
|
962
873
|
if (!this.inAdBreak) return;
|
|
963
874
|
const remaining = this.getRemainingAdMs();
|
|
964
|
-
|
|
965
|
-
if (shouldContinue && this.adPodQueue.length > 0) {
|
|
875
|
+
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
966
876
|
const next = this.adPodQueue.shift();
|
|
967
877
|
this.currentAdIndex++;
|
|
968
878
|
this.playSingleAd(next).catch(() => {
|
|
969
879
|
});
|
|
970
|
-
} else if (shouldContinue && this.canRequestMoreAds()) {
|
|
971
|
-
this.requestAdditionalAds().catch(() => {
|
|
972
|
-
this.handleAdFailure();
|
|
973
|
-
});
|
|
974
880
|
} else {
|
|
975
881
|
this.handleAdFailure();
|
|
976
882
|
}
|
|
@@ -1461,25 +1367,6 @@ var StormcloudVideoPlayer = class {
|
|
|
1461
1367
|
isShowingAds() {
|
|
1462
1368
|
return this.showAds;
|
|
1463
1369
|
}
|
|
1464
|
-
getAdBreakStats() {
|
|
1465
|
-
const remainingDurationMs = this.currentAdBreakTargetDurationMs != null ? Math.max(
|
|
1466
|
-
0,
|
|
1467
|
-
this.currentAdBreakTargetDurationMs - this.cumulativeAdDurationMs
|
|
1468
|
-
) : void 0;
|
|
1469
|
-
const estimatedFillRate = this.currentAdBreakTargetDurationMs != null && this.currentAdBreakTargetDurationMs > 0 ? this.cumulativeAdDurationMs / this.currentAdBreakTargetDurationMs * 100 : void 0;
|
|
1470
|
-
return {
|
|
1471
|
-
isInAdBreak: this.inAdBreak,
|
|
1472
|
-
currentAdIndex: this.currentAdIndex,
|
|
1473
|
-
totalAdsInBreak: this.totalAdsInBreak,
|
|
1474
|
-
targetDurationMs: this.currentAdBreakTargetDurationMs,
|
|
1475
|
-
cumulativeDurationMs: this.cumulativeAdDurationMs,
|
|
1476
|
-
estimatedFillRate,
|
|
1477
|
-
remainingDurationMs
|
|
1478
|
-
};
|
|
1479
|
-
}
|
|
1480
|
-
getPreloadedAdInfo() {
|
|
1481
|
-
return [...this.preloadedAdInfo];
|
|
1482
|
-
}
|
|
1483
1370
|
getStreamType() {
|
|
1484
1371
|
const url = this.config.src.toLowerCase();
|
|
1485
1372
|
if (url.includes(".m3u8") || url.includes("/hls/") || url.includes("application/vnd.apple.mpegurl")) {
|
|
@@ -1503,70 +1390,59 @@ var StormcloudVideoPlayer = class {
|
|
|
1503
1390
|
}
|
|
1504
1391
|
return true;
|
|
1505
1392
|
}
|
|
1506
|
-
async
|
|
1507
|
-
const usp = new URLSearchParams(params || {});
|
|
1508
|
-
const url = `${adstormApiUrl}?${usp.toString()}`;
|
|
1509
|
-
const res = await fetch(url);
|
|
1510
|
-
if (!res.ok) throw new Error(`Failed to fetch adstorm ads: ${res.status}`);
|
|
1511
|
-
const data = await res.json();
|
|
1512
|
-
const tag = data?.adTagUrl || data?.vastTagUrl || data?.tagUrl;
|
|
1513
|
-
if (typeof tag === "string" && tag.length > 0) {
|
|
1514
|
-
this.apiVastTagUrl = tag;
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
async handleAdStart(marker) {
|
|
1393
|
+
async handleAdStart(_marker) {
|
|
1518
1394
|
const scheduled = this.findCurrentOrNextBreak(
|
|
1519
1395
|
this.video.currentTime * 1e3
|
|
1520
1396
|
);
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
"[StormcloudVideoPlayer] Starting ad break with target duration:",
|
|
1533
|
-
{
|
|
1534
|
-
targetDurationMs,
|
|
1535
|
-
scte35Duration: marker.durationSeconds,
|
|
1536
|
-
scheduledDuration: scheduled?.durationMs
|
|
1397
|
+
const tags = this.selectVastTagsForBreak(scheduled);
|
|
1398
|
+
let vastTagUrl;
|
|
1399
|
+
let adsNumber = 1;
|
|
1400
|
+
if (this.apiVastTagUrl) {
|
|
1401
|
+
vastTagUrl = this.apiVastTagUrl;
|
|
1402
|
+
if (this.vastConfig) {
|
|
1403
|
+
const isHls = this.config.src.includes(".m3u8") || this.config.src.includes("hls");
|
|
1404
|
+
if (isHls && this.vastConfig.cue_tones?.number_ads) {
|
|
1405
|
+
adsNumber = this.vastConfig.cue_tones.number_ads;
|
|
1406
|
+
} else if (!isHls && this.vastConfig.timer_vod?.number_ads) {
|
|
1407
|
+
adsNumber = this.vastConfig.timer_vod.number_ads;
|
|
1537
1408
|
}
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1409
|
+
}
|
|
1410
|
+
this.adPodQueue = new Array(adsNumber - 1).fill(vastTagUrl);
|
|
1411
|
+
this.currentAdIndex = 0;
|
|
1412
|
+
this.totalAdsInBreak = adsNumber;
|
|
1413
|
+
if (this.config.debugAdTiming) {
|
|
1414
|
+
console.log(
|
|
1415
|
+
`[StormcloudVideoPlayer] Using API VAST tag with ${adsNumber} ads:`,
|
|
1416
|
+
vastTagUrl
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1419
|
+
} else if (tags && tags.length > 0) {
|
|
1420
|
+
vastTagUrl = tags[0];
|
|
1421
|
+
const rest = tags.slice(1);
|
|
1422
|
+
this.adPodQueue = rest;
|
|
1423
|
+
this.currentAdIndex = 0;
|
|
1424
|
+
this.totalAdsInBreak = tags.length;
|
|
1425
|
+
if (this.config.debugAdTiming) {
|
|
1426
|
+
console.log(
|
|
1427
|
+
"[StormcloudVideoPlayer] Using scheduled VAST tag:",
|
|
1428
|
+
vastTagUrl
|
|
1429
|
+
);
|
|
1430
|
+
}
|
|
1431
|
+
} else {
|
|
1542
1432
|
if (this.config.debugAdTiming) {
|
|
1543
|
-
console.log("[StormcloudVideoPlayer] No
|
|
1433
|
+
console.log("[StormcloudVideoPlayer] No VAST tag available for ad");
|
|
1544
1434
|
}
|
|
1545
1435
|
return;
|
|
1546
1436
|
}
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
this.showAds = true;
|
|
1552
|
-
if (this.config.debugAdTiming) {
|
|
1553
|
-
const totalEstimatedDuration = this.preloadedAdInfo.reduce(
|
|
1554
|
-
(sum, ad) => sum + ad.duration,
|
|
1555
|
-
0
|
|
1556
|
-
);
|
|
1557
|
-
console.log("[StormcloudVideoPlayer] Ad queue built:", {
|
|
1558
|
-
totalAds: adQueue.length,
|
|
1559
|
-
estimatedTotalDuration: totalEstimatedDuration,
|
|
1560
|
-
targetDuration: targetDurationMs ? targetDurationMs / 1e3 : "unknown",
|
|
1561
|
-
fillRate: targetDurationMs ? totalEstimatedDuration * 1e3 / targetDurationMs : "unknown"
|
|
1562
|
-
});
|
|
1437
|
+
if (vastTagUrl) {
|
|
1438
|
+
this.showAds = true;
|
|
1439
|
+
this.currentAdIndex++;
|
|
1440
|
+
await this.playSingleAd(vastTagUrl);
|
|
1563
1441
|
}
|
|
1564
|
-
this.
|
|
1565
|
-
|
|
1566
|
-
if (targetDurationMs != null) {
|
|
1567
|
-
this.expectedAdBreakDurationMs = targetDurationMs;
|
|
1442
|
+
if (this.expectedAdBreakDurationMs == null && scheduled?.durationMs != null) {
|
|
1443
|
+
this.expectedAdBreakDurationMs = scheduled.durationMs;
|
|
1568
1444
|
this.currentAdBreakStartWallClockMs = this.currentAdBreakStartWallClockMs ?? Date.now();
|
|
1569
|
-
this.scheduleAdStopCountdown(
|
|
1445
|
+
this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
|
|
1570
1446
|
}
|
|
1571
1447
|
}
|
|
1572
1448
|
findCurrentOrNextBreak(nowMs) {
|
|
@@ -1683,89 +1559,25 @@ var StormcloudVideoPlayer = class {
|
|
|
1683
1559
|
"[StormcloudVideoPlayer] Handling ad failure - resuming content"
|
|
1684
1560
|
);
|
|
1685
1561
|
}
|
|
1686
|
-
this.endAdBreak();
|
|
1687
|
-
if (this.video.paused) {
|
|
1688
|
-
this.video.play().catch(() => {
|
|
1689
|
-
if (this.config.debugAdTiming) {
|
|
1690
|
-
console.error(
|
|
1691
|
-
"[StormcloudVideoPlayer] Failed to resume video after ad failure"
|
|
1692
|
-
);
|
|
1693
|
-
}
|
|
1694
|
-
});
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
endAdBreak() {
|
|
1698
|
-
if (this.config.debugAdTiming) {
|
|
1699
|
-
const targetDuration = this.currentAdBreakTargetDurationMs ? this.currentAdBreakTargetDurationMs / 1e3 : "unknown";
|
|
1700
|
-
const actualDuration = this.cumulativeAdDurationMs / 1e3;
|
|
1701
|
-
const fillRate = this.currentAdBreakTargetDurationMs ? (this.cumulativeAdDurationMs / this.currentAdBreakTargetDurationMs * 100).toFixed(1) : "unknown";
|
|
1702
|
-
console.log("[StormcloudVideoPlayer] Ad break ended:", {
|
|
1703
|
-
targetDurationSeconds: targetDuration,
|
|
1704
|
-
actualDurationSeconds: actualDuration,
|
|
1705
|
-
fillRate: `${fillRate}%`,
|
|
1706
|
-
totalAdsPlayed: this.currentAdIndex
|
|
1707
|
-
});
|
|
1708
|
-
}
|
|
1709
1562
|
this.inAdBreak = false;
|
|
1710
1563
|
this.expectedAdBreakDurationMs = void 0;
|
|
1711
1564
|
this.currentAdBreakStartWallClockMs = void 0;
|
|
1712
|
-
this.currentAdBreakTargetDurationMs = void 0;
|
|
1713
|
-
this.cumulativeAdDurationMs = 0;
|
|
1714
1565
|
this.clearAdStartTimer();
|
|
1715
1566
|
this.clearAdStopTimer();
|
|
1716
1567
|
this.clearAdFailsafeTimer();
|
|
1717
1568
|
this.adPodQueue = [];
|
|
1718
|
-
this.preloadedAdInfo = [];
|
|
1719
1569
|
this.showAds = false;
|
|
1720
1570
|
this.currentAdIndex = 0;
|
|
1721
1571
|
this.totalAdsInBreak = 0;
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
if (remainingMs > 500) {
|
|
1725
|
-
return true;
|
|
1726
|
-
}
|
|
1727
|
-
if (this.currentAdBreakTargetDurationMs) {
|
|
1728
|
-
const targetRemainingMs = this.currentAdBreakTargetDurationMs - this.cumulativeAdDurationMs;
|
|
1729
|
-
const minAdDuration = this.config.minAdDurationMs ?? 5e3;
|
|
1730
|
-
if (targetRemainingMs > minAdDuration) {
|
|
1572
|
+
if (this.video.paused) {
|
|
1573
|
+
this.video.play()?.catch(() => {
|
|
1731
1574
|
if (this.config.debugAdTiming) {
|
|
1732
|
-
console.
|
|
1733
|
-
|
|
1575
|
+
console.error(
|
|
1576
|
+
"[StormcloudVideoPlayer] Failed to resume video after ad failure"
|
|
1734
1577
|
);
|
|
1735
1578
|
}
|
|
1736
|
-
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
return false;
|
|
1740
|
-
}
|
|
1741
|
-
canRequestMoreAds() {
|
|
1742
|
-
const maxAdsPerBreak = this.config.maxAdsPerBreak ?? 10;
|
|
1743
|
-
if (this.currentAdIndex >= maxAdsPerBreak) {
|
|
1744
|
-
return false;
|
|
1745
|
-
}
|
|
1746
|
-
return !!this.apiVastTagUrl;
|
|
1747
|
-
}
|
|
1748
|
-
async requestAdditionalAds() {
|
|
1749
|
-
if (!this.currentAdBreakTargetDurationMs || !this.apiVastTagUrl) {
|
|
1750
|
-
throw new Error(
|
|
1751
|
-
"Cannot request additional ads without target duration and VAST URL"
|
|
1752
|
-
);
|
|
1753
|
-
}
|
|
1754
|
-
const remainingDurationMs = this.currentAdBreakTargetDurationMs - this.cumulativeAdDurationMs;
|
|
1755
|
-
const estimatedAdDurationMs = this.getEstimatedAdDuration() * 1e3;
|
|
1756
|
-
if (remainingDurationMs < estimatedAdDurationMs * 0.5) {
|
|
1757
|
-
throw new Error("Not enough time remaining for additional ads");
|
|
1758
|
-
}
|
|
1759
|
-
if (this.config.debugAdTiming) {
|
|
1760
|
-
console.log(
|
|
1761
|
-
`[StormcloudVideoPlayer] Requesting additional ads for remaining ${remainingDurationMs}ms`
|
|
1762
|
-
);
|
|
1579
|
+
});
|
|
1763
1580
|
}
|
|
1764
|
-
this.adPodQueue.push(this.apiVastTagUrl);
|
|
1765
|
-
this.totalAdsInBreak++;
|
|
1766
|
-
const next = this.adPodQueue.shift();
|
|
1767
|
-
this.currentAdIndex++;
|
|
1768
|
-
await this.playSingleAd(next);
|
|
1769
1581
|
}
|
|
1770
1582
|
startAdFailsafeTimer() {
|
|
1771
1583
|
this.clearAdFailsafeTimer();
|
|
@@ -1799,109 +1611,6 @@ var StormcloudVideoPlayer = class {
|
|
|
1799
1611
|
}
|
|
1800
1612
|
return [b.vastTagUrl];
|
|
1801
1613
|
}
|
|
1802
|
-
async buildAdQueueForDuration(targetDurationMs) {
|
|
1803
|
-
const adQueue = [];
|
|
1804
|
-
let baseVastTagUrl = this.apiVastTagUrl;
|
|
1805
|
-
if (!baseVastTagUrl) {
|
|
1806
|
-
const scheduled = this.findCurrentOrNextBreak(
|
|
1807
|
-
this.video.currentTime * 1e3
|
|
1808
|
-
);
|
|
1809
|
-
const scheduledTags = this.selectVastTagsForBreak(scheduled);
|
|
1810
|
-
if (scheduledTags && scheduledTags.length > 0) {
|
|
1811
|
-
baseVastTagUrl = scheduledTags[0];
|
|
1812
|
-
}
|
|
1813
|
-
}
|
|
1814
|
-
if (!baseVastTagUrl) {
|
|
1815
|
-
return adQueue;
|
|
1816
|
-
}
|
|
1817
|
-
if (!targetDurationMs) {
|
|
1818
|
-
let adsNumber = 1;
|
|
1819
|
-
if (this.vastConfig) {
|
|
1820
|
-
const isHls = this.config.src.includes(".m3u8") || this.config.src.includes("hls");
|
|
1821
|
-
if (isHls && this.vastConfig.cue_tones?.number_ads) {
|
|
1822
|
-
adsNumber = this.vastConfig.cue_tones.number_ads;
|
|
1823
|
-
} else if (!isHls && this.vastConfig.timer_vod?.number_ads) {
|
|
1824
|
-
adsNumber = this.vastConfig.timer_vod.number_ads;
|
|
1825
|
-
}
|
|
1826
|
-
}
|
|
1827
|
-
return new Array(adsNumber).fill(baseVastTagUrl);
|
|
1828
|
-
}
|
|
1829
|
-
const targetDurationSeconds = targetDurationMs / 1e3;
|
|
1830
|
-
let cumulativeDurationSeconds = 0;
|
|
1831
|
-
const maxAdsToTry = 10;
|
|
1832
|
-
let adsAdded = 0;
|
|
1833
|
-
if (this.config.debugAdTiming) {
|
|
1834
|
-
console.log(
|
|
1835
|
-
`[StormcloudVideoPlayer] Attempting to fill ${targetDurationSeconds}s with ads`
|
|
1836
|
-
);
|
|
1837
|
-
}
|
|
1838
|
-
while (cumulativeDurationSeconds < targetDurationSeconds && adsAdded < maxAdsToTry) {
|
|
1839
|
-
adQueue.push(baseVastTagUrl);
|
|
1840
|
-
adsAdded++;
|
|
1841
|
-
const estimatedAdDuration = this.getEstimatedAdDuration();
|
|
1842
|
-
cumulativeDurationSeconds += estimatedAdDuration;
|
|
1843
|
-
if (this.config.debugAdTiming) {
|
|
1844
|
-
console.log(
|
|
1845
|
-
`[StormcloudVideoPlayer] Added ad ${adsAdded}, cumulative duration: ${cumulativeDurationSeconds}s`
|
|
1846
|
-
);
|
|
1847
|
-
}
|
|
1848
|
-
const remainingDuration = targetDurationSeconds - cumulativeDurationSeconds;
|
|
1849
|
-
const toleranceSeconds = (this.config.adBreakGapToleranceMs ?? 2e3) / 1e3;
|
|
1850
|
-
if (remainingDuration < estimatedAdDuration && remainingDuration >= -toleranceSeconds) {
|
|
1851
|
-
if (this.config.debugAdTiming) {
|
|
1852
|
-
console.log(
|
|
1853
|
-
`[StormcloudVideoPlayer] Within tolerance, adding final ad. Overage: ${-remainingDuration}s`
|
|
1854
|
-
);
|
|
1855
|
-
}
|
|
1856
|
-
break;
|
|
1857
|
-
}
|
|
1858
|
-
if (remainingDuration < estimatedAdDuration && remainingDuration < -toleranceSeconds) {
|
|
1859
|
-
if (this.config.debugAdTiming) {
|
|
1860
|
-
console.log(
|
|
1861
|
-
`[StormcloudVideoPlayer] Would exceed duration by too much, stopping at ${adsAdded} ads`
|
|
1862
|
-
);
|
|
1863
|
-
}
|
|
1864
|
-
break;
|
|
1865
|
-
}
|
|
1866
|
-
}
|
|
1867
|
-
return adQueue;
|
|
1868
|
-
}
|
|
1869
|
-
getEstimatedAdDuration() {
|
|
1870
|
-
if (this.vastConfig) {
|
|
1871
|
-
return 15;
|
|
1872
|
-
}
|
|
1873
|
-
return 30;
|
|
1874
|
-
}
|
|
1875
|
-
async getAdInfoForQueue(adQueue) {
|
|
1876
|
-
if (this.config.enableAdPreloading === false) {
|
|
1877
|
-
if (this.config.debugAdTiming) {
|
|
1878
|
-
console.log(
|
|
1879
|
-
"[StormcloudVideoPlayer] Ad preloading disabled, using estimates"
|
|
1880
|
-
);
|
|
1881
|
-
}
|
|
1882
|
-
return adQueue.map((vastTagUrl) => ({
|
|
1883
|
-
duration: this.getEstimatedAdDuration(),
|
|
1884
|
-
vastTagUrl,
|
|
1885
|
-
isPreloaded: false
|
|
1886
|
-
}));
|
|
1887
|
-
}
|
|
1888
|
-
try {
|
|
1889
|
-
const adInfos = await this.ima.preloadAds(adQueue);
|
|
1890
|
-
return adInfos;
|
|
1891
|
-
} catch (error) {
|
|
1892
|
-
if (this.config.debugAdTiming) {
|
|
1893
|
-
console.warn(
|
|
1894
|
-
"[StormcloudVideoPlayer] Failed to preload ads, using estimates:",
|
|
1895
|
-
error
|
|
1896
|
-
);
|
|
1897
|
-
}
|
|
1898
|
-
return adQueue.map((vastTagUrl) => ({
|
|
1899
|
-
duration: this.getEstimatedAdDuration(),
|
|
1900
|
-
vastTagUrl,
|
|
1901
|
-
isPreloaded: false
|
|
1902
|
-
}));
|
|
1903
|
-
}
|
|
1904
|
-
}
|
|
1905
1614
|
getRemainingAdMs() {
|
|
1906
1615
|
if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null)
|
|
1907
1616
|
return 0;
|