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
|
@@ -628,19 +628,10 @@ function createImaController(video, options) {
|
|
|
628
628
|
adsManager.addEventListener(
|
|
629
629
|
AdEvent.CONTENT_PAUSE_REQUESTED,
|
|
630
630
|
() => {
|
|
631
|
-
console.log("[DEBUG-FLOW] \u{1F3AF} CONTENT_PAUSE_REQUESTED - Ad
|
|
631
|
+
console.log("[DEBUG-FLOW] \u{1F3AF} CONTENT_PAUSE_REQUESTED - Ad request accepted");
|
|
632
632
|
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
633
633
|
video.pause();
|
|
634
634
|
}
|
|
635
|
-
hideContentVideo();
|
|
636
|
-
if (adContainerEl) {
|
|
637
|
-
adContainerEl.style.pointerEvents = "auto";
|
|
638
|
-
adContainerEl.style.display = "flex";
|
|
639
|
-
adContainerEl.style.backgroundColor = "#000";
|
|
640
|
-
adContainerEl.offsetHeight;
|
|
641
|
-
adContainerEl.style.opacity = "1";
|
|
642
|
-
console.log("[DEBUG-LAYER] \u{1F7E1} Ad container VISIBLE");
|
|
643
|
-
}
|
|
644
635
|
adPlaying = true;
|
|
645
636
|
setAdPlayingFlag(true);
|
|
646
637
|
emit("content_pause");
|
|
@@ -2168,6 +2159,9 @@ var StormcloudVideoPlayer = class {
|
|
|
2168
2159
|
this.activeAdRequestToken = null;
|
|
2169
2160
|
this.adRequestWatchdogToken = null;
|
|
2170
2161
|
this.adFailsafeToken = null;
|
|
2162
|
+
this.fetchedAdDurations = /* @__PURE__ */ new Map();
|
|
2163
|
+
this.targetAdBreakDurationMs = null;
|
|
2164
|
+
this.isAdaptiveMode = false;
|
|
2171
2165
|
initializePolyfills();
|
|
2172
2166
|
const browserOverrides = getBrowserConfigOverrides();
|
|
2173
2167
|
this.config = { ...config, ...browserOverrides };
|
|
@@ -2473,7 +2467,7 @@ var StormcloudVideoPlayer = class {
|
|
|
2473
2467
|
this.clearAdRequestWatchdog();
|
|
2474
2468
|
this.activeAdRequestToken = null;
|
|
2475
2469
|
this.showAds = true;
|
|
2476
|
-
|
|
2470
|
+
console.log("[DEBUG-LAYER] \u{1F3AC} Layers: Main=hidden, Ad=visible, Placeholder=no");
|
|
2477
2471
|
});
|
|
2478
2472
|
this.ima.on("content_resume", () => {
|
|
2479
2473
|
console.log(`[DEBUG-POD] \u23F8\uFE0F content_resume | ad ${this.currentAdIndex}/${this.totalAdsInBreak}, queue=${this.adPodQueue.length}, remaining=${this.getRemainingAdMs()}ms`);
|
|
@@ -2653,6 +2647,12 @@ var StormcloudVideoPlayer = class {
|
|
|
2653
2647
|
});
|
|
2654
2648
|
}
|
|
2655
2649
|
if (marker.type === "start") {
|
|
2650
|
+
if (this.inAdBreak) {
|
|
2651
|
+
console.log(
|
|
2652
|
+
`[DEBUG-POD] \u26A0\uFE0F SCTE-35 start marker ignored - already in ad break (currentTime: ${this.video.currentTime})`
|
|
2653
|
+
);
|
|
2654
|
+
return;
|
|
2655
|
+
}
|
|
2656
2656
|
this.inAdBreak = true;
|
|
2657
2657
|
const durationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : void 0;
|
|
2658
2658
|
this.expectedAdBreakDurationMs = durationMs;
|
|
@@ -2719,6 +2719,9 @@ var StormcloudVideoPlayer = class {
|
|
|
2719
2719
|
return;
|
|
2720
2720
|
}
|
|
2721
2721
|
if (marker.type === "progress" && this.inAdBreak) {
|
|
2722
|
+
console.log(
|
|
2723
|
+
`[DEBUG-POD] \u{1F4CA} SCTE-35 progress marker (currentTime: ${this.video.currentTime})`
|
|
2724
|
+
);
|
|
2722
2725
|
if (marker.durationSeconds != null) {
|
|
2723
2726
|
this.expectedAdBreakDurationMs = marker.durationSeconds * 1e3;
|
|
2724
2727
|
}
|
|
@@ -2730,7 +2733,8 @@ var StormcloudVideoPlayer = class {
|
|
|
2730
2733
|
);
|
|
2731
2734
|
this.scheduleAdStopCountdown(remainingMs);
|
|
2732
2735
|
}
|
|
2733
|
-
if (!this.ima.isAdPlaying()) {
|
|
2736
|
+
if (!this.ima.isAdPlaying() && this.activeAdRequestToken === null) {
|
|
2737
|
+
console.log("[DEBUG-POD] \u{1F4CA} Progress marker: no ad playing, attempting to start");
|
|
2734
2738
|
const scheduled = this.findCurrentOrNextBreak(
|
|
2735
2739
|
this.video.currentTime * 1e3
|
|
2736
2740
|
);
|
|
@@ -2739,25 +2743,31 @@ var StormcloudVideoPlayer = class {
|
|
|
2739
2743
|
const first = tags[0];
|
|
2740
2744
|
const rest = tags.slice(1);
|
|
2741
2745
|
this.adPodQueue = rest;
|
|
2742
|
-
if (!this.showAds) {
|
|
2743
|
-
this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
|
|
2744
|
-
}
|
|
2745
2746
|
this.playSingleAd(first).catch(() => {
|
|
2746
2747
|
});
|
|
2747
2748
|
}
|
|
2749
|
+
} else {
|
|
2750
|
+
console.log(
|
|
2751
|
+
`[DEBUG-POD] \u{1F4CA} Progress marker: ad playing or request active (playing=${this.ima.isAdPlaying()}, token=${this.activeAdRequestToken})`
|
|
2752
|
+
);
|
|
2748
2753
|
}
|
|
2749
2754
|
return;
|
|
2750
2755
|
}
|
|
2751
2756
|
if (marker.type === "end") {
|
|
2757
|
+
console.log(
|
|
2758
|
+
`[DEBUG-POD] \u{1F3C1} SCTE-35 end marker received (currentTime: ${this.video.currentTime})`
|
|
2759
|
+
);
|
|
2752
2760
|
this.inAdBreak = false;
|
|
2753
2761
|
this.expectedAdBreakDurationMs = void 0;
|
|
2754
2762
|
this.currentAdBreakStartWallClockMs = void 0;
|
|
2755
2763
|
this.clearAdStartTimer();
|
|
2756
2764
|
this.clearAdStopTimer();
|
|
2757
2765
|
if (this.ima.isAdPlaying()) {
|
|
2766
|
+
console.log("[DEBUG-POD] \u{1F6D1} Stopping ad due to SCTE-35 end marker");
|
|
2758
2767
|
this.ima.stop().catch(() => {
|
|
2759
2768
|
});
|
|
2760
2769
|
}
|
|
2770
|
+
this.handleAdPodComplete();
|
|
2761
2771
|
return;
|
|
2762
2772
|
}
|
|
2763
2773
|
}
|
|
@@ -2958,7 +2968,7 @@ var StormcloudVideoPlayer = class {
|
|
|
2958
2968
|
}
|
|
2959
2969
|
}
|
|
2960
2970
|
async fetchAdConfiguration() {
|
|
2961
|
-
var _a, _b, _c;
|
|
2971
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
2962
2972
|
const vastMode = this.config.vastMode || "default";
|
|
2963
2973
|
if (this.config.debugAdTiming) {
|
|
2964
2974
|
console.log("[StormcloudVideoPlayer] VAST mode:", vastMode);
|
|
@@ -3029,6 +3039,16 @@ var StormcloudVideoPlayer = class {
|
|
|
3029
3039
|
);
|
|
3030
3040
|
}
|
|
3031
3041
|
}
|
|
3042
|
+
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;
|
|
3043
|
+
if (numberAds != null && numberAds > 0) {
|
|
3044
|
+
this.apiNumberAds = numberAds;
|
|
3045
|
+
if (this.config.debugAdTiming) {
|
|
3046
|
+
console.log(
|
|
3047
|
+
"[StormcloudVideoPlayer] Number of ads per break from API:",
|
|
3048
|
+
this.apiNumberAds
|
|
3049
|
+
);
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3032
3052
|
}
|
|
3033
3053
|
getCurrentAdIndex() {
|
|
3034
3054
|
return this.currentAdIndex;
|
|
@@ -3036,6 +3056,27 @@ var StormcloudVideoPlayer = class {
|
|
|
3036
3056
|
getTotalAdsInBreak() {
|
|
3037
3057
|
return this.totalAdsInBreak;
|
|
3038
3058
|
}
|
|
3059
|
+
generateVastUrlsWithCorrelators(baseUrl, count) {
|
|
3060
|
+
const urls = [];
|
|
3061
|
+
for (let i = 0; i < count; i++) {
|
|
3062
|
+
try {
|
|
3063
|
+
const url = new URL(baseUrl);
|
|
3064
|
+
const timestamp = Date.now();
|
|
3065
|
+
const random = Math.floor(Math.random() * 1e6);
|
|
3066
|
+
const uniqueCorrelator = `${timestamp}${random}${i}`;
|
|
3067
|
+
url.searchParams.set("correlator", uniqueCorrelator);
|
|
3068
|
+
urls.push(url.toString());
|
|
3069
|
+
} catch (error) {
|
|
3070
|
+
console.warn(
|
|
3071
|
+
"[StormcloudVideoPlayer] Failed to parse VAST URL:",
|
|
3072
|
+
baseUrl,
|
|
3073
|
+
error
|
|
3074
|
+
);
|
|
3075
|
+
urls.push(`${baseUrl}${baseUrl.includes("?") ? "&" : "?"}correlator=${Date.now()}${i}`);
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
return urls;
|
|
3079
|
+
}
|
|
3039
3080
|
isAdPlaying() {
|
|
3040
3081
|
return this.inAdBreak && this.ima.isAdPlaying();
|
|
3041
3082
|
}
|
|
@@ -3074,7 +3115,52 @@ var StormcloudVideoPlayer = class {
|
|
|
3074
3115
|
const tags = this.selectVastTagsForBreak(scheduled);
|
|
3075
3116
|
let vastTagUrls = [];
|
|
3076
3117
|
if (this.apiVastTagUrl) {
|
|
3077
|
-
|
|
3118
|
+
let numberOfAds = 1;
|
|
3119
|
+
if (this.isLiveStream) {
|
|
3120
|
+
const adBreakDurationMs = _marker.durationSeconds != null ? _marker.durationSeconds * 1e3 : scheduled == null ? void 0 : scheduled.durationMs;
|
|
3121
|
+
if (adBreakDurationMs != null && adBreakDurationMs > 0) {
|
|
3122
|
+
this.isAdaptiveMode = true;
|
|
3123
|
+
this.targetAdBreakDurationMs = adBreakDurationMs;
|
|
3124
|
+
this.fetchedAdDurations.clear();
|
|
3125
|
+
numberOfAds = 2;
|
|
3126
|
+
if (this.config.debugAdTiming) {
|
|
3127
|
+
console.log(
|
|
3128
|
+
`[ADAPTIVE-POD] \u{1F4FA} LIVE MODE (ADAPTIVE): Target duration=${adBreakDurationMs}ms | Starting with ${numberOfAds} ads, will fetch actual durations and add more dynamically`
|
|
3129
|
+
);
|
|
3130
|
+
}
|
|
3131
|
+
} else {
|
|
3132
|
+
if (this.config.debugAdTiming) {
|
|
3133
|
+
console.warn(
|
|
3134
|
+
"[DEBUG-POD] \u26A0\uFE0F LIVE MODE: No duration available, defaulting to 1 ad"
|
|
3135
|
+
);
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3138
|
+
} else {
|
|
3139
|
+
this.isAdaptiveMode = false;
|
|
3140
|
+
this.targetAdBreakDurationMs = null;
|
|
3141
|
+
this.fetchedAdDurations.clear();
|
|
3142
|
+
if (this.apiNumberAds && this.apiNumberAds > 1) {
|
|
3143
|
+
numberOfAds = this.apiNumberAds;
|
|
3144
|
+
if (this.config.debugAdTiming) {
|
|
3145
|
+
console.log(
|
|
3146
|
+
`[DEBUG-POD] \u{1F3AC} VOD MODE (FIXED): Using number_ads=${numberOfAds} from API`
|
|
3147
|
+
);
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
if (numberOfAds > 1) {
|
|
3152
|
+
vastTagUrls = this.generateVastUrlsWithCorrelators(
|
|
3153
|
+
this.apiVastTagUrl,
|
|
3154
|
+
numberOfAds
|
|
3155
|
+
);
|
|
3156
|
+
if (this.config.debugAdTiming) {
|
|
3157
|
+
console.log(
|
|
3158
|
+
`[DEBUG-POD] \u{1F504} Generated ${vastTagUrls.length} initial VAST URLs with unique correlators`
|
|
3159
|
+
);
|
|
3160
|
+
}
|
|
3161
|
+
} else {
|
|
3162
|
+
vastTagUrls = [this.apiVastTagUrl];
|
|
3163
|
+
}
|
|
3078
3164
|
} else if (tags && tags.length > 0) {
|
|
3079
3165
|
vastTagUrls = tags;
|
|
3080
3166
|
} else {
|
|
@@ -3088,10 +3174,12 @@ var StormcloudVideoPlayer = class {
|
|
|
3088
3174
|
this.vastToMediaUrlMap.clear();
|
|
3089
3175
|
this.preloadedMediaUrls.clear();
|
|
3090
3176
|
this.preloadingMediaUrls.clear();
|
|
3177
|
+
const currentMuted = this.video.muted;
|
|
3178
|
+
const currentVolume = this.video.volume;
|
|
3091
3179
|
console.log(
|
|
3092
|
-
`[DEBUG-AUDIO] \u{1F4BE} Capturing
|
|
3180
|
+
`[DEBUG-AUDIO] \u{1F4BE} Capturing ORIGINAL state (once) | muted=${currentMuted}, volume=${currentVolume}`
|
|
3093
3181
|
);
|
|
3094
|
-
this.ima.updateOriginalMutedState(
|
|
3182
|
+
this.ima.updateOriginalMutedState(currentMuted, currentVolume);
|
|
3095
3183
|
this.inAdBreak = true;
|
|
3096
3184
|
this.currentAdIndex = 0;
|
|
3097
3185
|
this.totalAdsInBreak = vastTagUrls.length;
|
|
@@ -3305,7 +3393,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3305
3393
|
this.clearAdRequestWatchdog();
|
|
3306
3394
|
this.clearAdFailsafeTimer();
|
|
3307
3395
|
this.activeAdRequestToken = null;
|
|
3308
|
-
this.releaseAdHoldState();
|
|
3309
3396
|
this.preloadingAdUrls.clear();
|
|
3310
3397
|
this.vastToMediaUrlMap.clear();
|
|
3311
3398
|
this.preloadedMediaUrls.clear();
|
|
@@ -3322,13 +3409,18 @@ var StormcloudVideoPlayer = class {
|
|
|
3322
3409
|
this.totalAdsInBreak = 0;
|
|
3323
3410
|
this.ima.stop().catch(() => {
|
|
3324
3411
|
});
|
|
3325
|
-
const
|
|
3326
|
-
const
|
|
3327
|
-
this.video.muted = originalMutedState;
|
|
3328
|
-
this.video.volume = originalVolume;
|
|
3412
|
+
const restoredMuted = this.ima.getOriginalMutedState();
|
|
3413
|
+
const restoredVolume = this.ima.getOriginalVolume();
|
|
3329
3414
|
console.log(
|
|
3330
|
-
`[DEBUG-AUDIO] \u{1F50A}
|
|
3415
|
+
`[DEBUG-AUDIO] \u{1F50A} Audio restored by IMA | muted=${restoredMuted}, volume=${restoredVolume}`
|
|
3331
3416
|
);
|
|
3417
|
+
console.log("[DEBUG-LAYER] \u{1F3AC} Layers: Main=visible, Ad=hidden, Placeholder=no");
|
|
3418
|
+
if (this.video.muted !== restoredMuted) {
|
|
3419
|
+
this.video.muted = restoredMuted;
|
|
3420
|
+
}
|
|
3421
|
+
if (Math.abs(this.video.volume - restoredVolume) > 0.01) {
|
|
3422
|
+
this.video.volume = restoredVolume;
|
|
3423
|
+
}
|
|
3332
3424
|
if (!this.shouldContinueLiveStreamDuringAds() && this.video.paused) {
|
|
3333
3425
|
console.log("[DEBUG-FLOW] \u25B6\uFE0F Resuming main video playback");
|
|
3334
3426
|
(_a = this.video.play()) == null ? void 0 : _a.catch((error) => {
|
|
@@ -3337,20 +3429,20 @@ var StormcloudVideoPlayer = class {
|
|
|
3337
3429
|
}
|
|
3338
3430
|
}
|
|
3339
3431
|
handleAdFailure() {
|
|
3432
|
+
console.log("[DEBUG-POD] \u274C handleAdFailure - skipping to next ad or ending break");
|
|
3340
3433
|
const remaining = this.getRemainingAdMs();
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
return;
|
|
3434
|
+
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
3435
|
+
const nextPreloaded = this.findNextPreloadedAd();
|
|
3436
|
+
if (nextPreloaded) {
|
|
3437
|
+
this.currentAdIndex++;
|
|
3438
|
+
console.log(`[DEBUG-POD] \u27A1\uFE0F Trying next ad after failure (${this.currentAdIndex}/${this.totalAdsInBreak})`);
|
|
3439
|
+
this.playSingleAd(nextPreloaded).catch(() => {
|
|
3440
|
+
this.handleAdPodComplete();
|
|
3441
|
+
});
|
|
3442
|
+
return;
|
|
3443
|
+
}
|
|
3352
3444
|
}
|
|
3353
|
-
console.log("[DEBUG-POD] \u23F9\uFE0F
|
|
3445
|
+
console.log("[DEBUG-POD] \u23F9\uFE0F Ending ad break after failure");
|
|
3354
3446
|
this.handleAdPodComplete();
|
|
3355
3447
|
}
|
|
3356
3448
|
startAdRequestWatchdog(token) {
|
|
@@ -3423,12 +3515,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3423
3515
|
}
|
|
3424
3516
|
return [b.vastTagUrl];
|
|
3425
3517
|
}
|
|
3426
|
-
logQueuedAdUrls(urls) {
|
|
3427
|
-
if (!this.config.debugAdTiming) {
|
|
3428
|
-
return;
|
|
3429
|
-
}
|
|
3430
|
-
console.log("[StormcloudVideoPlayer] ALL ad URLs queued:", urls);
|
|
3431
|
-
}
|
|
3432
3518
|
logAdState(event, extra = {}) {
|
|
3433
3519
|
if (!this.config.debugAdTiming) {
|
|
3434
3520
|
return;
|
|
@@ -3443,22 +3529,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3443
3529
|
...extra
|
|
3444
3530
|
});
|
|
3445
3531
|
}
|
|
3446
|
-
enforceAdHoldState() {
|
|
3447
|
-
this.video.dataset.stormcloudAdPlaying = "true";
|
|
3448
|
-
this.video.muted = true;
|
|
3449
|
-
this.video.volume = 0;
|
|
3450
|
-
console.log("[DEBUG-LAYER] \u{1F512} Enforced ad hold state (main video muted)");
|
|
3451
|
-
if (typeof this.ima.showPlaceholder === "function") {
|
|
3452
|
-
this.ima.showPlaceholder();
|
|
3453
|
-
}
|
|
3454
|
-
}
|
|
3455
|
-
releaseAdHoldState() {
|
|
3456
|
-
delete this.video.dataset.stormcloudAdPlaying;
|
|
3457
|
-
console.log("[DEBUG-LAYER] \u{1F513} Released ad hold state");
|
|
3458
|
-
if (typeof this.ima.hidePlaceholder === "function") {
|
|
3459
|
-
this.ima.hidePlaceholder();
|
|
3460
|
-
}
|
|
3461
|
-
}
|
|
3462
3532
|
async fetchAndParseVastXml(vastTagUrl) {
|
|
3463
3533
|
try {
|
|
3464
3534
|
const response = await fetch(vastTagUrl, { mode: "cors" });
|
|
@@ -3509,6 +3579,99 @@ var StormcloudVideoPlayer = class {
|
|
|
3509
3579
|
}
|
|
3510
3580
|
return mediaUrls;
|
|
3511
3581
|
}
|
|
3582
|
+
async fetchVastDuration(vastTagUrl) {
|
|
3583
|
+
var _a;
|
|
3584
|
+
try {
|
|
3585
|
+
const response = await fetch(vastTagUrl, { mode: "cors" });
|
|
3586
|
+
if (!response.ok) {
|
|
3587
|
+
if (this.config.debugAdTiming) {
|
|
3588
|
+
console.warn(
|
|
3589
|
+
`[ADAPTIVE-POD] Failed to fetch VAST: ${response.status}`
|
|
3590
|
+
);
|
|
3591
|
+
}
|
|
3592
|
+
return null;
|
|
3593
|
+
}
|
|
3594
|
+
const xmlText = await response.text();
|
|
3595
|
+
const parser = new DOMParser();
|
|
3596
|
+
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
|
|
3597
|
+
const durationText = (_a = xmlDoc.querySelector("Duration")) == null ? void 0 : _a.textContent;
|
|
3598
|
+
if (!durationText) {
|
|
3599
|
+
if (this.config.debugAdTiming) {
|
|
3600
|
+
console.warn("[ADAPTIVE-POD] No Duration element found in VAST");
|
|
3601
|
+
}
|
|
3602
|
+
return null;
|
|
3603
|
+
}
|
|
3604
|
+
const durationParts = durationText.split(":");
|
|
3605
|
+
const durationSeconds = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
3606
|
+
return durationSeconds;
|
|
3607
|
+
} catch (error) {
|
|
3608
|
+
if (this.config.debugAdTiming) {
|
|
3609
|
+
console.warn(
|
|
3610
|
+
`[ADAPTIVE-POD] Error fetching VAST duration from ${vastTagUrl}:`,
|
|
3611
|
+
error
|
|
3612
|
+
);
|
|
3613
|
+
}
|
|
3614
|
+
return null;
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
3617
|
+
calculateAdditionalAdsNeeded() {
|
|
3618
|
+
if (!this.isAdaptiveMode || this.targetAdBreakDurationMs === null) {
|
|
3619
|
+
return 0;
|
|
3620
|
+
}
|
|
3621
|
+
let totalFetchedDurationMs = 0;
|
|
3622
|
+
for (const duration of this.fetchedAdDurations.values()) {
|
|
3623
|
+
totalFetchedDurationMs += duration * 1e3;
|
|
3624
|
+
}
|
|
3625
|
+
const remainingTimeMs = this.targetAdBreakDurationMs - totalFetchedDurationMs;
|
|
3626
|
+
if (remainingTimeMs <= 0) {
|
|
3627
|
+
if (this.config.debugAdTiming) {
|
|
3628
|
+
console.log(
|
|
3629
|
+
`[ADAPTIVE-POD] \u2705 Target duration reached: ${totalFetchedDurationMs}ms / ${this.targetAdBreakDurationMs}ms`
|
|
3630
|
+
);
|
|
3631
|
+
}
|
|
3632
|
+
return 0;
|
|
3633
|
+
}
|
|
3634
|
+
const fetchedCount = this.fetchedAdDurations.size;
|
|
3635
|
+
const averageDurationMs = fetchedCount > 0 ? totalFetchedDurationMs / fetchedCount : 30 * 1e3;
|
|
3636
|
+
const additionalAds = Math.ceil(remainingTimeMs / averageDurationMs);
|
|
3637
|
+
if (this.config.debugAdTiming) {
|
|
3638
|
+
console.log(
|
|
3639
|
+
`[ADAPTIVE-POD] \u{1F4CA} Need ${additionalAds} more ads | Fetched: ${totalFetchedDurationMs}ms / Target: ${this.targetAdBreakDurationMs}ms | Remaining: ${remainingTimeMs}ms | Avg duration: ${averageDurationMs}ms`
|
|
3640
|
+
);
|
|
3641
|
+
}
|
|
3642
|
+
return additionalAds;
|
|
3643
|
+
}
|
|
3644
|
+
async addAdaptiveAdsToQueue() {
|
|
3645
|
+
if (!this.isAdaptiveMode || !this.apiVastTagUrl) {
|
|
3646
|
+
return;
|
|
3647
|
+
}
|
|
3648
|
+
const additionalAds = this.calculateAdditionalAdsNeeded();
|
|
3649
|
+
if (additionalAds <= 0) {
|
|
3650
|
+
return;
|
|
3651
|
+
}
|
|
3652
|
+
const newUrls = this.generateVastUrlsWithCorrelators(
|
|
3653
|
+
this.apiVastTagUrl,
|
|
3654
|
+
additionalAds
|
|
3655
|
+
);
|
|
3656
|
+
if (this.config.debugAdTiming) {
|
|
3657
|
+
console.log(
|
|
3658
|
+
`[ADAPTIVE-POD] \u{1F504} Adding ${newUrls.length} additional VAST URLs to queue`
|
|
3659
|
+
);
|
|
3660
|
+
}
|
|
3661
|
+
this.adPodAllUrls.push(...newUrls);
|
|
3662
|
+
this.adPodQueue.push(...newUrls);
|
|
3663
|
+
this.totalAdsInBreak += newUrls.length;
|
|
3664
|
+
for (const url of newUrls) {
|
|
3665
|
+
this.preloadSingleAd(url).catch((error) => {
|
|
3666
|
+
if (this.config.debugAdTiming) {
|
|
3667
|
+
console.warn(
|
|
3668
|
+
`[ADAPTIVE-POD] Failed to preload adaptive ad:`,
|
|
3669
|
+
error
|
|
3670
|
+
);
|
|
3671
|
+
}
|
|
3672
|
+
});
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3512
3675
|
async preloadMediaFile(mediaUrl) {
|
|
3513
3676
|
if (this.preloadedMediaUrls.has(mediaUrl)) {
|
|
3514
3677
|
return;
|
|
@@ -3578,6 +3741,18 @@ var StormcloudVideoPlayer = class {
|
|
|
3578
3741
|
async preloadSingleAd(vastTagUrl) {
|
|
3579
3742
|
if (!vastTagUrl) return;
|
|
3580
3743
|
try {
|
|
3744
|
+
if (this.isAdaptiveMode && !this.fetchedAdDurations.has(vastTagUrl)) {
|
|
3745
|
+
const duration = await this.fetchVastDuration(vastTagUrl);
|
|
3746
|
+
if (duration !== null) {
|
|
3747
|
+
this.fetchedAdDurations.set(vastTagUrl, duration);
|
|
3748
|
+
if (this.config.debugAdTiming) {
|
|
3749
|
+
console.log(
|
|
3750
|
+
`[ADAPTIVE-POD] \u2713 Fetched ad duration: ${duration}s (${this.fetchedAdDurations.size} ads fetched so far)`
|
|
3751
|
+
);
|
|
3752
|
+
}
|
|
3753
|
+
await this.addAdaptiveAdsToQueue();
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3581
3756
|
if (this.ima.preloadAds && !this.ima.hasPreloadedAd(vastTagUrl)) {
|
|
3582
3757
|
if (!this.preloadingAdUrls.has(vastTagUrl)) {
|
|
3583
3758
|
if (this.config.debugAdTiming) {
|
|
@@ -3823,7 +3998,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3823
3998
|
}
|
|
3824
3999
|
(_a = this.hls) == null ? void 0 : _a.destroy();
|
|
3825
4000
|
(_b = this.ima) == null ? void 0 : _b.destroy();
|
|
3826
|
-
this.releaseAdHoldState();
|
|
3827
4001
|
this.preloadingAdUrls.clear();
|
|
3828
4002
|
this.vastToMediaUrlMap.clear();
|
|
3829
4003
|
this.preloadedMediaUrls.clear();
|