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/players/index.cjs
CHANGED
|
@@ -670,19 +670,10 @@ function createImaController(video, options) {
|
|
|
670
670
|
adsManager.addEventListener(
|
|
671
671
|
AdEvent.CONTENT_PAUSE_REQUESTED,
|
|
672
672
|
() => {
|
|
673
|
-
console.log("[DEBUG-FLOW] \u{1F3AF} CONTENT_PAUSE_REQUESTED - Ad
|
|
673
|
+
console.log("[DEBUG-FLOW] \u{1F3AF} CONTENT_PAUSE_REQUESTED - Ad request accepted");
|
|
674
674
|
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
675
675
|
video.pause();
|
|
676
676
|
}
|
|
677
|
-
hideContentVideo();
|
|
678
|
-
if (adContainerEl) {
|
|
679
|
-
adContainerEl.style.pointerEvents = "auto";
|
|
680
|
-
adContainerEl.style.display = "flex";
|
|
681
|
-
adContainerEl.style.backgroundColor = "#000";
|
|
682
|
-
adContainerEl.offsetHeight;
|
|
683
|
-
adContainerEl.style.opacity = "1";
|
|
684
|
-
console.log("[DEBUG-LAYER] \u{1F7E1} Ad container VISIBLE");
|
|
685
|
-
}
|
|
686
677
|
adPlaying = true;
|
|
687
678
|
setAdPlayingFlag(true);
|
|
688
679
|
emit("content_pause");
|
|
@@ -2210,6 +2201,9 @@ var StormcloudVideoPlayer = class {
|
|
|
2210
2201
|
this.activeAdRequestToken = null;
|
|
2211
2202
|
this.adRequestWatchdogToken = null;
|
|
2212
2203
|
this.adFailsafeToken = null;
|
|
2204
|
+
this.fetchedAdDurations = /* @__PURE__ */ new Map();
|
|
2205
|
+
this.targetAdBreakDurationMs = null;
|
|
2206
|
+
this.isAdaptiveMode = false;
|
|
2213
2207
|
initializePolyfills();
|
|
2214
2208
|
const browserOverrides = getBrowserConfigOverrides();
|
|
2215
2209
|
this.config = { ...config, ...browserOverrides };
|
|
@@ -2515,7 +2509,7 @@ var StormcloudVideoPlayer = class {
|
|
|
2515
2509
|
this.clearAdRequestWatchdog();
|
|
2516
2510
|
this.activeAdRequestToken = null;
|
|
2517
2511
|
this.showAds = true;
|
|
2518
|
-
|
|
2512
|
+
console.log("[DEBUG-LAYER] \u{1F3AC} Layers: Main=hidden, Ad=visible, Placeholder=no");
|
|
2519
2513
|
});
|
|
2520
2514
|
this.ima.on("content_resume", () => {
|
|
2521
2515
|
console.log(`[DEBUG-POD] \u23F8\uFE0F content_resume | ad ${this.currentAdIndex}/${this.totalAdsInBreak}, queue=${this.adPodQueue.length}, remaining=${this.getRemainingAdMs()}ms`);
|
|
@@ -2695,6 +2689,12 @@ var StormcloudVideoPlayer = class {
|
|
|
2695
2689
|
});
|
|
2696
2690
|
}
|
|
2697
2691
|
if (marker.type === "start") {
|
|
2692
|
+
if (this.inAdBreak) {
|
|
2693
|
+
console.log(
|
|
2694
|
+
`[DEBUG-POD] \u26A0\uFE0F SCTE-35 start marker ignored - already in ad break (currentTime: ${this.video.currentTime})`
|
|
2695
|
+
);
|
|
2696
|
+
return;
|
|
2697
|
+
}
|
|
2698
2698
|
this.inAdBreak = true;
|
|
2699
2699
|
const durationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : void 0;
|
|
2700
2700
|
this.expectedAdBreakDurationMs = durationMs;
|
|
@@ -2761,6 +2761,9 @@ var StormcloudVideoPlayer = class {
|
|
|
2761
2761
|
return;
|
|
2762
2762
|
}
|
|
2763
2763
|
if (marker.type === "progress" && this.inAdBreak) {
|
|
2764
|
+
console.log(
|
|
2765
|
+
`[DEBUG-POD] \u{1F4CA} SCTE-35 progress marker (currentTime: ${this.video.currentTime})`
|
|
2766
|
+
);
|
|
2764
2767
|
if (marker.durationSeconds != null) {
|
|
2765
2768
|
this.expectedAdBreakDurationMs = marker.durationSeconds * 1e3;
|
|
2766
2769
|
}
|
|
@@ -2772,7 +2775,8 @@ var StormcloudVideoPlayer = class {
|
|
|
2772
2775
|
);
|
|
2773
2776
|
this.scheduleAdStopCountdown(remainingMs);
|
|
2774
2777
|
}
|
|
2775
|
-
if (!this.ima.isAdPlaying()) {
|
|
2778
|
+
if (!this.ima.isAdPlaying() && this.activeAdRequestToken === null) {
|
|
2779
|
+
console.log("[DEBUG-POD] \u{1F4CA} Progress marker: no ad playing, attempting to start");
|
|
2776
2780
|
const scheduled = this.findCurrentOrNextBreak(
|
|
2777
2781
|
this.video.currentTime * 1e3
|
|
2778
2782
|
);
|
|
@@ -2781,25 +2785,31 @@ var StormcloudVideoPlayer = class {
|
|
|
2781
2785
|
const first = tags[0];
|
|
2782
2786
|
const rest = tags.slice(1);
|
|
2783
2787
|
this.adPodQueue = rest;
|
|
2784
|
-
if (!this.showAds) {
|
|
2785
|
-
this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
|
|
2786
|
-
}
|
|
2787
2788
|
this.playSingleAd(first).catch(() => {
|
|
2788
2789
|
});
|
|
2789
2790
|
}
|
|
2791
|
+
} else {
|
|
2792
|
+
console.log(
|
|
2793
|
+
`[DEBUG-POD] \u{1F4CA} Progress marker: ad playing or request active (playing=${this.ima.isAdPlaying()}, token=${this.activeAdRequestToken})`
|
|
2794
|
+
);
|
|
2790
2795
|
}
|
|
2791
2796
|
return;
|
|
2792
2797
|
}
|
|
2793
2798
|
if (marker.type === "end") {
|
|
2799
|
+
console.log(
|
|
2800
|
+
`[DEBUG-POD] \u{1F3C1} SCTE-35 end marker received (currentTime: ${this.video.currentTime})`
|
|
2801
|
+
);
|
|
2794
2802
|
this.inAdBreak = false;
|
|
2795
2803
|
this.expectedAdBreakDurationMs = void 0;
|
|
2796
2804
|
this.currentAdBreakStartWallClockMs = void 0;
|
|
2797
2805
|
this.clearAdStartTimer();
|
|
2798
2806
|
this.clearAdStopTimer();
|
|
2799
2807
|
if (this.ima.isAdPlaying()) {
|
|
2808
|
+
console.log("[DEBUG-POD] \u{1F6D1} Stopping ad due to SCTE-35 end marker");
|
|
2800
2809
|
this.ima.stop().catch(() => {
|
|
2801
2810
|
});
|
|
2802
2811
|
}
|
|
2812
|
+
this.handleAdPodComplete();
|
|
2803
2813
|
return;
|
|
2804
2814
|
}
|
|
2805
2815
|
}
|
|
@@ -3000,7 +3010,7 @@ var StormcloudVideoPlayer = class {
|
|
|
3000
3010
|
}
|
|
3001
3011
|
}
|
|
3002
3012
|
async fetchAdConfiguration() {
|
|
3003
|
-
var _a, _b, _c;
|
|
3013
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
3004
3014
|
const vastMode = this.config.vastMode || "default";
|
|
3005
3015
|
if (this.config.debugAdTiming) {
|
|
3006
3016
|
console.log("[StormcloudVideoPlayer] VAST mode:", vastMode);
|
|
@@ -3071,6 +3081,16 @@ var StormcloudVideoPlayer = class {
|
|
|
3071
3081
|
);
|
|
3072
3082
|
}
|
|
3073
3083
|
}
|
|
3084
|
+
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;
|
|
3085
|
+
if (numberAds != null && numberAds > 0) {
|
|
3086
|
+
this.apiNumberAds = numberAds;
|
|
3087
|
+
if (this.config.debugAdTiming) {
|
|
3088
|
+
console.log(
|
|
3089
|
+
"[StormcloudVideoPlayer] Number of ads per break from API:",
|
|
3090
|
+
this.apiNumberAds
|
|
3091
|
+
);
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3074
3094
|
}
|
|
3075
3095
|
getCurrentAdIndex() {
|
|
3076
3096
|
return this.currentAdIndex;
|
|
@@ -3078,6 +3098,27 @@ var StormcloudVideoPlayer = class {
|
|
|
3078
3098
|
getTotalAdsInBreak() {
|
|
3079
3099
|
return this.totalAdsInBreak;
|
|
3080
3100
|
}
|
|
3101
|
+
generateVastUrlsWithCorrelators(baseUrl, count) {
|
|
3102
|
+
const urls = [];
|
|
3103
|
+
for (let i = 0; i < count; i++) {
|
|
3104
|
+
try {
|
|
3105
|
+
const url = new URL(baseUrl);
|
|
3106
|
+
const timestamp = Date.now();
|
|
3107
|
+
const random = Math.floor(Math.random() * 1e6);
|
|
3108
|
+
const uniqueCorrelator = `${timestamp}${random}${i}`;
|
|
3109
|
+
url.searchParams.set("correlator", uniqueCorrelator);
|
|
3110
|
+
urls.push(url.toString());
|
|
3111
|
+
} catch (error) {
|
|
3112
|
+
console.warn(
|
|
3113
|
+
"[StormcloudVideoPlayer] Failed to parse VAST URL:",
|
|
3114
|
+
baseUrl,
|
|
3115
|
+
error
|
|
3116
|
+
);
|
|
3117
|
+
urls.push(`${baseUrl}${baseUrl.includes("?") ? "&" : "?"}correlator=${Date.now()}${i}`);
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
return urls;
|
|
3121
|
+
}
|
|
3081
3122
|
isAdPlaying() {
|
|
3082
3123
|
return this.inAdBreak && this.ima.isAdPlaying();
|
|
3083
3124
|
}
|
|
@@ -3116,7 +3157,52 @@ var StormcloudVideoPlayer = class {
|
|
|
3116
3157
|
const tags = this.selectVastTagsForBreak(scheduled);
|
|
3117
3158
|
let vastTagUrls = [];
|
|
3118
3159
|
if (this.apiVastTagUrl) {
|
|
3119
|
-
|
|
3160
|
+
let numberOfAds = 1;
|
|
3161
|
+
if (this.isLiveStream) {
|
|
3162
|
+
const adBreakDurationMs = _marker.durationSeconds != null ? _marker.durationSeconds * 1e3 : scheduled == null ? void 0 : scheduled.durationMs;
|
|
3163
|
+
if (adBreakDurationMs != null && adBreakDurationMs > 0) {
|
|
3164
|
+
this.isAdaptiveMode = true;
|
|
3165
|
+
this.targetAdBreakDurationMs = adBreakDurationMs;
|
|
3166
|
+
this.fetchedAdDurations.clear();
|
|
3167
|
+
numberOfAds = 2;
|
|
3168
|
+
if (this.config.debugAdTiming) {
|
|
3169
|
+
console.log(
|
|
3170
|
+
`[ADAPTIVE-POD] \u{1F4FA} LIVE MODE (ADAPTIVE): Target duration=${adBreakDurationMs}ms | Starting with ${numberOfAds} ads, will fetch actual durations and add more dynamically`
|
|
3171
|
+
);
|
|
3172
|
+
}
|
|
3173
|
+
} else {
|
|
3174
|
+
if (this.config.debugAdTiming) {
|
|
3175
|
+
console.warn(
|
|
3176
|
+
"[DEBUG-POD] \u26A0\uFE0F LIVE MODE: No duration available, defaulting to 1 ad"
|
|
3177
|
+
);
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
} else {
|
|
3181
|
+
this.isAdaptiveMode = false;
|
|
3182
|
+
this.targetAdBreakDurationMs = null;
|
|
3183
|
+
this.fetchedAdDurations.clear();
|
|
3184
|
+
if (this.apiNumberAds && this.apiNumberAds > 1) {
|
|
3185
|
+
numberOfAds = this.apiNumberAds;
|
|
3186
|
+
if (this.config.debugAdTiming) {
|
|
3187
|
+
console.log(
|
|
3188
|
+
`[DEBUG-POD] \u{1F3AC} VOD MODE (FIXED): Using number_ads=${numberOfAds} from API`
|
|
3189
|
+
);
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
if (numberOfAds > 1) {
|
|
3194
|
+
vastTagUrls = this.generateVastUrlsWithCorrelators(
|
|
3195
|
+
this.apiVastTagUrl,
|
|
3196
|
+
numberOfAds
|
|
3197
|
+
);
|
|
3198
|
+
if (this.config.debugAdTiming) {
|
|
3199
|
+
console.log(
|
|
3200
|
+
`[DEBUG-POD] \u{1F504} Generated ${vastTagUrls.length} initial VAST URLs with unique correlators`
|
|
3201
|
+
);
|
|
3202
|
+
}
|
|
3203
|
+
} else {
|
|
3204
|
+
vastTagUrls = [this.apiVastTagUrl];
|
|
3205
|
+
}
|
|
3120
3206
|
} else if (tags && tags.length > 0) {
|
|
3121
3207
|
vastTagUrls = tags;
|
|
3122
3208
|
} else {
|
|
@@ -3130,10 +3216,12 @@ var StormcloudVideoPlayer = class {
|
|
|
3130
3216
|
this.vastToMediaUrlMap.clear();
|
|
3131
3217
|
this.preloadedMediaUrls.clear();
|
|
3132
3218
|
this.preloadingMediaUrls.clear();
|
|
3219
|
+
const currentMuted = this.video.muted;
|
|
3220
|
+
const currentVolume = this.video.volume;
|
|
3133
3221
|
console.log(
|
|
3134
|
-
`[DEBUG-AUDIO] \u{1F4BE} Capturing
|
|
3222
|
+
`[DEBUG-AUDIO] \u{1F4BE} Capturing ORIGINAL state (once) | muted=${currentMuted}, volume=${currentVolume}`
|
|
3135
3223
|
);
|
|
3136
|
-
this.ima.updateOriginalMutedState(
|
|
3224
|
+
this.ima.updateOriginalMutedState(currentMuted, currentVolume);
|
|
3137
3225
|
this.inAdBreak = true;
|
|
3138
3226
|
this.currentAdIndex = 0;
|
|
3139
3227
|
this.totalAdsInBreak = vastTagUrls.length;
|
|
@@ -3347,7 +3435,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3347
3435
|
this.clearAdRequestWatchdog();
|
|
3348
3436
|
this.clearAdFailsafeTimer();
|
|
3349
3437
|
this.activeAdRequestToken = null;
|
|
3350
|
-
this.releaseAdHoldState();
|
|
3351
3438
|
this.preloadingAdUrls.clear();
|
|
3352
3439
|
this.vastToMediaUrlMap.clear();
|
|
3353
3440
|
this.preloadedMediaUrls.clear();
|
|
@@ -3364,13 +3451,18 @@ var StormcloudVideoPlayer = class {
|
|
|
3364
3451
|
this.totalAdsInBreak = 0;
|
|
3365
3452
|
this.ima.stop().catch(() => {
|
|
3366
3453
|
});
|
|
3367
|
-
const
|
|
3368
|
-
const
|
|
3369
|
-
this.video.muted = originalMutedState;
|
|
3370
|
-
this.video.volume = originalVolume;
|
|
3454
|
+
const restoredMuted = this.ima.getOriginalMutedState();
|
|
3455
|
+
const restoredVolume = this.ima.getOriginalVolume();
|
|
3371
3456
|
console.log(
|
|
3372
|
-
`[DEBUG-AUDIO] \u{1F50A}
|
|
3457
|
+
`[DEBUG-AUDIO] \u{1F50A} Audio restored by IMA | muted=${restoredMuted}, volume=${restoredVolume}`
|
|
3373
3458
|
);
|
|
3459
|
+
console.log("[DEBUG-LAYER] \u{1F3AC} Layers: Main=visible, Ad=hidden, Placeholder=no");
|
|
3460
|
+
if (this.video.muted !== restoredMuted) {
|
|
3461
|
+
this.video.muted = restoredMuted;
|
|
3462
|
+
}
|
|
3463
|
+
if (Math.abs(this.video.volume - restoredVolume) > 0.01) {
|
|
3464
|
+
this.video.volume = restoredVolume;
|
|
3465
|
+
}
|
|
3374
3466
|
if (!this.shouldContinueLiveStreamDuringAds() && this.video.paused) {
|
|
3375
3467
|
console.log("[DEBUG-FLOW] \u25B6\uFE0F Resuming main video playback");
|
|
3376
3468
|
(_a = this.video.play()) == null ? void 0 : _a.catch((error) => {
|
|
@@ -3379,20 +3471,20 @@ var StormcloudVideoPlayer = class {
|
|
|
3379
3471
|
}
|
|
3380
3472
|
}
|
|
3381
3473
|
handleAdFailure() {
|
|
3474
|
+
console.log("[DEBUG-POD] \u274C handleAdFailure - skipping to next ad or ending break");
|
|
3382
3475
|
const remaining = this.getRemainingAdMs();
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
return;
|
|
3476
|
+
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
3477
|
+
const nextPreloaded = this.findNextPreloadedAd();
|
|
3478
|
+
if (nextPreloaded) {
|
|
3479
|
+
this.currentAdIndex++;
|
|
3480
|
+
console.log(`[DEBUG-POD] \u27A1\uFE0F Trying next ad after failure (${this.currentAdIndex}/${this.totalAdsInBreak})`);
|
|
3481
|
+
this.playSingleAd(nextPreloaded).catch(() => {
|
|
3482
|
+
this.handleAdPodComplete();
|
|
3483
|
+
});
|
|
3484
|
+
return;
|
|
3485
|
+
}
|
|
3394
3486
|
}
|
|
3395
|
-
console.log("[DEBUG-POD] \u23F9\uFE0F
|
|
3487
|
+
console.log("[DEBUG-POD] \u23F9\uFE0F Ending ad break after failure");
|
|
3396
3488
|
this.handleAdPodComplete();
|
|
3397
3489
|
}
|
|
3398
3490
|
startAdRequestWatchdog(token) {
|
|
@@ -3465,12 +3557,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3465
3557
|
}
|
|
3466
3558
|
return [b.vastTagUrl];
|
|
3467
3559
|
}
|
|
3468
|
-
logQueuedAdUrls(urls) {
|
|
3469
|
-
if (!this.config.debugAdTiming) {
|
|
3470
|
-
return;
|
|
3471
|
-
}
|
|
3472
|
-
console.log("[StormcloudVideoPlayer] ALL ad URLs queued:", urls);
|
|
3473
|
-
}
|
|
3474
3560
|
logAdState(event, extra = {}) {
|
|
3475
3561
|
if (!this.config.debugAdTiming) {
|
|
3476
3562
|
return;
|
|
@@ -3485,22 +3571,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3485
3571
|
...extra
|
|
3486
3572
|
});
|
|
3487
3573
|
}
|
|
3488
|
-
enforceAdHoldState() {
|
|
3489
|
-
this.video.dataset.stormcloudAdPlaying = "true";
|
|
3490
|
-
this.video.muted = true;
|
|
3491
|
-
this.video.volume = 0;
|
|
3492
|
-
console.log("[DEBUG-LAYER] \u{1F512} Enforced ad hold state (main video muted)");
|
|
3493
|
-
if (typeof this.ima.showPlaceholder === "function") {
|
|
3494
|
-
this.ima.showPlaceholder();
|
|
3495
|
-
}
|
|
3496
|
-
}
|
|
3497
|
-
releaseAdHoldState() {
|
|
3498
|
-
delete this.video.dataset.stormcloudAdPlaying;
|
|
3499
|
-
console.log("[DEBUG-LAYER] \u{1F513} Released ad hold state");
|
|
3500
|
-
if (typeof this.ima.hidePlaceholder === "function") {
|
|
3501
|
-
this.ima.hidePlaceholder();
|
|
3502
|
-
}
|
|
3503
|
-
}
|
|
3504
3574
|
async fetchAndParseVastXml(vastTagUrl) {
|
|
3505
3575
|
try {
|
|
3506
3576
|
const response = await fetch(vastTagUrl, { mode: "cors" });
|
|
@@ -3551,6 +3621,99 @@ var StormcloudVideoPlayer = class {
|
|
|
3551
3621
|
}
|
|
3552
3622
|
return mediaUrls;
|
|
3553
3623
|
}
|
|
3624
|
+
async fetchVastDuration(vastTagUrl) {
|
|
3625
|
+
var _a;
|
|
3626
|
+
try {
|
|
3627
|
+
const response = await fetch(vastTagUrl, { mode: "cors" });
|
|
3628
|
+
if (!response.ok) {
|
|
3629
|
+
if (this.config.debugAdTiming) {
|
|
3630
|
+
console.warn(
|
|
3631
|
+
`[ADAPTIVE-POD] Failed to fetch VAST: ${response.status}`
|
|
3632
|
+
);
|
|
3633
|
+
}
|
|
3634
|
+
return null;
|
|
3635
|
+
}
|
|
3636
|
+
const xmlText = await response.text();
|
|
3637
|
+
const parser = new DOMParser();
|
|
3638
|
+
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
|
|
3639
|
+
const durationText = (_a = xmlDoc.querySelector("Duration")) == null ? void 0 : _a.textContent;
|
|
3640
|
+
if (!durationText) {
|
|
3641
|
+
if (this.config.debugAdTiming) {
|
|
3642
|
+
console.warn("[ADAPTIVE-POD] No Duration element found in VAST");
|
|
3643
|
+
}
|
|
3644
|
+
return null;
|
|
3645
|
+
}
|
|
3646
|
+
const durationParts = durationText.split(":");
|
|
3647
|
+
const durationSeconds = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
3648
|
+
return durationSeconds;
|
|
3649
|
+
} catch (error) {
|
|
3650
|
+
if (this.config.debugAdTiming) {
|
|
3651
|
+
console.warn(
|
|
3652
|
+
`[ADAPTIVE-POD] Error fetching VAST duration from ${vastTagUrl}:`,
|
|
3653
|
+
error
|
|
3654
|
+
);
|
|
3655
|
+
}
|
|
3656
|
+
return null;
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3659
|
+
calculateAdditionalAdsNeeded() {
|
|
3660
|
+
if (!this.isAdaptiveMode || this.targetAdBreakDurationMs === null) {
|
|
3661
|
+
return 0;
|
|
3662
|
+
}
|
|
3663
|
+
let totalFetchedDurationMs = 0;
|
|
3664
|
+
for (const duration of this.fetchedAdDurations.values()) {
|
|
3665
|
+
totalFetchedDurationMs += duration * 1e3;
|
|
3666
|
+
}
|
|
3667
|
+
const remainingTimeMs = this.targetAdBreakDurationMs - totalFetchedDurationMs;
|
|
3668
|
+
if (remainingTimeMs <= 0) {
|
|
3669
|
+
if (this.config.debugAdTiming) {
|
|
3670
|
+
console.log(
|
|
3671
|
+
`[ADAPTIVE-POD] \u2705 Target duration reached: ${totalFetchedDurationMs}ms / ${this.targetAdBreakDurationMs}ms`
|
|
3672
|
+
);
|
|
3673
|
+
}
|
|
3674
|
+
return 0;
|
|
3675
|
+
}
|
|
3676
|
+
const fetchedCount = this.fetchedAdDurations.size;
|
|
3677
|
+
const averageDurationMs = fetchedCount > 0 ? totalFetchedDurationMs / fetchedCount : 30 * 1e3;
|
|
3678
|
+
const additionalAds = Math.ceil(remainingTimeMs / averageDurationMs);
|
|
3679
|
+
if (this.config.debugAdTiming) {
|
|
3680
|
+
console.log(
|
|
3681
|
+
`[ADAPTIVE-POD] \u{1F4CA} Need ${additionalAds} more ads | Fetched: ${totalFetchedDurationMs}ms / Target: ${this.targetAdBreakDurationMs}ms | Remaining: ${remainingTimeMs}ms | Avg duration: ${averageDurationMs}ms`
|
|
3682
|
+
);
|
|
3683
|
+
}
|
|
3684
|
+
return additionalAds;
|
|
3685
|
+
}
|
|
3686
|
+
async addAdaptiveAdsToQueue() {
|
|
3687
|
+
if (!this.isAdaptiveMode || !this.apiVastTagUrl) {
|
|
3688
|
+
return;
|
|
3689
|
+
}
|
|
3690
|
+
const additionalAds = this.calculateAdditionalAdsNeeded();
|
|
3691
|
+
if (additionalAds <= 0) {
|
|
3692
|
+
return;
|
|
3693
|
+
}
|
|
3694
|
+
const newUrls = this.generateVastUrlsWithCorrelators(
|
|
3695
|
+
this.apiVastTagUrl,
|
|
3696
|
+
additionalAds
|
|
3697
|
+
);
|
|
3698
|
+
if (this.config.debugAdTiming) {
|
|
3699
|
+
console.log(
|
|
3700
|
+
`[ADAPTIVE-POD] \u{1F504} Adding ${newUrls.length} additional VAST URLs to queue`
|
|
3701
|
+
);
|
|
3702
|
+
}
|
|
3703
|
+
this.adPodAllUrls.push(...newUrls);
|
|
3704
|
+
this.adPodQueue.push(...newUrls);
|
|
3705
|
+
this.totalAdsInBreak += newUrls.length;
|
|
3706
|
+
for (const url of newUrls) {
|
|
3707
|
+
this.preloadSingleAd(url).catch((error) => {
|
|
3708
|
+
if (this.config.debugAdTiming) {
|
|
3709
|
+
console.warn(
|
|
3710
|
+
`[ADAPTIVE-POD] Failed to preload adaptive ad:`,
|
|
3711
|
+
error
|
|
3712
|
+
);
|
|
3713
|
+
}
|
|
3714
|
+
});
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3554
3717
|
async preloadMediaFile(mediaUrl) {
|
|
3555
3718
|
if (this.preloadedMediaUrls.has(mediaUrl)) {
|
|
3556
3719
|
return;
|
|
@@ -3620,6 +3783,18 @@ var StormcloudVideoPlayer = class {
|
|
|
3620
3783
|
async preloadSingleAd(vastTagUrl) {
|
|
3621
3784
|
if (!vastTagUrl) return;
|
|
3622
3785
|
try {
|
|
3786
|
+
if (this.isAdaptiveMode && !this.fetchedAdDurations.has(vastTagUrl)) {
|
|
3787
|
+
const duration = await this.fetchVastDuration(vastTagUrl);
|
|
3788
|
+
if (duration !== null) {
|
|
3789
|
+
this.fetchedAdDurations.set(vastTagUrl, duration);
|
|
3790
|
+
if (this.config.debugAdTiming) {
|
|
3791
|
+
console.log(
|
|
3792
|
+
`[ADAPTIVE-POD] \u2713 Fetched ad duration: ${duration}s (${this.fetchedAdDurations.size} ads fetched so far)`
|
|
3793
|
+
);
|
|
3794
|
+
}
|
|
3795
|
+
await this.addAdaptiveAdsToQueue();
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3623
3798
|
if (this.ima.preloadAds && !this.ima.hasPreloadedAd(vastTagUrl)) {
|
|
3624
3799
|
if (!this.preloadingAdUrls.has(vastTagUrl)) {
|
|
3625
3800
|
if (this.config.debugAdTiming) {
|
|
@@ -3865,7 +4040,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3865
4040
|
}
|
|
3866
4041
|
(_a = this.hls) == null ? void 0 : _a.destroy();
|
|
3867
4042
|
(_b = this.ima) == null ? void 0 : _b.destroy();
|
|
3868
|
-
this.releaseAdHoldState();
|
|
3869
4043
|
this.preloadingAdUrls.clear();
|
|
3870
4044
|
this.vastToMediaUrlMap.clear();
|
|
3871
4045
|
this.preloadedMediaUrls.clear();
|