stormcloud-video-player 0.2.36 → 0.3.1
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 +235 -21
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +8 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +235 -21
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +235 -21
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +8 -0
- package/lib/players/HlsPlayer.cjs +235 -21
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/index.cjs +235 -21
- package/lib/players/index.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.cjs +235 -21
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/package.json +1 -1
package/lib/players/index.cjs
CHANGED
|
@@ -2201,6 +2201,9 @@ var StormcloudVideoPlayer = class {
|
|
|
2201
2201
|
this.activeAdRequestToken = null;
|
|
2202
2202
|
this.adRequestWatchdogToken = null;
|
|
2203
2203
|
this.adFailsafeToken = null;
|
|
2204
|
+
this.fetchedAdDurations = /* @__PURE__ */ new Map();
|
|
2205
|
+
this.targetAdBreakDurationMs = null;
|
|
2206
|
+
this.isAdaptiveMode = false;
|
|
2204
2207
|
initializePolyfills();
|
|
2205
2208
|
const browserOverrides = getBrowserConfigOverrides();
|
|
2206
2209
|
this.config = { ...config, ...browserOverrides };
|
|
@@ -3007,7 +3010,7 @@ var StormcloudVideoPlayer = class {
|
|
|
3007
3010
|
}
|
|
3008
3011
|
}
|
|
3009
3012
|
async fetchAdConfiguration() {
|
|
3010
|
-
var _a, _b, _c;
|
|
3013
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
3011
3014
|
const vastMode = this.config.vastMode || "default";
|
|
3012
3015
|
if (this.config.debugAdTiming) {
|
|
3013
3016
|
console.log("[StormcloudVideoPlayer] VAST mode:", vastMode);
|
|
@@ -3078,6 +3081,16 @@ var StormcloudVideoPlayer = class {
|
|
|
3078
3081
|
);
|
|
3079
3082
|
}
|
|
3080
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
|
+
}
|
|
3081
3094
|
}
|
|
3082
3095
|
getCurrentAdIndex() {
|
|
3083
3096
|
return this.currentAdIndex;
|
|
@@ -3085,6 +3098,27 @@ var StormcloudVideoPlayer = class {
|
|
|
3085
3098
|
getTotalAdsInBreak() {
|
|
3086
3099
|
return this.totalAdsInBreak;
|
|
3087
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
|
+
}
|
|
3088
3122
|
isAdPlaying() {
|
|
3089
3123
|
return this.inAdBreak && this.ima.isAdPlaying();
|
|
3090
3124
|
}
|
|
@@ -3123,7 +3157,52 @@ var StormcloudVideoPlayer = class {
|
|
|
3123
3157
|
const tags = this.selectVastTagsForBreak(scheduled);
|
|
3124
3158
|
let vastTagUrls = [];
|
|
3125
3159
|
if (this.apiVastTagUrl) {
|
|
3126
|
-
|
|
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
|
+
}
|
|
3127
3206
|
} else if (tags && tags.length > 0) {
|
|
3128
3207
|
vastTagUrls = tags;
|
|
3129
3208
|
} else {
|
|
@@ -3173,7 +3252,8 @@ var StormcloudVideoPlayer = class {
|
|
|
3173
3252
|
console.log("[DEBUG-POD] \u26A0\uFE0F No ads in pod");
|
|
3174
3253
|
return;
|
|
3175
3254
|
}
|
|
3176
|
-
|
|
3255
|
+
const waitTime = this.isAdaptiveMode ? 1500 : 500;
|
|
3256
|
+
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
3177
3257
|
const firstPreloaded = this.findNextPreloadedAd();
|
|
3178
3258
|
if (!firstPreloaded) {
|
|
3179
3259
|
console.log("[DEBUG-POD] \u26A0\uFE0F No preloaded ads after wait, trying first ad");
|
|
@@ -3542,6 +3622,89 @@ var StormcloudVideoPlayer = class {
|
|
|
3542
3622
|
}
|
|
3543
3623
|
return mediaUrls;
|
|
3544
3624
|
}
|
|
3625
|
+
async fetchVastDuration(vastTagUrl) {
|
|
3626
|
+
var _a;
|
|
3627
|
+
try {
|
|
3628
|
+
const response = await fetch(vastTagUrl, { mode: "cors" });
|
|
3629
|
+
if (!response.ok) {
|
|
3630
|
+
if (this.config.debugAdTiming) {
|
|
3631
|
+
console.warn(
|
|
3632
|
+
`[ADAPTIVE-POD] Failed to fetch VAST: ${response.status}`
|
|
3633
|
+
);
|
|
3634
|
+
}
|
|
3635
|
+
return null;
|
|
3636
|
+
}
|
|
3637
|
+
const xmlText = await response.text();
|
|
3638
|
+
const parser = new DOMParser();
|
|
3639
|
+
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
|
|
3640
|
+
const durationText = (_a = xmlDoc.querySelector("Duration")) == null ? void 0 : _a.textContent;
|
|
3641
|
+
if (!durationText) {
|
|
3642
|
+
if (this.config.debugAdTiming) {
|
|
3643
|
+
console.warn("[ADAPTIVE-POD] No Duration element found in VAST");
|
|
3644
|
+
}
|
|
3645
|
+
return null;
|
|
3646
|
+
}
|
|
3647
|
+
const durationParts = durationText.split(":");
|
|
3648
|
+
const durationSeconds = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
3649
|
+
return durationSeconds;
|
|
3650
|
+
} catch (error) {
|
|
3651
|
+
if (this.config.debugAdTiming) {
|
|
3652
|
+
console.warn(
|
|
3653
|
+
`[ADAPTIVE-POD] Error fetching VAST duration from ${vastTagUrl}:`,
|
|
3654
|
+
error
|
|
3655
|
+
);
|
|
3656
|
+
}
|
|
3657
|
+
return null;
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
calculateAdditionalAdsNeeded() {
|
|
3661
|
+
if (!this.isAdaptiveMode || this.targetAdBreakDurationMs === null) {
|
|
3662
|
+
return 0;
|
|
3663
|
+
}
|
|
3664
|
+
let totalFetchedDurationMs = 0;
|
|
3665
|
+
for (const duration of this.fetchedAdDurations.values()) {
|
|
3666
|
+
totalFetchedDurationMs += duration * 1e3;
|
|
3667
|
+
}
|
|
3668
|
+
const remainingTimeMs = this.targetAdBreakDurationMs - totalFetchedDurationMs;
|
|
3669
|
+
if (remainingTimeMs <= 0) {
|
|
3670
|
+
if (this.config.debugAdTiming) {
|
|
3671
|
+
console.log(
|
|
3672
|
+
`[ADAPTIVE-POD] \u2705 Target duration reached: ${totalFetchedDurationMs}ms / ${this.targetAdBreakDurationMs}ms`
|
|
3673
|
+
);
|
|
3674
|
+
}
|
|
3675
|
+
return 0;
|
|
3676
|
+
}
|
|
3677
|
+
const fetchedCount = this.fetchedAdDurations.size;
|
|
3678
|
+
const averageDurationMs = fetchedCount > 0 ? totalFetchedDurationMs / fetchedCount : 30 * 1e3;
|
|
3679
|
+
const additionalAds = Math.ceil(remainingTimeMs / averageDurationMs);
|
|
3680
|
+
if (this.config.debugAdTiming) {
|
|
3681
|
+
console.log(
|
|
3682
|
+
`[ADAPTIVE-POD] \u{1F4CA} Need ${additionalAds} more ads | Fetched: ${totalFetchedDurationMs}ms / Target: ${this.targetAdBreakDurationMs}ms | Remaining: ${remainingTimeMs}ms | Avg duration: ${averageDurationMs}ms`
|
|
3683
|
+
);
|
|
3684
|
+
}
|
|
3685
|
+
return additionalAds;
|
|
3686
|
+
}
|
|
3687
|
+
async addAdaptiveAdsToQueue() {
|
|
3688
|
+
if (!this.isAdaptiveMode || !this.apiVastTagUrl) {
|
|
3689
|
+
return;
|
|
3690
|
+
}
|
|
3691
|
+
const additionalAds = this.calculateAdditionalAdsNeeded();
|
|
3692
|
+
if (additionalAds <= 0) {
|
|
3693
|
+
return;
|
|
3694
|
+
}
|
|
3695
|
+
const newUrls = this.generateVastUrlsWithCorrelators(
|
|
3696
|
+
this.apiVastTagUrl,
|
|
3697
|
+
additionalAds
|
|
3698
|
+
);
|
|
3699
|
+
if (this.config.debugAdTiming) {
|
|
3700
|
+
console.log(
|
|
3701
|
+
`[ADAPTIVE-POD] \u{1F504} Adding ${newUrls.length} additional VAST URLs to queue (will be preloaded sequentially)`
|
|
3702
|
+
);
|
|
3703
|
+
}
|
|
3704
|
+
this.adPodAllUrls.push(...newUrls);
|
|
3705
|
+
this.adPodQueue.push(...newUrls);
|
|
3706
|
+
this.totalAdsInBreak += newUrls.length;
|
|
3707
|
+
}
|
|
3545
3708
|
async preloadMediaFile(mediaUrl) {
|
|
3546
3709
|
if (this.preloadedMediaUrls.has(mediaUrl)) {
|
|
3547
3710
|
return;
|
|
@@ -3586,31 +3749,82 @@ var StormcloudVideoPlayer = class {
|
|
|
3586
3749
|
if (this.adPodAllUrls.length === 0) {
|
|
3587
3750
|
return;
|
|
3588
3751
|
}
|
|
3589
|
-
if (this.
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
);
|
|
3752
|
+
if (this.isAdaptiveMode) {
|
|
3753
|
+
if (this.config.debugAdTiming) {
|
|
3754
|
+
console.log(
|
|
3755
|
+
`[ADAPTIVE-POD] Starting sequential preload of ${this.adPodAllUrls.length} initial ads`
|
|
3756
|
+
);
|
|
3757
|
+
}
|
|
3758
|
+
const processedUrls = /* @__PURE__ */ new Set();
|
|
3759
|
+
while (true) {
|
|
3760
|
+
const nextUrl = this.adPodAllUrls.find((url) => !processedUrls.has(url));
|
|
3761
|
+
if (!nextUrl) {
|
|
3762
|
+
break;
|
|
3601
3763
|
}
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3764
|
+
processedUrls.add(nextUrl);
|
|
3765
|
+
try {
|
|
3766
|
+
await this.preloadSingleAd(nextUrl);
|
|
3767
|
+
} catch (error) {
|
|
3768
|
+
if (this.config.debugAdTiming) {
|
|
3769
|
+
console.warn(
|
|
3770
|
+
`[ADAPTIVE-POD] Preload failed for ${nextUrl}:`,
|
|
3771
|
+
error
|
|
3772
|
+
);
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3775
|
+
if (this.calculateAdditionalAdsNeeded() === 0) {
|
|
3776
|
+
if (this.config.debugAdTiming) {
|
|
3777
|
+
console.log(
|
|
3778
|
+
`[ADAPTIVE-POD] \u2705 Target duration reached, stopping preload`
|
|
3779
|
+
);
|
|
3780
|
+
}
|
|
3781
|
+
break;
|
|
3782
|
+
}
|
|
3783
|
+
}
|
|
3784
|
+
if (this.config.debugAdTiming) {
|
|
3785
|
+
console.log(
|
|
3786
|
+
`[ADAPTIVE-POD] Sequential preloading completed (${processedUrls.size} ads preloaded)`
|
|
3787
|
+
);
|
|
3788
|
+
}
|
|
3789
|
+
} else {
|
|
3790
|
+
if (this.config.debugAdTiming) {
|
|
3791
|
+
console.log(
|
|
3792
|
+
`[StormcloudVideoPlayer] Starting parallel preload of ${this.adPodAllUrls.length} ads`
|
|
3793
|
+
);
|
|
3794
|
+
}
|
|
3795
|
+
const preloadPromises = this.adPodAllUrls.map(
|
|
3796
|
+
(vastTagUrl) => this.preloadSingleAd(vastTagUrl).catch((error) => {
|
|
3797
|
+
if (this.config.debugAdTiming) {
|
|
3798
|
+
console.warn(
|
|
3799
|
+
`[StormcloudVideoPlayer] Preload failed for ${vastTagUrl}:`,
|
|
3800
|
+
error
|
|
3801
|
+
);
|
|
3802
|
+
}
|
|
3803
|
+
})
|
|
3608
3804
|
);
|
|
3805
|
+
await Promise.all(preloadPromises);
|
|
3806
|
+
if (this.config.debugAdTiming) {
|
|
3807
|
+
console.log(
|
|
3808
|
+
`[StormcloudVideoPlayer] Background preloading completed for all ads`
|
|
3809
|
+
);
|
|
3810
|
+
}
|
|
3609
3811
|
}
|
|
3610
3812
|
}
|
|
3611
3813
|
async preloadSingleAd(vastTagUrl) {
|
|
3612
3814
|
if (!vastTagUrl) return;
|
|
3613
3815
|
try {
|
|
3816
|
+
if (this.isAdaptiveMode && !this.fetchedAdDurations.has(vastTagUrl)) {
|
|
3817
|
+
const duration = await this.fetchVastDuration(vastTagUrl);
|
|
3818
|
+
if (duration !== null) {
|
|
3819
|
+
this.fetchedAdDurations.set(vastTagUrl, duration);
|
|
3820
|
+
if (this.config.debugAdTiming) {
|
|
3821
|
+
console.log(
|
|
3822
|
+
`[ADAPTIVE-POD] \u2713 Fetched ad duration: ${duration}s (${this.fetchedAdDurations.size} ads fetched so far)`
|
|
3823
|
+
);
|
|
3824
|
+
}
|
|
3825
|
+
await this.addAdaptiveAdsToQueue();
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3614
3828
|
if (this.ima.preloadAds && !this.ima.hasPreloadedAd(vastTagUrl)) {
|
|
3615
3829
|
if (!this.preloadingAdUrls.has(vastTagUrl)) {
|
|
3616
3830
|
if (this.config.debugAdTiming) {
|