stormcloud-video-player 0.2.36 → 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 +186 -2
- 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 +186 -2
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +186 -2
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +8 -0
- package/lib/players/HlsPlayer.cjs +186 -2
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/index.cjs +186 -2
- package/lib/players/index.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.cjs +186 -2
- 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 {
|
|
@@ -3542,6 +3621,99 @@ var StormcloudVideoPlayer = class {
|
|
|
3542
3621
|
}
|
|
3543
3622
|
return mediaUrls;
|
|
3544
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
|
+
}
|
|
3545
3717
|
async preloadMediaFile(mediaUrl) {
|
|
3546
3718
|
if (this.preloadedMediaUrls.has(mediaUrl)) {
|
|
3547
3719
|
return;
|
|
@@ -3611,6 +3783,18 @@ var StormcloudVideoPlayer = class {
|
|
|
3611
3783
|
async preloadSingleAd(vastTagUrl) {
|
|
3612
3784
|
if (!vastTagUrl) return;
|
|
3613
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
|
+
}
|
|
3614
3798
|
if (this.ima.preloadAds && !this.ima.hasPreloadedAd(vastTagUrl)) {
|
|
3615
3799
|
if (!this.preloadingAdUrls.has(vastTagUrl)) {
|
|
3616
3800
|
if (this.config.debugAdTiming) {
|