stormcloud-video-player 0.3.8 → 0.3.10
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/dist/stormcloud-vp.min.js +2 -2
- package/lib/index.cjs +242 -193
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +11 -2
- package/lib/index.d.ts +11 -2
- package/lib/index.js +242 -193
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +242 -193
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +11 -2
- package/lib/players/HlsPlayer.cjs +242 -193
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/index.cjs +242 -193
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/ima.cjs +3 -10
- package/lib/sdk/ima.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.cjs +242 -193
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/package.json +1 -1
package/lib/index.d.cts
CHANGED
|
@@ -142,6 +142,12 @@ declare class StormcloudVideoPlayer {
|
|
|
142
142
|
private targetAdBreakDurationMs;
|
|
143
143
|
private isAdaptiveMode;
|
|
144
144
|
private failedVastUrls;
|
|
145
|
+
private continuousFetchingActive;
|
|
146
|
+
private adRequestQueue;
|
|
147
|
+
private successfulAdRequests;
|
|
148
|
+
private maxPlaceholderDurationMs;
|
|
149
|
+
private placeholderStartTimeMs;
|
|
150
|
+
private isShowingPlaceholder;
|
|
145
151
|
constructor(config: StormcloudVideoPlayerConfig);
|
|
146
152
|
private createAdPlayer;
|
|
147
153
|
load(): Promise<void>;
|
|
@@ -169,7 +175,11 @@ declare class StormcloudVideoPlayer {
|
|
|
169
175
|
shouldShowNativeControls(): boolean;
|
|
170
176
|
private shouldContinueLiveStreamDuringAds;
|
|
171
177
|
private handleAdStart;
|
|
172
|
-
private
|
|
178
|
+
private startContinuousFetching;
|
|
179
|
+
private continuousFetchLoop;
|
|
180
|
+
private stopContinuousFetching;
|
|
181
|
+
private tryNextAvailableAd;
|
|
182
|
+
private showPlaceholderAndWaitForAds;
|
|
173
183
|
private findCurrentOrNextBreak;
|
|
174
184
|
private onTimeUpdate;
|
|
175
185
|
private handleMidAdJoin;
|
|
@@ -182,7 +192,6 @@ declare class StormcloudVideoPlayer {
|
|
|
182
192
|
private playSingleAd;
|
|
183
193
|
private handleAdPodComplete;
|
|
184
194
|
private handleAdFailure;
|
|
185
|
-
private tryNextAdWithRetry;
|
|
186
195
|
private startAdRequestWatchdog;
|
|
187
196
|
private clearAdRequestWatchdog;
|
|
188
197
|
private startAdFailsafeTimer;
|
package/lib/index.d.ts
CHANGED
|
@@ -142,6 +142,12 @@ declare class StormcloudVideoPlayer {
|
|
|
142
142
|
private targetAdBreakDurationMs;
|
|
143
143
|
private isAdaptiveMode;
|
|
144
144
|
private failedVastUrls;
|
|
145
|
+
private continuousFetchingActive;
|
|
146
|
+
private adRequestQueue;
|
|
147
|
+
private successfulAdRequests;
|
|
148
|
+
private maxPlaceholderDurationMs;
|
|
149
|
+
private placeholderStartTimeMs;
|
|
150
|
+
private isShowingPlaceholder;
|
|
145
151
|
constructor(config: StormcloudVideoPlayerConfig);
|
|
146
152
|
private createAdPlayer;
|
|
147
153
|
load(): Promise<void>;
|
|
@@ -169,7 +175,11 @@ declare class StormcloudVideoPlayer {
|
|
|
169
175
|
shouldShowNativeControls(): boolean;
|
|
170
176
|
private shouldContinueLiveStreamDuringAds;
|
|
171
177
|
private handleAdStart;
|
|
172
|
-
private
|
|
178
|
+
private startContinuousFetching;
|
|
179
|
+
private continuousFetchLoop;
|
|
180
|
+
private stopContinuousFetching;
|
|
181
|
+
private tryNextAvailableAd;
|
|
182
|
+
private showPlaceholderAndWaitForAds;
|
|
173
183
|
private findCurrentOrNextBreak;
|
|
174
184
|
private onTimeUpdate;
|
|
175
185
|
private handleMidAdJoin;
|
|
@@ -182,7 +192,6 @@ declare class StormcloudVideoPlayer {
|
|
|
182
192
|
private playSingleAd;
|
|
183
193
|
private handleAdPodComplete;
|
|
184
194
|
private handleAdFailure;
|
|
185
|
-
private tryNextAdWithRetry;
|
|
186
195
|
private startAdRequestWatchdog;
|
|
187
196
|
private clearAdRequestWatchdog;
|
|
188
197
|
private startAdFailsafeTimer;
|
package/lib/index.js
CHANGED
|
@@ -339,12 +339,8 @@ function createImaController(video, options) {
|
|
|
339
339
|
let adsLoadedReject;
|
|
340
340
|
function makeAdsRequest(google, vastTagUrl) {
|
|
341
341
|
const adsRequest = new google.ima.AdsRequest();
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
adsRequest.adsResponse = preloadedResponse;
|
|
345
|
-
} else {
|
|
346
|
-
adsRequest.adTagUrl = vastTagUrl;
|
|
347
|
-
}
|
|
342
|
+
console.log("[IMA] \u{1F4E1} Requesting VAST from URL (letting IMA fetch fresh)");
|
|
343
|
+
adsRequest.adTagUrl = vastTagUrl;
|
|
348
344
|
const videoWidth = video.offsetWidth || video.clientWidth || 640;
|
|
349
345
|
const videoHeight = video.offsetHeight || video.clientHeight || 360;
|
|
350
346
|
adsRequest.linearAdSlotWidth = videoWidth;
|
|
@@ -367,11 +363,8 @@ function createImaController(video, options) {
|
|
|
367
363
|
console.warn("[IMA] Failed to call setAdWillPlayMuted:", error);
|
|
368
364
|
}
|
|
369
365
|
}
|
|
370
|
-
adsRequest.vastLoadTimeout =
|
|
366
|
+
adsRequest.vastLoadTimeout = 1e4;
|
|
371
367
|
adsLoader.requestAds(adsRequest);
|
|
372
|
-
if (preloadedResponse) {
|
|
373
|
-
preloadedVast.delete(vastTagUrl);
|
|
374
|
-
}
|
|
375
368
|
}
|
|
376
369
|
function ensurePlaceholderContainer() {
|
|
377
370
|
var _a;
|
|
@@ -2087,6 +2080,12 @@ var StormcloudVideoPlayer = class {
|
|
|
2087
2080
|
this.targetAdBreakDurationMs = null;
|
|
2088
2081
|
this.isAdaptiveMode = false;
|
|
2089
2082
|
this.failedVastUrls = /* @__PURE__ */ new Set();
|
|
2083
|
+
this.continuousFetchingActive = false;
|
|
2084
|
+
this.adRequestQueue = [];
|
|
2085
|
+
this.successfulAdRequests = [];
|
|
2086
|
+
this.maxPlaceholderDurationMs = 5e3;
|
|
2087
|
+
this.placeholderStartTimeMs = null;
|
|
2088
|
+
this.isShowingPlaceholder = false;
|
|
2090
2089
|
initializePolyfills();
|
|
2091
2090
|
const browserOverrides = getBrowserConfigOverrides();
|
|
2092
2091
|
this.config = { ...config, ...browserOverrides };
|
|
@@ -2393,16 +2392,11 @@ var StormcloudVideoPlayer = class {
|
|
|
2393
2392
|
return;
|
|
2394
2393
|
}
|
|
2395
2394
|
const remaining = this.getRemainingAdMs();
|
|
2396
|
-
if (
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
this.handleAdPodComplete();
|
|
2402
|
-
});
|
|
2403
|
-
} else {
|
|
2404
|
-
this.handleAdPodComplete();
|
|
2405
|
-
}
|
|
2395
|
+
if (this.config.debugAdTiming) {
|
|
2396
|
+
console.log(`[CONTINUOUS-FETCH] content_resume event: remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}`);
|
|
2397
|
+
}
|
|
2398
|
+
if (remaining > 500) {
|
|
2399
|
+
this.tryNextAvailableAd();
|
|
2406
2400
|
} else {
|
|
2407
2401
|
this.handleAdPodComplete();
|
|
2408
2402
|
}
|
|
@@ -3002,155 +2996,240 @@ var StormcloudVideoPlayer = class {
|
|
|
3002
2996
|
return true;
|
|
3003
2997
|
}
|
|
3004
2998
|
async handleAdStart(_marker) {
|
|
3005
|
-
var _a;
|
|
3006
2999
|
const scheduled = this.findCurrentOrNextBreak(
|
|
3007
3000
|
this.video.currentTime * 1e3
|
|
3008
3001
|
);
|
|
3009
3002
|
const tags = this.selectVastTagsForBreak(scheduled);
|
|
3010
|
-
let
|
|
3003
|
+
let baseVastUrl;
|
|
3011
3004
|
if (this.apiVastTagUrl) {
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3005
|
+
baseVastUrl = this.apiVastTagUrl;
|
|
3006
|
+
} else if (tags && tags.length > 0 && tags[0]) {
|
|
3007
|
+
baseVastUrl = tags[0];
|
|
3008
|
+
} else {
|
|
3009
|
+
return;
|
|
3010
|
+
}
|
|
3011
|
+
const adBreakDurationMs = _marker.durationSeconds != null ? _marker.durationSeconds * 1e3 : scheduled == null ? void 0 : scheduled.durationMs;
|
|
3012
|
+
if (this.isLiveStream && adBreakDurationMs != null && adBreakDurationMs > 0) {
|
|
3013
|
+
this.isAdaptiveMode = true;
|
|
3014
|
+
this.targetAdBreakDurationMs = adBreakDurationMs;
|
|
3015
|
+
this.fetchedAdDurations.clear();
|
|
3016
|
+
if (this.config.debugAdTiming) {
|
|
3017
|
+
console.log(
|
|
3018
|
+
`[CONTINUOUS-FETCH] \u{1F4FA} LIVE MODE: Target duration=${adBreakDurationMs}ms | Will continuously fetch ads during break`
|
|
3019
|
+
);
|
|
3020
|
+
}
|
|
3021
|
+
} else {
|
|
3022
|
+
this.isAdaptiveMode = false;
|
|
3023
|
+
this.targetAdBreakDurationMs = null;
|
|
3024
|
+
this.fetchedAdDurations.clear();
|
|
3025
|
+
if (this.config.debugAdTiming) {
|
|
3026
|
+
console.log(
|
|
3027
|
+
`[CONTINUOUS-FETCH] \u{1F3AC} VOD MODE: Using fixed ad strategy`
|
|
3028
|
+
);
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
this.adPodAllUrls = [];
|
|
3032
|
+
this.preloadingAdUrls.clear();
|
|
3033
|
+
this.vastToMediaUrlMap.clear();
|
|
3034
|
+
this.preloadedMediaUrls.clear();
|
|
3035
|
+
this.preloadingMediaUrls.clear();
|
|
3036
|
+
this.failedVastUrls.clear();
|
|
3037
|
+
this.adRequestQueue = [];
|
|
3038
|
+
this.successfulAdRequests = [];
|
|
3039
|
+
this.continuousFetchingActive = true;
|
|
3040
|
+
this.isShowingPlaceholder = false;
|
|
3041
|
+
this.placeholderStartTimeMs = null;
|
|
3042
|
+
const currentMuted = this.video.muted;
|
|
3043
|
+
const currentVolume = this.video.volume;
|
|
3044
|
+
this.ima.updateOriginalMutedState(currentMuted, currentVolume);
|
|
3045
|
+
this.inAdBreak = true;
|
|
3046
|
+
this.currentAdIndex = 0;
|
|
3047
|
+
this.totalAdsInBreak = 1;
|
|
3048
|
+
this.adPodQueue = [];
|
|
3049
|
+
if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
|
|
3050
|
+
this.expectedAdBreakDurationMs = adBreakDurationMs;
|
|
3051
|
+
this.currentAdBreakStartWallClockMs = Date.now();
|
|
3052
|
+
this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
|
|
3053
|
+
}
|
|
3054
|
+
if (this.config.debugAdTiming) {
|
|
3055
|
+
console.log("[CONTINUOUS-FETCH] \u{1F680} Immediately requesting first ad...");
|
|
3056
|
+
}
|
|
3057
|
+
const firstAdUrlArray = this.generateVastUrlsWithCorrelators(baseVastUrl, 1);
|
|
3058
|
+
const firstAdUrl = firstAdUrlArray[0];
|
|
3059
|
+
if (!firstAdUrl) {
|
|
3060
|
+
if (this.config.debugAdTiming) {
|
|
3061
|
+
console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Failed to generate first ad URL");
|
|
3062
|
+
}
|
|
3063
|
+
this.handleAdPodComplete();
|
|
3064
|
+
return;
|
|
3065
|
+
}
|
|
3066
|
+
try {
|
|
3067
|
+
await this.ima.requestAds(firstAdUrl);
|
|
3068
|
+
if (this.config.debugAdTiming) {
|
|
3069
|
+
console.log("[CONTINUOUS-FETCH] \u2705 First ad request successful, starting playback");
|
|
3070
|
+
}
|
|
3071
|
+
this.successfulAdRequests.push(firstAdUrl);
|
|
3072
|
+
this.currentAdIndex++;
|
|
3073
|
+
this.startContinuousFetching(baseVastUrl);
|
|
3074
|
+
await this.ima.play();
|
|
3075
|
+
} catch (error) {
|
|
3076
|
+
if (this.config.debugAdTiming) {
|
|
3077
|
+
console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F First ad request failed:", error);
|
|
3078
|
+
}
|
|
3079
|
+
this.failedVastUrls.add(firstAdUrl);
|
|
3080
|
+
this.startContinuousFetching(baseVastUrl);
|
|
3081
|
+
this.tryNextAvailableAd();
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
startContinuousFetching(baseVastUrl) {
|
|
3085
|
+
if (!this.continuousFetchingActive) {
|
|
3086
|
+
return;
|
|
3087
|
+
}
|
|
3088
|
+
if (this.config.debugAdTiming) {
|
|
3089
|
+
console.log("[CONTINUOUS-FETCH] \u{1F504} Starting continuous ad fetching loop");
|
|
3090
|
+
}
|
|
3091
|
+
this.continuousFetchLoop(baseVastUrl);
|
|
3092
|
+
}
|
|
3093
|
+
async continuousFetchLoop(baseVastUrl) {
|
|
3094
|
+
while (this.continuousFetchingActive && this.inAdBreak) {
|
|
3095
|
+
const remaining = this.getRemainingAdMs();
|
|
3096
|
+
if (remaining <= 0) {
|
|
3097
|
+
if (this.config.debugAdTiming) {
|
|
3098
|
+
console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F Ad break time expired, stopping fetch loop");
|
|
3031
3099
|
}
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
this.
|
|
3036
|
-
|
|
3037
|
-
|
|
3100
|
+
break;
|
|
3101
|
+
}
|
|
3102
|
+
if (this.adRequestQueue.length > 0) {
|
|
3103
|
+
if (this.config.debugAdTiming) {
|
|
3104
|
+
console.log(`[CONTINUOUS-FETCH] \u23F3 Already have ${this.adRequestQueue.length} ads queued, waiting...`);
|
|
3105
|
+
}
|
|
3106
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
3107
|
+
continue;
|
|
3108
|
+
}
|
|
3109
|
+
const newAdUrl = this.generateVastUrlsWithCorrelators(baseVastUrl, 1)[0];
|
|
3110
|
+
if (!newAdUrl || this.failedVastUrls.has(newAdUrl)) {
|
|
3111
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3112
|
+
continue;
|
|
3113
|
+
}
|
|
3114
|
+
if (this.config.debugAdTiming) {
|
|
3115
|
+
console.log("[CONTINUOUS-FETCH] \u{1F4E1} Attempting to fetch additional ad...");
|
|
3116
|
+
}
|
|
3117
|
+
try {
|
|
3118
|
+
const response = await fetch(newAdUrl, { mode: "cors" });
|
|
3119
|
+
if (!response.ok) {
|
|
3120
|
+
throw new Error(`Failed to fetch VAST: ${response.status}`);
|
|
3121
|
+
}
|
|
3122
|
+
const xmlText = await response.text();
|
|
3123
|
+
const parser = new DOMParser();
|
|
3124
|
+
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
|
|
3125
|
+
const mediaFiles = xmlDoc.querySelectorAll("MediaFile");
|
|
3126
|
+
if (mediaFiles.length === 0) {
|
|
3038
3127
|
if (this.config.debugAdTiming) {
|
|
3039
|
-
console.log(
|
|
3040
|
-
`[DEBUG-POD] \u{1F3AC} VOD MODE (FIXED): Using number_ads=${numberOfAds} from API`
|
|
3041
|
-
);
|
|
3128
|
+
console.log("[CONTINUOUS-FETCH] \u26A0\uFE0F VAST response has no media files, skipping");
|
|
3042
3129
|
}
|
|
3130
|
+
this.failedVastUrls.add(newAdUrl);
|
|
3131
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3132
|
+
continue;
|
|
3043
3133
|
}
|
|
3044
|
-
}
|
|
3045
|
-
if (numberOfAds > 1) {
|
|
3046
|
-
vastTagUrls = this.generateVastUrlsWithCorrelators(
|
|
3047
|
-
this.apiVastTagUrl,
|
|
3048
|
-
numberOfAds
|
|
3049
|
-
);
|
|
3050
3134
|
if (this.config.debugAdTiming) {
|
|
3051
|
-
console.log(
|
|
3052
|
-
`[DEBUG-POD] \u{1F504} Generated ${vastTagUrls.length} initial VAST URLs with unique correlators`
|
|
3053
|
-
);
|
|
3135
|
+
console.log("[CONTINUOUS-FETCH] \u2705 Successfully fetched new ad, adding to queue");
|
|
3054
3136
|
}
|
|
3055
|
-
|
|
3056
|
-
|
|
3137
|
+
this.adRequestQueue.push(newAdUrl);
|
|
3138
|
+
this.totalAdsInBreak++;
|
|
3139
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
3140
|
+
} catch (error) {
|
|
3141
|
+
if (this.config.debugAdTiming) {
|
|
3142
|
+
console.log("[CONTINUOUS-FETCH] \u274C Ad fetch failed:", error.message);
|
|
3143
|
+
}
|
|
3144
|
+
this.failedVastUrls.add(newAdUrl);
|
|
3145
|
+
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
3057
3146
|
}
|
|
3058
|
-
}
|
|
3059
|
-
|
|
3060
|
-
|
|
3147
|
+
}
|
|
3148
|
+
if (this.config.debugAdTiming) {
|
|
3149
|
+
console.log("[CONTINUOUS-FETCH] \u{1F6D1} Continuous fetch loop ended");
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
stopContinuousFetching() {
|
|
3153
|
+
this.continuousFetchingActive = false;
|
|
3154
|
+
if (this.config.debugAdTiming) {
|
|
3155
|
+
console.log("[CONTINUOUS-FETCH] \u{1F6D1} Stopping continuous ad fetching");
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
async tryNextAvailableAd() {
|
|
3159
|
+
const remaining = this.getRemainingAdMs();
|
|
3160
|
+
if (remaining <= 500) {
|
|
3161
|
+
if (this.config.debugAdTiming) {
|
|
3162
|
+
console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F No time remaining, ending ad break");
|
|
3163
|
+
}
|
|
3164
|
+
this.handleAdPodComplete();
|
|
3061
3165
|
return;
|
|
3062
3166
|
}
|
|
3063
|
-
if (
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
this.vastToMediaUrlMap.clear();
|
|
3067
|
-
this.preloadedMediaUrls.clear();
|
|
3068
|
-
this.preloadingMediaUrls.clear();
|
|
3069
|
-
this.failedVastUrls.clear();
|
|
3070
|
-
const currentMuted = this.video.muted;
|
|
3071
|
-
const currentVolume = this.video.volume;
|
|
3072
|
-
this.ima.updateOriginalMutedState(currentMuted, currentVolume);
|
|
3073
|
-
this.inAdBreak = true;
|
|
3074
|
-
this.currentAdIndex = 0;
|
|
3075
|
-
this.totalAdsInBreak = vastTagUrls.length;
|
|
3076
|
-
this.adPodQueue = [...vastTagUrls];
|
|
3077
|
-
if (this.isAdaptiveMode) {
|
|
3167
|
+
if (this.adRequestQueue.length > 0) {
|
|
3168
|
+
const nextAdUrl = this.adRequestQueue.shift();
|
|
3169
|
+
if (nextAdUrl) {
|
|
3078
3170
|
if (this.config.debugAdTiming) {
|
|
3079
|
-
console.log("[
|
|
3171
|
+
console.log("[CONTINUOUS-FETCH] \u{1F3AC} Playing next queued ad");
|
|
3080
3172
|
}
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
const url = this.adPodAllUrls[i];
|
|
3085
|
-
if (url) {
|
|
3086
|
-
await this.preloadSingleAd(url);
|
|
3087
|
-
if (this.config.debugAdTiming) {
|
|
3088
|
-
console.log(`[ADAPTIVE-POD] \u2705 Preloaded ad ${i + 1}/${adsToPreloadBeforeStart}`);
|
|
3089
|
-
}
|
|
3090
|
-
}
|
|
3091
|
-
}
|
|
3092
|
-
if (this.config.debugAdTiming) {
|
|
3093
|
-
console.log("[ADAPTIVE-POD] \u{1F3AC} First ads preloaded, starting playback immediately");
|
|
3094
|
-
}
|
|
3095
|
-
} catch (error) {
|
|
3096
|
-
if (this.config.debugAdTiming) {
|
|
3097
|
-
console.warn(
|
|
3098
|
-
"[ADAPTIVE-POD] \u26A0\uFE0F Error preloading initial ads:",
|
|
3099
|
-
error
|
|
3100
|
-
);
|
|
3101
|
-
}
|
|
3102
|
-
}
|
|
3103
|
-
this.preloadAllAdsInBackground().catch((error) => {
|
|
3104
|
-
if (this.config.debugAdTiming) {
|
|
3105
|
-
console.warn(
|
|
3106
|
-
"[ADAPTIVE-POD] Error in background preloading:",
|
|
3107
|
-
error
|
|
3108
|
-
);
|
|
3109
|
-
}
|
|
3110
|
-
});
|
|
3111
|
-
await this.playAdPod();
|
|
3112
|
-
} else {
|
|
3113
|
-
this.preloadAllAdsInBackground().catch((error) => {
|
|
3114
|
-
if (this.config.debugAdTiming) {
|
|
3115
|
-
console.warn(
|
|
3116
|
-
"[StormcloudVideoPlayer] Error in background preloading:",
|
|
3117
|
-
error
|
|
3118
|
-
);
|
|
3119
|
-
}
|
|
3173
|
+
this.currentAdIndex++;
|
|
3174
|
+
await this.playSingleAd(nextAdUrl).catch(() => {
|
|
3175
|
+
this.tryNextAvailableAd();
|
|
3120
3176
|
});
|
|
3121
|
-
|
|
3177
|
+
return;
|
|
3122
3178
|
}
|
|
3123
3179
|
}
|
|
3124
|
-
if (this.
|
|
3125
|
-
this.
|
|
3126
|
-
|
|
3127
|
-
|
|
3180
|
+
if (!this.isShowingPlaceholder && remaining > 1e3) {
|
|
3181
|
+
this.showPlaceholderAndWaitForAds();
|
|
3182
|
+
} else {
|
|
3183
|
+
if (this.config.debugAdTiming) {
|
|
3184
|
+
console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F No more ads available, ending ad break");
|
|
3185
|
+
}
|
|
3186
|
+
this.handleAdPodComplete();
|
|
3128
3187
|
}
|
|
3129
3188
|
}
|
|
3130
|
-
async
|
|
3131
|
-
|
|
3189
|
+
async showPlaceholderAndWaitForAds() {
|
|
3190
|
+
const remaining = this.getRemainingAdMs();
|
|
3191
|
+
const waitTime = Math.min(this.maxPlaceholderDurationMs, remaining);
|
|
3192
|
+
if (waitTime < 1e3) {
|
|
3193
|
+
this.handleAdPodComplete();
|
|
3132
3194
|
return;
|
|
3133
3195
|
}
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3196
|
+
if (this.config.debugAdTiming) {
|
|
3197
|
+
console.log(`[CONTINUOUS-FETCH] \u2B1B Showing black placeholder for ${waitTime}ms while waiting for ads`);
|
|
3198
|
+
}
|
|
3199
|
+
this.isShowingPlaceholder = true;
|
|
3200
|
+
this.placeholderStartTimeMs = Date.now();
|
|
3201
|
+
this.ima.showPlaceholder();
|
|
3202
|
+
const checkInterval = 500;
|
|
3203
|
+
const maxChecks = Math.floor(waitTime / checkInterval);
|
|
3204
|
+
for (let i = 0; i < maxChecks; i++) {
|
|
3205
|
+
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
3206
|
+
if (!this.inAdBreak) {
|
|
3207
|
+
return;
|
|
3208
|
+
}
|
|
3209
|
+
if (this.adRequestQueue.length > 0) {
|
|
3210
|
+
if (this.config.debugAdTiming) {
|
|
3211
|
+
console.log("[CONTINUOUS-FETCH] \u2705 New ad became available during placeholder");
|
|
3212
|
+
}
|
|
3213
|
+
this.isShowingPlaceholder = false;
|
|
3214
|
+
this.placeholderStartTimeMs = null;
|
|
3215
|
+
this.ima.hidePlaceholder();
|
|
3216
|
+
const nextAdUrl = this.adRequestQueue.shift();
|
|
3217
|
+
if (nextAdUrl) {
|
|
3218
|
+
this.currentAdIndex++;
|
|
3219
|
+
await this.playSingleAd(nextAdUrl).catch(() => {
|
|
3220
|
+
this.tryNextAvailableAd();
|
|
3221
|
+
});
|
|
3145
3222
|
}
|
|
3223
|
+
return;
|
|
3146
3224
|
}
|
|
3147
|
-
return;
|
|
3148
3225
|
}
|
|
3149
|
-
this.
|
|
3150
|
-
|
|
3151
|
-
await this.playSingleAd(firstPreloaded);
|
|
3152
|
-
} catch (error) {
|
|
3226
|
+
if (this.config.debugAdTiming) {
|
|
3227
|
+
console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout reached, no ads fetched");
|
|
3153
3228
|
}
|
|
3229
|
+
this.isShowingPlaceholder = false;
|
|
3230
|
+
this.placeholderStartTimeMs = null;
|
|
3231
|
+
this.ima.hidePlaceholder();
|
|
3232
|
+
this.handleAdPodComplete();
|
|
3154
3233
|
}
|
|
3155
3234
|
findCurrentOrNextBreak(nowMs) {
|
|
3156
3235
|
var _a;
|
|
@@ -3310,10 +3389,18 @@ var StormcloudVideoPlayer = class {
|
|
|
3310
3389
|
this.clearAdRequestWatchdog();
|
|
3311
3390
|
this.clearAdFailsafeTimer();
|
|
3312
3391
|
this.activeAdRequestToken = null;
|
|
3392
|
+
this.stopContinuousFetching();
|
|
3393
|
+
if (this.isShowingPlaceholder) {
|
|
3394
|
+
this.ima.hidePlaceholder();
|
|
3395
|
+
this.isShowingPlaceholder = false;
|
|
3396
|
+
this.placeholderStartTimeMs = null;
|
|
3397
|
+
}
|
|
3313
3398
|
this.preloadingAdUrls.clear();
|
|
3314
3399
|
this.vastToMediaUrlMap.clear();
|
|
3315
3400
|
this.preloadedMediaUrls.clear();
|
|
3316
3401
|
this.preloadingMediaUrls.clear();
|
|
3402
|
+
this.adRequestQueue = [];
|
|
3403
|
+
this.successfulAdRequests = [];
|
|
3317
3404
|
this.inAdBreak = false;
|
|
3318
3405
|
this.expectedAdBreakDurationMs = void 0;
|
|
3319
3406
|
this.currentAdBreakStartWallClockMs = void 0;
|
|
@@ -3340,61 +3427,14 @@ var StormcloudVideoPlayer = class {
|
|
|
3340
3427
|
}
|
|
3341
3428
|
}
|
|
3342
3429
|
handleAdFailure() {
|
|
3343
|
-
const remaining = this.getRemainingAdMs();
|
|
3344
|
-
const availableAds = this.adPodQueue.filter((url) => !this.failedVastUrls.has(url)).length;
|
|
3345
|
-
if (remaining > 500 && availableAds > 0) {
|
|
3346
|
-
if (this.isAdaptiveMode && this.currentAdIndex <= 1) {
|
|
3347
|
-
console.log("[ADAPTIVE-POD] \u23F3 First ad failed, waiting for sequential preload to catch up...");
|
|
3348
|
-
setTimeout(() => {
|
|
3349
|
-
this.tryNextAdWithRetry(0);
|
|
3350
|
-
}, 1500);
|
|
3351
|
-
return;
|
|
3352
|
-
}
|
|
3353
|
-
const nextPreloaded = this.findNextPreloadedAd();
|
|
3354
|
-
if (nextPreloaded) {
|
|
3355
|
-
this.currentAdIndex++;
|
|
3356
|
-
this.playSingleAd(nextPreloaded).catch(() => {
|
|
3357
|
-
this.handleAdPodComplete();
|
|
3358
|
-
});
|
|
3359
|
-
return;
|
|
3360
|
-
}
|
|
3361
|
-
}
|
|
3362
|
-
console.error("[AD-ERROR] All ads failed or time expired. Failed URLs:", this.failedVastUrls.size);
|
|
3363
|
-
this.handleAdPodComplete();
|
|
3364
|
-
}
|
|
3365
|
-
tryNextAdWithRetry(retryCount) {
|
|
3366
|
-
const maxRetries = 3;
|
|
3367
3430
|
const remaining = this.getRemainingAdMs();
|
|
3368
3431
|
if (this.config.debugAdTiming) {
|
|
3369
|
-
console.log(
|
|
3370
|
-
`[ADAPTIVE-POD] \u{1F50D} Retry attempt ${retryCount}: remaining=${remaining}ms, queue=${this.adPodQueue.length}, totalAds=${this.totalAdsInBreak}`
|
|
3371
|
-
);
|
|
3432
|
+
console.log(`[CONTINUOUS-FETCH] Ad failure: remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}`);
|
|
3372
3433
|
}
|
|
3373
|
-
if (remaining
|
|
3374
|
-
|
|
3375
|
-
this.handleAdPodComplete();
|
|
3376
|
-
return;
|
|
3377
|
-
}
|
|
3378
|
-
const nextPreloaded = this.findNextPreloadedAd();
|
|
3379
|
-
if (nextPreloaded) {
|
|
3380
|
-
this.currentAdIndex++;
|
|
3381
|
-
console.log(
|
|
3382
|
-
`[ADAPTIVE-POD] \u2705 Found preloaded ad after retry ${retryCount}, playing (${this.currentAdIndex}/${this.totalAdsInBreak})`
|
|
3383
|
-
);
|
|
3384
|
-
this.playSingleAd(nextPreloaded).catch(() => {
|
|
3385
|
-
this.handleAdPodComplete();
|
|
3386
|
-
});
|
|
3387
|
-
} else if (retryCount < maxRetries) {
|
|
3388
|
-
console.log(
|
|
3389
|
-
`[ADAPTIVE-POD] \u23F3 No preloaded ads yet (queue has ${this.adPodQueue.length} URLs), retry ${retryCount + 1}/${maxRetries} in 1s...`
|
|
3390
|
-
);
|
|
3391
|
-
setTimeout(() => {
|
|
3392
|
-
this.tryNextAdWithRetry(retryCount + 1);
|
|
3393
|
-
}, 1e3);
|
|
3434
|
+
if (remaining > 500) {
|
|
3435
|
+
this.tryNextAvailableAd();
|
|
3394
3436
|
} else {
|
|
3395
|
-
console.
|
|
3396
|
-
`[ADAPTIVE-POD] \u274C Max retries reached, no preloaded ads available (queue=${this.adPodQueue.length} URLs)`
|
|
3397
|
-
);
|
|
3437
|
+
console.error("[AD-ERROR] Ad failed and no time remaining. Failed URLs:", this.failedVastUrls.size);
|
|
3398
3438
|
this.handleAdPodComplete();
|
|
3399
3439
|
}
|
|
3400
3440
|
}
|
|
@@ -3791,6 +3831,12 @@ var StormcloudVideoPlayer = class {
|
|
|
3791
3831
|
);
|
|
3792
3832
|
}
|
|
3793
3833
|
mediaUrls = await this.fetchAndParseVastXml(vastTagUrl);
|
|
3834
|
+
if (this.config.debugAdTiming) {
|
|
3835
|
+
console.log(
|
|
3836
|
+
`[StormcloudVideoPlayer] Extracted ${mediaUrls.length} media URLs:`,
|
|
3837
|
+
mediaUrls
|
|
3838
|
+
);
|
|
3839
|
+
}
|
|
3794
3840
|
if (mediaUrls.length > 0) {
|
|
3795
3841
|
this.vastToMediaUrlMap.set(vastTagUrl, mediaUrls);
|
|
3796
3842
|
}
|
|
@@ -3986,6 +4032,7 @@ var StormcloudVideoPlayer = class {
|
|
|
3986
4032
|
}
|
|
3987
4033
|
destroy() {
|
|
3988
4034
|
var _a, _b;
|
|
4035
|
+
this.stopContinuousFetching();
|
|
3989
4036
|
this.clearAdStartTimer();
|
|
3990
4037
|
this.clearAdStopTimer();
|
|
3991
4038
|
this.clearAdFailsafeTimer();
|
|
@@ -4000,6 +4047,8 @@ var StormcloudVideoPlayer = class {
|
|
|
4000
4047
|
this.preloadedMediaUrls.clear();
|
|
4001
4048
|
this.preloadingMediaUrls.clear();
|
|
4002
4049
|
this.adPodAllUrls = [];
|
|
4050
|
+
this.adRequestQueue = [];
|
|
4051
|
+
this.successfulAdRequests = [];
|
|
4003
4052
|
}
|
|
4004
4053
|
};
|
|
4005
4054
|
|