stormcloud-video-player 0.2.35 → 0.3.0
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 +118 -0
- package/dist/stormcloud-vp.min.js +2 -2
- package/lib/index.cjs +234 -60
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +8 -3
- package/lib/index.d.ts +8 -3
- package/lib/index.js +234 -60
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +234 -60
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +8 -3
- package/lib/players/HlsPlayer.cjs +234 -60
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/index.cjs +234 -60
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/ima.cjs +1 -10
- package/lib/sdk/ima.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.cjs +234 -60
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/package.json +1 -1
package/lib/index.cjs
CHANGED
|
@@ -695,19 +695,10 @@ function createImaController(video, options) {
|
|
|
695
695
|
adsManager.addEventListener(
|
|
696
696
|
AdEvent.CONTENT_PAUSE_REQUESTED,
|
|
697
697
|
() => {
|
|
698
|
-
console.log("[DEBUG-FLOW] \u{1F3AF} CONTENT_PAUSE_REQUESTED - Ad
|
|
698
|
+
console.log("[DEBUG-FLOW] \u{1F3AF} CONTENT_PAUSE_REQUESTED - Ad request accepted");
|
|
699
699
|
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
700
700
|
video.pause();
|
|
701
701
|
}
|
|
702
|
-
hideContentVideo();
|
|
703
|
-
if (adContainerEl) {
|
|
704
|
-
adContainerEl.style.pointerEvents = "auto";
|
|
705
|
-
adContainerEl.style.display = "flex";
|
|
706
|
-
adContainerEl.style.backgroundColor = "#000";
|
|
707
|
-
adContainerEl.offsetHeight;
|
|
708
|
-
adContainerEl.style.opacity = "1";
|
|
709
|
-
console.log("[DEBUG-LAYER] \u{1F7E1} Ad container VISIBLE");
|
|
710
|
-
}
|
|
711
702
|
adPlaying = true;
|
|
712
703
|
setAdPlayingFlag(true);
|
|
713
704
|
emit("content_pause");
|
|
@@ -2235,6 +2226,9 @@ var StormcloudVideoPlayer = class {
|
|
|
2235
2226
|
this.activeAdRequestToken = null;
|
|
2236
2227
|
this.adRequestWatchdogToken = null;
|
|
2237
2228
|
this.adFailsafeToken = null;
|
|
2229
|
+
this.fetchedAdDurations = /* @__PURE__ */ new Map();
|
|
2230
|
+
this.targetAdBreakDurationMs = null;
|
|
2231
|
+
this.isAdaptiveMode = false;
|
|
2238
2232
|
initializePolyfills();
|
|
2239
2233
|
const browserOverrides = getBrowserConfigOverrides();
|
|
2240
2234
|
this.config = { ...config, ...browserOverrides };
|
|
@@ -2540,7 +2534,7 @@ var StormcloudVideoPlayer = class {
|
|
|
2540
2534
|
this.clearAdRequestWatchdog();
|
|
2541
2535
|
this.activeAdRequestToken = null;
|
|
2542
2536
|
this.showAds = true;
|
|
2543
|
-
|
|
2537
|
+
console.log("[DEBUG-LAYER] \u{1F3AC} Layers: Main=hidden, Ad=visible, Placeholder=no");
|
|
2544
2538
|
});
|
|
2545
2539
|
this.ima.on("content_resume", () => {
|
|
2546
2540
|
console.log(`[DEBUG-POD] \u23F8\uFE0F content_resume | ad ${this.currentAdIndex}/${this.totalAdsInBreak}, queue=${this.adPodQueue.length}, remaining=${this.getRemainingAdMs()}ms`);
|
|
@@ -2720,6 +2714,12 @@ var StormcloudVideoPlayer = class {
|
|
|
2720
2714
|
});
|
|
2721
2715
|
}
|
|
2722
2716
|
if (marker.type === "start") {
|
|
2717
|
+
if (this.inAdBreak) {
|
|
2718
|
+
console.log(
|
|
2719
|
+
`[DEBUG-POD] \u26A0\uFE0F SCTE-35 start marker ignored - already in ad break (currentTime: ${this.video.currentTime})`
|
|
2720
|
+
);
|
|
2721
|
+
return;
|
|
2722
|
+
}
|
|
2723
2723
|
this.inAdBreak = true;
|
|
2724
2724
|
const durationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : void 0;
|
|
2725
2725
|
this.expectedAdBreakDurationMs = durationMs;
|
|
@@ -2786,6 +2786,9 @@ var StormcloudVideoPlayer = class {
|
|
|
2786
2786
|
return;
|
|
2787
2787
|
}
|
|
2788
2788
|
if (marker.type === "progress" && this.inAdBreak) {
|
|
2789
|
+
console.log(
|
|
2790
|
+
`[DEBUG-POD] \u{1F4CA} SCTE-35 progress marker (currentTime: ${this.video.currentTime})`
|
|
2791
|
+
);
|
|
2789
2792
|
if (marker.durationSeconds != null) {
|
|
2790
2793
|
this.expectedAdBreakDurationMs = marker.durationSeconds * 1e3;
|
|
2791
2794
|
}
|
|
@@ -2797,7 +2800,8 @@ var StormcloudVideoPlayer = class {
|
|
|
2797
2800
|
);
|
|
2798
2801
|
this.scheduleAdStopCountdown(remainingMs);
|
|
2799
2802
|
}
|
|
2800
|
-
if (!this.ima.isAdPlaying()) {
|
|
2803
|
+
if (!this.ima.isAdPlaying() && this.activeAdRequestToken === null) {
|
|
2804
|
+
console.log("[DEBUG-POD] \u{1F4CA} Progress marker: no ad playing, attempting to start");
|
|
2801
2805
|
const scheduled = this.findCurrentOrNextBreak(
|
|
2802
2806
|
this.video.currentTime * 1e3
|
|
2803
2807
|
);
|
|
@@ -2806,25 +2810,31 @@ var StormcloudVideoPlayer = class {
|
|
|
2806
2810
|
const first = tags[0];
|
|
2807
2811
|
const rest = tags.slice(1);
|
|
2808
2812
|
this.adPodQueue = rest;
|
|
2809
|
-
if (!this.showAds) {
|
|
2810
|
-
this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
|
|
2811
|
-
}
|
|
2812
2813
|
this.playSingleAd(first).catch(() => {
|
|
2813
2814
|
});
|
|
2814
2815
|
}
|
|
2816
|
+
} else {
|
|
2817
|
+
console.log(
|
|
2818
|
+
`[DEBUG-POD] \u{1F4CA} Progress marker: ad playing or request active (playing=${this.ima.isAdPlaying()}, token=${this.activeAdRequestToken})`
|
|
2819
|
+
);
|
|
2815
2820
|
}
|
|
2816
2821
|
return;
|
|
2817
2822
|
}
|
|
2818
2823
|
if (marker.type === "end") {
|
|
2824
|
+
console.log(
|
|
2825
|
+
`[DEBUG-POD] \u{1F3C1} SCTE-35 end marker received (currentTime: ${this.video.currentTime})`
|
|
2826
|
+
);
|
|
2819
2827
|
this.inAdBreak = false;
|
|
2820
2828
|
this.expectedAdBreakDurationMs = void 0;
|
|
2821
2829
|
this.currentAdBreakStartWallClockMs = void 0;
|
|
2822
2830
|
this.clearAdStartTimer();
|
|
2823
2831
|
this.clearAdStopTimer();
|
|
2824
2832
|
if (this.ima.isAdPlaying()) {
|
|
2833
|
+
console.log("[DEBUG-POD] \u{1F6D1} Stopping ad due to SCTE-35 end marker");
|
|
2825
2834
|
this.ima.stop().catch(() => {
|
|
2826
2835
|
});
|
|
2827
2836
|
}
|
|
2837
|
+
this.handleAdPodComplete();
|
|
2828
2838
|
return;
|
|
2829
2839
|
}
|
|
2830
2840
|
}
|
|
@@ -3025,7 +3035,7 @@ var StormcloudVideoPlayer = class {
|
|
|
3025
3035
|
}
|
|
3026
3036
|
}
|
|
3027
3037
|
async fetchAdConfiguration() {
|
|
3028
|
-
var _a, _b, _c;
|
|
3038
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
3029
3039
|
const vastMode = this.config.vastMode || "default";
|
|
3030
3040
|
if (this.config.debugAdTiming) {
|
|
3031
3041
|
console.log("[StormcloudVideoPlayer] VAST mode:", vastMode);
|
|
@@ -3096,6 +3106,16 @@ var StormcloudVideoPlayer = class {
|
|
|
3096
3106
|
);
|
|
3097
3107
|
}
|
|
3098
3108
|
}
|
|
3109
|
+
const numberAds = (_g = (_f = (_e = (_d = data.response) == null ? void 0 : _d.options) == null ? void 0 : _e.vast) == null ? void 0 : _f.cue_tones) == null ? void 0 : _g.number_ads;
|
|
3110
|
+
if (numberAds != null && numberAds > 0) {
|
|
3111
|
+
this.apiNumberAds = numberAds;
|
|
3112
|
+
if (this.config.debugAdTiming) {
|
|
3113
|
+
console.log(
|
|
3114
|
+
"[StormcloudVideoPlayer] Number of ads per break from API:",
|
|
3115
|
+
this.apiNumberAds
|
|
3116
|
+
);
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3099
3119
|
}
|
|
3100
3120
|
getCurrentAdIndex() {
|
|
3101
3121
|
return this.currentAdIndex;
|
|
@@ -3103,6 +3123,27 @@ var StormcloudVideoPlayer = class {
|
|
|
3103
3123
|
getTotalAdsInBreak() {
|
|
3104
3124
|
return this.totalAdsInBreak;
|
|
3105
3125
|
}
|
|
3126
|
+
generateVastUrlsWithCorrelators(baseUrl, count) {
|
|
3127
|
+
const urls = [];
|
|
3128
|
+
for (let i = 0; i < count; i++) {
|
|
3129
|
+
try {
|
|
3130
|
+
const url = new URL(baseUrl);
|
|
3131
|
+
const timestamp = Date.now();
|
|
3132
|
+
const random = Math.floor(Math.random() * 1e6);
|
|
3133
|
+
const uniqueCorrelator = `${timestamp}${random}${i}`;
|
|
3134
|
+
url.searchParams.set("correlator", uniqueCorrelator);
|
|
3135
|
+
urls.push(url.toString());
|
|
3136
|
+
} catch (error) {
|
|
3137
|
+
console.warn(
|
|
3138
|
+
"[StormcloudVideoPlayer] Failed to parse VAST URL:",
|
|
3139
|
+
baseUrl,
|
|
3140
|
+
error
|
|
3141
|
+
);
|
|
3142
|
+
urls.push(`${baseUrl}${baseUrl.includes("?") ? "&" : "?"}correlator=${Date.now()}${i}`);
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
return urls;
|
|
3146
|
+
}
|
|
3106
3147
|
isAdPlaying() {
|
|
3107
3148
|
return this.inAdBreak && this.ima.isAdPlaying();
|
|
3108
3149
|
}
|
|
@@ -3141,7 +3182,52 @@ var StormcloudVideoPlayer = class {
|
|
|
3141
3182
|
const tags = this.selectVastTagsForBreak(scheduled);
|
|
3142
3183
|
let vastTagUrls = [];
|
|
3143
3184
|
if (this.apiVastTagUrl) {
|
|
3144
|
-
|
|
3185
|
+
let numberOfAds = 1;
|
|
3186
|
+
if (this.isLiveStream) {
|
|
3187
|
+
const adBreakDurationMs = _marker.durationSeconds != null ? _marker.durationSeconds * 1e3 : scheduled == null ? void 0 : scheduled.durationMs;
|
|
3188
|
+
if (adBreakDurationMs != null && adBreakDurationMs > 0) {
|
|
3189
|
+
this.isAdaptiveMode = true;
|
|
3190
|
+
this.targetAdBreakDurationMs = adBreakDurationMs;
|
|
3191
|
+
this.fetchedAdDurations.clear();
|
|
3192
|
+
numberOfAds = 2;
|
|
3193
|
+
if (this.config.debugAdTiming) {
|
|
3194
|
+
console.log(
|
|
3195
|
+
`[ADAPTIVE-POD] \u{1F4FA} LIVE MODE (ADAPTIVE): Target duration=${adBreakDurationMs}ms | Starting with ${numberOfAds} ads, will fetch actual durations and add more dynamically`
|
|
3196
|
+
);
|
|
3197
|
+
}
|
|
3198
|
+
} else {
|
|
3199
|
+
if (this.config.debugAdTiming) {
|
|
3200
|
+
console.warn(
|
|
3201
|
+
"[DEBUG-POD] \u26A0\uFE0F LIVE MODE: No duration available, defaulting to 1 ad"
|
|
3202
|
+
);
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
} else {
|
|
3206
|
+
this.isAdaptiveMode = false;
|
|
3207
|
+
this.targetAdBreakDurationMs = null;
|
|
3208
|
+
this.fetchedAdDurations.clear();
|
|
3209
|
+
if (this.apiNumberAds && this.apiNumberAds > 1) {
|
|
3210
|
+
numberOfAds = this.apiNumberAds;
|
|
3211
|
+
if (this.config.debugAdTiming) {
|
|
3212
|
+
console.log(
|
|
3213
|
+
`[DEBUG-POD] \u{1F3AC} VOD MODE (FIXED): Using number_ads=${numberOfAds} from API`
|
|
3214
|
+
);
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
if (numberOfAds > 1) {
|
|
3219
|
+
vastTagUrls = this.generateVastUrlsWithCorrelators(
|
|
3220
|
+
this.apiVastTagUrl,
|
|
3221
|
+
numberOfAds
|
|
3222
|
+
);
|
|
3223
|
+
if (this.config.debugAdTiming) {
|
|
3224
|
+
console.log(
|
|
3225
|
+
`[DEBUG-POD] \u{1F504} Generated ${vastTagUrls.length} initial VAST URLs with unique correlators`
|
|
3226
|
+
);
|
|
3227
|
+
}
|
|
3228
|
+
} else {
|
|
3229
|
+
vastTagUrls = [this.apiVastTagUrl];
|
|
3230
|
+
}
|
|
3145
3231
|
} else if (tags && tags.length > 0) {
|
|
3146
3232
|
vastTagUrls = tags;
|
|
3147
3233
|
} else {
|
|
@@ -3155,10 +3241,12 @@ var StormcloudVideoPlayer = class {
|
|
|
3155
3241
|
this.vastToMediaUrlMap.clear();
|
|
3156
3242
|
this.preloadedMediaUrls.clear();
|
|
3157
3243
|
this.preloadingMediaUrls.clear();
|
|
3244
|
+
const currentMuted = this.video.muted;
|
|
3245
|
+
const currentVolume = this.video.volume;
|
|
3158
3246
|
console.log(
|
|
3159
|
-
`[DEBUG-AUDIO] \u{1F4BE} Capturing
|
|
3247
|
+
`[DEBUG-AUDIO] \u{1F4BE} Capturing ORIGINAL state (once) | muted=${currentMuted}, volume=${currentVolume}`
|
|
3160
3248
|
);
|
|
3161
|
-
this.ima.updateOriginalMutedState(
|
|
3249
|
+
this.ima.updateOriginalMutedState(currentMuted, currentVolume);
|
|
3162
3250
|
this.inAdBreak = true;
|
|
3163
3251
|
this.currentAdIndex = 0;
|
|
3164
3252
|
this.totalAdsInBreak = vastTagUrls.length;
|
|
@@ -3372,7 +3460,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3372
3460
|
this.clearAdRequestWatchdog();
|
|
3373
3461
|
this.clearAdFailsafeTimer();
|
|
3374
3462
|
this.activeAdRequestToken = null;
|
|
3375
|
-
this.releaseAdHoldState();
|
|
3376
3463
|
this.preloadingAdUrls.clear();
|
|
3377
3464
|
this.vastToMediaUrlMap.clear();
|
|
3378
3465
|
this.preloadedMediaUrls.clear();
|
|
@@ -3389,13 +3476,18 @@ var StormcloudVideoPlayer = class {
|
|
|
3389
3476
|
this.totalAdsInBreak = 0;
|
|
3390
3477
|
this.ima.stop().catch(() => {
|
|
3391
3478
|
});
|
|
3392
|
-
const
|
|
3393
|
-
const
|
|
3394
|
-
this.video.muted = originalMutedState;
|
|
3395
|
-
this.video.volume = originalVolume;
|
|
3479
|
+
const restoredMuted = this.ima.getOriginalMutedState();
|
|
3480
|
+
const restoredVolume = this.ima.getOriginalVolume();
|
|
3396
3481
|
console.log(
|
|
3397
|
-
`[DEBUG-AUDIO] \u{1F50A}
|
|
3482
|
+
`[DEBUG-AUDIO] \u{1F50A} Audio restored by IMA | muted=${restoredMuted}, volume=${restoredVolume}`
|
|
3398
3483
|
);
|
|
3484
|
+
console.log("[DEBUG-LAYER] \u{1F3AC} Layers: Main=visible, Ad=hidden, Placeholder=no");
|
|
3485
|
+
if (this.video.muted !== restoredMuted) {
|
|
3486
|
+
this.video.muted = restoredMuted;
|
|
3487
|
+
}
|
|
3488
|
+
if (Math.abs(this.video.volume - restoredVolume) > 0.01) {
|
|
3489
|
+
this.video.volume = restoredVolume;
|
|
3490
|
+
}
|
|
3399
3491
|
if (!this.shouldContinueLiveStreamDuringAds() && this.video.paused) {
|
|
3400
3492
|
console.log("[DEBUG-FLOW] \u25B6\uFE0F Resuming main video playback");
|
|
3401
3493
|
(_a = this.video.play()) == null ? void 0 : _a.catch((error) => {
|
|
@@ -3404,20 +3496,20 @@ var StormcloudVideoPlayer = class {
|
|
|
3404
3496
|
}
|
|
3405
3497
|
}
|
|
3406
3498
|
handleAdFailure() {
|
|
3499
|
+
console.log("[DEBUG-POD] \u274C handleAdFailure - skipping to next ad or ending break");
|
|
3407
3500
|
const remaining = this.getRemainingAdMs();
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
return;
|
|
3501
|
+
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
3502
|
+
const nextPreloaded = this.findNextPreloadedAd();
|
|
3503
|
+
if (nextPreloaded) {
|
|
3504
|
+
this.currentAdIndex++;
|
|
3505
|
+
console.log(`[DEBUG-POD] \u27A1\uFE0F Trying next ad after failure (${this.currentAdIndex}/${this.totalAdsInBreak})`);
|
|
3506
|
+
this.playSingleAd(nextPreloaded).catch(() => {
|
|
3507
|
+
this.handleAdPodComplete();
|
|
3508
|
+
});
|
|
3509
|
+
return;
|
|
3510
|
+
}
|
|
3419
3511
|
}
|
|
3420
|
-
console.log("[DEBUG-POD] \u23F9\uFE0F
|
|
3512
|
+
console.log("[DEBUG-POD] \u23F9\uFE0F Ending ad break after failure");
|
|
3421
3513
|
this.handleAdPodComplete();
|
|
3422
3514
|
}
|
|
3423
3515
|
startAdRequestWatchdog(token) {
|
|
@@ -3490,12 +3582,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3490
3582
|
}
|
|
3491
3583
|
return [b.vastTagUrl];
|
|
3492
3584
|
}
|
|
3493
|
-
logQueuedAdUrls(urls) {
|
|
3494
|
-
if (!this.config.debugAdTiming) {
|
|
3495
|
-
return;
|
|
3496
|
-
}
|
|
3497
|
-
console.log("[StormcloudVideoPlayer] ALL ad URLs queued:", urls);
|
|
3498
|
-
}
|
|
3499
3585
|
logAdState(event, extra = {}) {
|
|
3500
3586
|
if (!this.config.debugAdTiming) {
|
|
3501
3587
|
return;
|
|
@@ -3510,22 +3596,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3510
3596
|
...extra
|
|
3511
3597
|
});
|
|
3512
3598
|
}
|
|
3513
|
-
enforceAdHoldState() {
|
|
3514
|
-
this.video.dataset.stormcloudAdPlaying = "true";
|
|
3515
|
-
this.video.muted = true;
|
|
3516
|
-
this.video.volume = 0;
|
|
3517
|
-
console.log("[DEBUG-LAYER] \u{1F512} Enforced ad hold state (main video muted)");
|
|
3518
|
-
if (typeof this.ima.showPlaceholder === "function") {
|
|
3519
|
-
this.ima.showPlaceholder();
|
|
3520
|
-
}
|
|
3521
|
-
}
|
|
3522
|
-
releaseAdHoldState() {
|
|
3523
|
-
delete this.video.dataset.stormcloudAdPlaying;
|
|
3524
|
-
console.log("[DEBUG-LAYER] \u{1F513} Released ad hold state");
|
|
3525
|
-
if (typeof this.ima.hidePlaceholder === "function") {
|
|
3526
|
-
this.ima.hidePlaceholder();
|
|
3527
|
-
}
|
|
3528
|
-
}
|
|
3529
3599
|
async fetchAndParseVastXml(vastTagUrl) {
|
|
3530
3600
|
try {
|
|
3531
3601
|
const response = await fetch(vastTagUrl, { mode: "cors" });
|
|
@@ -3576,6 +3646,99 @@ var StormcloudVideoPlayer = class {
|
|
|
3576
3646
|
}
|
|
3577
3647
|
return mediaUrls;
|
|
3578
3648
|
}
|
|
3649
|
+
async fetchVastDuration(vastTagUrl) {
|
|
3650
|
+
var _a;
|
|
3651
|
+
try {
|
|
3652
|
+
const response = await fetch(vastTagUrl, { mode: "cors" });
|
|
3653
|
+
if (!response.ok) {
|
|
3654
|
+
if (this.config.debugAdTiming) {
|
|
3655
|
+
console.warn(
|
|
3656
|
+
`[ADAPTIVE-POD] Failed to fetch VAST: ${response.status}`
|
|
3657
|
+
);
|
|
3658
|
+
}
|
|
3659
|
+
return null;
|
|
3660
|
+
}
|
|
3661
|
+
const xmlText = await response.text();
|
|
3662
|
+
const parser = new DOMParser();
|
|
3663
|
+
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
|
|
3664
|
+
const durationText = (_a = xmlDoc.querySelector("Duration")) == null ? void 0 : _a.textContent;
|
|
3665
|
+
if (!durationText) {
|
|
3666
|
+
if (this.config.debugAdTiming) {
|
|
3667
|
+
console.warn("[ADAPTIVE-POD] No Duration element found in VAST");
|
|
3668
|
+
}
|
|
3669
|
+
return null;
|
|
3670
|
+
}
|
|
3671
|
+
const durationParts = durationText.split(":");
|
|
3672
|
+
const durationSeconds = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
3673
|
+
return durationSeconds;
|
|
3674
|
+
} catch (error) {
|
|
3675
|
+
if (this.config.debugAdTiming) {
|
|
3676
|
+
console.warn(
|
|
3677
|
+
`[ADAPTIVE-POD] Error fetching VAST duration from ${vastTagUrl}:`,
|
|
3678
|
+
error
|
|
3679
|
+
);
|
|
3680
|
+
}
|
|
3681
|
+
return null;
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
calculateAdditionalAdsNeeded() {
|
|
3685
|
+
if (!this.isAdaptiveMode || this.targetAdBreakDurationMs === null) {
|
|
3686
|
+
return 0;
|
|
3687
|
+
}
|
|
3688
|
+
let totalFetchedDurationMs = 0;
|
|
3689
|
+
for (const duration of this.fetchedAdDurations.values()) {
|
|
3690
|
+
totalFetchedDurationMs += duration * 1e3;
|
|
3691
|
+
}
|
|
3692
|
+
const remainingTimeMs = this.targetAdBreakDurationMs - totalFetchedDurationMs;
|
|
3693
|
+
if (remainingTimeMs <= 0) {
|
|
3694
|
+
if (this.config.debugAdTiming) {
|
|
3695
|
+
console.log(
|
|
3696
|
+
`[ADAPTIVE-POD] \u2705 Target duration reached: ${totalFetchedDurationMs}ms / ${this.targetAdBreakDurationMs}ms`
|
|
3697
|
+
);
|
|
3698
|
+
}
|
|
3699
|
+
return 0;
|
|
3700
|
+
}
|
|
3701
|
+
const fetchedCount = this.fetchedAdDurations.size;
|
|
3702
|
+
const averageDurationMs = fetchedCount > 0 ? totalFetchedDurationMs / fetchedCount : 30 * 1e3;
|
|
3703
|
+
const additionalAds = Math.ceil(remainingTimeMs / averageDurationMs);
|
|
3704
|
+
if (this.config.debugAdTiming) {
|
|
3705
|
+
console.log(
|
|
3706
|
+
`[ADAPTIVE-POD] \u{1F4CA} Need ${additionalAds} more ads | Fetched: ${totalFetchedDurationMs}ms / Target: ${this.targetAdBreakDurationMs}ms | Remaining: ${remainingTimeMs}ms | Avg duration: ${averageDurationMs}ms`
|
|
3707
|
+
);
|
|
3708
|
+
}
|
|
3709
|
+
return additionalAds;
|
|
3710
|
+
}
|
|
3711
|
+
async addAdaptiveAdsToQueue() {
|
|
3712
|
+
if (!this.isAdaptiveMode || !this.apiVastTagUrl) {
|
|
3713
|
+
return;
|
|
3714
|
+
}
|
|
3715
|
+
const additionalAds = this.calculateAdditionalAdsNeeded();
|
|
3716
|
+
if (additionalAds <= 0) {
|
|
3717
|
+
return;
|
|
3718
|
+
}
|
|
3719
|
+
const newUrls = this.generateVastUrlsWithCorrelators(
|
|
3720
|
+
this.apiVastTagUrl,
|
|
3721
|
+
additionalAds
|
|
3722
|
+
);
|
|
3723
|
+
if (this.config.debugAdTiming) {
|
|
3724
|
+
console.log(
|
|
3725
|
+
`[ADAPTIVE-POD] \u{1F504} Adding ${newUrls.length} additional VAST URLs to queue`
|
|
3726
|
+
);
|
|
3727
|
+
}
|
|
3728
|
+
this.adPodAllUrls.push(...newUrls);
|
|
3729
|
+
this.adPodQueue.push(...newUrls);
|
|
3730
|
+
this.totalAdsInBreak += newUrls.length;
|
|
3731
|
+
for (const url of newUrls) {
|
|
3732
|
+
this.preloadSingleAd(url).catch((error) => {
|
|
3733
|
+
if (this.config.debugAdTiming) {
|
|
3734
|
+
console.warn(
|
|
3735
|
+
`[ADAPTIVE-POD] Failed to preload adaptive ad:`,
|
|
3736
|
+
error
|
|
3737
|
+
);
|
|
3738
|
+
}
|
|
3739
|
+
});
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3579
3742
|
async preloadMediaFile(mediaUrl) {
|
|
3580
3743
|
if (this.preloadedMediaUrls.has(mediaUrl)) {
|
|
3581
3744
|
return;
|
|
@@ -3645,6 +3808,18 @@ var StormcloudVideoPlayer = class {
|
|
|
3645
3808
|
async preloadSingleAd(vastTagUrl) {
|
|
3646
3809
|
if (!vastTagUrl) return;
|
|
3647
3810
|
try {
|
|
3811
|
+
if (this.isAdaptiveMode && !this.fetchedAdDurations.has(vastTagUrl)) {
|
|
3812
|
+
const duration = await this.fetchVastDuration(vastTagUrl);
|
|
3813
|
+
if (duration !== null) {
|
|
3814
|
+
this.fetchedAdDurations.set(vastTagUrl, duration);
|
|
3815
|
+
if (this.config.debugAdTiming) {
|
|
3816
|
+
console.log(
|
|
3817
|
+
`[ADAPTIVE-POD] \u2713 Fetched ad duration: ${duration}s (${this.fetchedAdDurations.size} ads fetched so far)`
|
|
3818
|
+
);
|
|
3819
|
+
}
|
|
3820
|
+
await this.addAdaptiveAdsToQueue();
|
|
3821
|
+
}
|
|
3822
|
+
}
|
|
3648
3823
|
if (this.ima.preloadAds && !this.ima.hasPreloadedAd(vastTagUrl)) {
|
|
3649
3824
|
if (!this.preloadingAdUrls.has(vastTagUrl)) {
|
|
3650
3825
|
if (this.config.debugAdTiming) {
|
|
@@ -3890,7 +4065,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3890
4065
|
}
|
|
3891
4066
|
(_a = this.hls) == null ? void 0 : _a.destroy();
|
|
3892
4067
|
(_b = this.ima) == null ? void 0 : _b.destroy();
|
|
3893
|
-
this.releaseAdHoldState();
|
|
3894
4068
|
this.preloadingAdUrls.clear();
|
|
3895
4069
|
this.vastToMediaUrlMap.clear();
|
|
3896
4070
|
this.preloadedMediaUrls.clear();
|