stormcloud-video-player 0.3.9 → 0.3.11
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 +244 -183
- 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 +244 -183
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +244 -183
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +11 -2
- package/lib/players/HlsPlayer.cjs +244 -183
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/index.cjs +244 -183
- package/lib/players/index.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.cjs +244 -183
- 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
|
@@ -2080,6 +2080,12 @@ var StormcloudVideoPlayer = class {
|
|
|
2080
2080
|
this.targetAdBreakDurationMs = null;
|
|
2081
2081
|
this.isAdaptiveMode = false;
|
|
2082
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;
|
|
2083
2089
|
initializePolyfills();
|
|
2084
2090
|
const browserOverrides = getBrowserConfigOverrides();
|
|
2085
2091
|
this.config = { ...config, ...browserOverrides };
|
|
@@ -2386,16 +2392,11 @@ var StormcloudVideoPlayer = class {
|
|
|
2386
2392
|
return;
|
|
2387
2393
|
}
|
|
2388
2394
|
const remaining = this.getRemainingAdMs();
|
|
2389
|
-
if (
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
this.handleAdPodComplete();
|
|
2395
|
-
});
|
|
2396
|
-
} else {
|
|
2397
|
-
this.handleAdPodComplete();
|
|
2398
|
-
}
|
|
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();
|
|
2399
2400
|
} else {
|
|
2400
2401
|
this.handleAdPodComplete();
|
|
2401
2402
|
}
|
|
@@ -2995,155 +2996,251 @@ var StormcloudVideoPlayer = class {
|
|
|
2995
2996
|
return true;
|
|
2996
2997
|
}
|
|
2997
2998
|
async handleAdStart(_marker) {
|
|
2998
|
-
var _a;
|
|
2999
2999
|
const scheduled = this.findCurrentOrNextBreak(
|
|
3000
3000
|
this.video.currentTime * 1e3
|
|
3001
3001
|
);
|
|
3002
3002
|
const tags = this.selectVastTagsForBreak(scheduled);
|
|
3003
|
-
let
|
|
3003
|
+
let baseVastUrl;
|
|
3004
3004
|
if (this.apiVastTagUrl) {
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
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");
|
|
3024
3099
|
}
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
if (this.
|
|
3030
|
-
|
|
3100
|
+
break;
|
|
3101
|
+
}
|
|
3102
|
+
const maxQueueSize = 3;
|
|
3103
|
+
if (this.adRequestQueue.length >= maxQueueSize) {
|
|
3104
|
+
if (this.config.debugAdTiming) {
|
|
3105
|
+
console.log(`[CONTINUOUS-FETCH] \u23F8\uFE0F Queue full (${this.adRequestQueue.length}), pausing fetching...`);
|
|
3106
|
+
}
|
|
3107
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
3108
|
+
continue;
|
|
3109
|
+
}
|
|
3110
|
+
const newAdUrl = this.generateVastUrlsWithCorrelators(baseVastUrl, 1)[0];
|
|
3111
|
+
if (!newAdUrl || this.failedVastUrls.has(newAdUrl)) {
|
|
3112
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3113
|
+
continue;
|
|
3114
|
+
}
|
|
3115
|
+
if (this.config.debugAdTiming) {
|
|
3116
|
+
console.log(`[CONTINUOUS-FETCH] \u{1F4E1} Attempting to fetch ad (${this.successfulAdRequests.length + this.adRequestQueue.length + 1} total)...`);
|
|
3117
|
+
}
|
|
3118
|
+
try {
|
|
3119
|
+
const response = await fetch(newAdUrl, { mode: "cors" });
|
|
3120
|
+
if (!response.ok) {
|
|
3121
|
+
throw new Error(`Failed to fetch VAST: ${response.status}`);
|
|
3122
|
+
}
|
|
3123
|
+
const xmlText = await response.text();
|
|
3124
|
+
const parser = new DOMParser();
|
|
3125
|
+
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
|
|
3126
|
+
const mediaFiles = xmlDoc.querySelectorAll("MediaFile");
|
|
3127
|
+
if (mediaFiles.length === 0) {
|
|
3031
3128
|
if (this.config.debugAdTiming) {
|
|
3032
|
-
console.log(
|
|
3033
|
-
`[DEBUG-POD] \u{1F3AC} VOD MODE (FIXED): Using number_ads=${numberOfAds} from API`
|
|
3034
|
-
);
|
|
3129
|
+
console.log("[CONTINUOUS-FETCH] \u26A0\uFE0F VAST response has no media files, skipping");
|
|
3035
3130
|
}
|
|
3131
|
+
this.failedVastUrls.add(newAdUrl);
|
|
3132
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3133
|
+
continue;
|
|
3036
3134
|
}
|
|
3037
|
-
}
|
|
3038
|
-
if (numberOfAds > 1) {
|
|
3039
|
-
vastTagUrls = this.generateVastUrlsWithCorrelators(
|
|
3040
|
-
this.apiVastTagUrl,
|
|
3041
|
-
numberOfAds
|
|
3042
|
-
);
|
|
3043
3135
|
if (this.config.debugAdTiming) {
|
|
3044
|
-
console.log(
|
|
3045
|
-
`[DEBUG-POD] \u{1F504} Generated ${vastTagUrls.length} initial VAST URLs with unique correlators`
|
|
3046
|
-
);
|
|
3136
|
+
console.log(`[CONTINUOUS-FETCH] \u2705 Successfully fetched ad, adding to queue (queue size: ${this.adRequestQueue.length + 1})`);
|
|
3047
3137
|
}
|
|
3048
|
-
|
|
3049
|
-
|
|
3138
|
+
this.adRequestQueue.push(newAdUrl);
|
|
3139
|
+
this.totalAdsInBreak++;
|
|
3140
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
3141
|
+
} catch (error) {
|
|
3142
|
+
if (this.config.debugAdTiming) {
|
|
3143
|
+
console.log("[CONTINUOUS-FETCH] \u274C Ad fetch failed:", error.message);
|
|
3144
|
+
}
|
|
3145
|
+
this.failedVastUrls.add(newAdUrl);
|
|
3146
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
3050
3147
|
}
|
|
3051
|
-
}
|
|
3052
|
-
|
|
3053
|
-
|
|
3148
|
+
}
|
|
3149
|
+
if (this.config.debugAdTiming) {
|
|
3150
|
+
console.log("[CONTINUOUS-FETCH] \u{1F6D1} Continuous fetch loop ended");
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
stopContinuousFetching() {
|
|
3154
|
+
this.continuousFetchingActive = false;
|
|
3155
|
+
if (this.config.debugAdTiming) {
|
|
3156
|
+
console.log("[CONTINUOUS-FETCH] \u{1F6D1} Stopping continuous ad fetching");
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
async tryNextAvailableAd(retryCount = 0) {
|
|
3160
|
+
const remaining = this.getRemainingAdMs();
|
|
3161
|
+
if (remaining <= 500) {
|
|
3162
|
+
if (this.config.debugAdTiming) {
|
|
3163
|
+
console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F No time remaining, ending ad break");
|
|
3164
|
+
}
|
|
3165
|
+
this.handleAdPodComplete();
|
|
3054
3166
|
return;
|
|
3055
3167
|
}
|
|
3056
|
-
if (
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
this.vastToMediaUrlMap.clear();
|
|
3060
|
-
this.preloadedMediaUrls.clear();
|
|
3061
|
-
this.preloadingMediaUrls.clear();
|
|
3062
|
-
this.failedVastUrls.clear();
|
|
3063
|
-
const currentMuted = this.video.muted;
|
|
3064
|
-
const currentVolume = this.video.volume;
|
|
3065
|
-
this.ima.updateOriginalMutedState(currentMuted, currentVolume);
|
|
3066
|
-
this.inAdBreak = true;
|
|
3067
|
-
this.currentAdIndex = 0;
|
|
3068
|
-
this.totalAdsInBreak = vastTagUrls.length;
|
|
3069
|
-
this.adPodQueue = [...vastTagUrls];
|
|
3070
|
-
if (this.isAdaptiveMode) {
|
|
3168
|
+
if (this.adRequestQueue.length > 0) {
|
|
3169
|
+
const nextAdUrl = this.adRequestQueue.shift();
|
|
3170
|
+
if (nextAdUrl) {
|
|
3071
3171
|
if (this.config.debugAdTiming) {
|
|
3072
|
-
console.log(
|
|
3172
|
+
console.log(`[CONTINUOUS-FETCH] \u{1F3AC} Playing next queued ad (${this.currentAdIndex + 1}/${this.totalAdsInBreak}, ${this.adRequestQueue.length} remaining in queue)`);
|
|
3073
3173
|
}
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
if (url) {
|
|
3079
|
-
await this.preloadSingleAd(url);
|
|
3080
|
-
if (this.config.debugAdTiming) {
|
|
3081
|
-
console.log(`[ADAPTIVE-POD] \u2705 Preloaded ad ${i + 1}/${adsToPreloadBeforeStart}`);
|
|
3082
|
-
}
|
|
3083
|
-
}
|
|
3084
|
-
}
|
|
3085
|
-
if (this.config.debugAdTiming) {
|
|
3086
|
-
console.log("[ADAPTIVE-POD] \u{1F3AC} First ads preloaded, starting playback immediately");
|
|
3087
|
-
}
|
|
3088
|
-
} catch (error) {
|
|
3089
|
-
if (this.config.debugAdTiming) {
|
|
3090
|
-
console.warn(
|
|
3091
|
-
"[ADAPTIVE-POD] \u26A0\uFE0F Error preloading initial ads:",
|
|
3092
|
-
error
|
|
3093
|
-
);
|
|
3094
|
-
}
|
|
3095
|
-
}
|
|
3096
|
-
this.preloadAllAdsInBackground().catch((error) => {
|
|
3097
|
-
if (this.config.debugAdTiming) {
|
|
3098
|
-
console.warn(
|
|
3099
|
-
"[ADAPTIVE-POD] Error in background preloading:",
|
|
3100
|
-
error
|
|
3101
|
-
);
|
|
3102
|
-
}
|
|
3103
|
-
});
|
|
3104
|
-
await this.playAdPod();
|
|
3105
|
-
} else {
|
|
3106
|
-
this.preloadAllAdsInBackground().catch((error) => {
|
|
3107
|
-
if (this.config.debugAdTiming) {
|
|
3108
|
-
console.warn(
|
|
3109
|
-
"[StormcloudVideoPlayer] Error in background preloading:",
|
|
3110
|
-
error
|
|
3111
|
-
);
|
|
3112
|
-
}
|
|
3174
|
+
this.currentAdIndex++;
|
|
3175
|
+
this.successfulAdRequests.push(nextAdUrl);
|
|
3176
|
+
await this.playSingleAd(nextAdUrl).catch(() => {
|
|
3177
|
+
this.tryNextAvailableAd(0);
|
|
3113
3178
|
});
|
|
3114
|
-
|
|
3179
|
+
return;
|
|
3115
3180
|
}
|
|
3116
3181
|
}
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3182
|
+
const maxRetries = 5;
|
|
3183
|
+
if (this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3) {
|
|
3184
|
+
if (this.config.debugAdTiming) {
|
|
3185
|
+
console.log(`[CONTINUOUS-FETCH] \u23F3 Queue empty but fetching active, waiting... (retry ${retryCount + 1}/${maxRetries})`);
|
|
3186
|
+
}
|
|
3187
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3188
|
+
await this.tryNextAvailableAd(retryCount + 1);
|
|
3189
|
+
return;
|
|
3190
|
+
}
|
|
3191
|
+
if (!this.isShowingPlaceholder && remaining > 1e3) {
|
|
3192
|
+
this.showPlaceholderAndWaitForAds();
|
|
3193
|
+
} else {
|
|
3194
|
+
if (this.config.debugAdTiming) {
|
|
3195
|
+
console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F No more ads available, ending ad break");
|
|
3196
|
+
}
|
|
3197
|
+
this.handleAdPodComplete();
|
|
3121
3198
|
}
|
|
3122
3199
|
}
|
|
3123
|
-
async
|
|
3124
|
-
|
|
3200
|
+
async showPlaceholderAndWaitForAds() {
|
|
3201
|
+
const remaining = this.getRemainingAdMs();
|
|
3202
|
+
const waitTime = Math.min(this.maxPlaceholderDurationMs, remaining);
|
|
3203
|
+
if (waitTime < 1e3) {
|
|
3204
|
+
this.handleAdPodComplete();
|
|
3125
3205
|
return;
|
|
3126
3206
|
}
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3207
|
+
if (this.config.debugAdTiming) {
|
|
3208
|
+
console.log(`[CONTINUOUS-FETCH] \u2B1B Showing black placeholder for ${waitTime}ms while waiting for ads`);
|
|
3209
|
+
}
|
|
3210
|
+
this.isShowingPlaceholder = true;
|
|
3211
|
+
this.placeholderStartTimeMs = Date.now();
|
|
3212
|
+
this.ima.showPlaceholder();
|
|
3213
|
+
const checkInterval = 500;
|
|
3214
|
+
const maxChecks = Math.floor(waitTime / checkInterval);
|
|
3215
|
+
for (let i = 0; i < maxChecks; i++) {
|
|
3216
|
+
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
3217
|
+
if (!this.inAdBreak) {
|
|
3218
|
+
return;
|
|
3219
|
+
}
|
|
3220
|
+
if (this.adRequestQueue.length > 0) {
|
|
3221
|
+
if (this.config.debugAdTiming) {
|
|
3222
|
+
console.log("[CONTINUOUS-FETCH] \u2705 New ad became available during placeholder");
|
|
3223
|
+
}
|
|
3224
|
+
this.isShowingPlaceholder = false;
|
|
3225
|
+
this.placeholderStartTimeMs = null;
|
|
3226
|
+
this.ima.hidePlaceholder();
|
|
3227
|
+
const nextAdUrl = this.adRequestQueue.shift();
|
|
3228
|
+
if (nextAdUrl) {
|
|
3229
|
+
this.currentAdIndex++;
|
|
3230
|
+
await this.playSingleAd(nextAdUrl).catch(() => {
|
|
3231
|
+
this.tryNextAvailableAd();
|
|
3232
|
+
});
|
|
3138
3233
|
}
|
|
3234
|
+
return;
|
|
3139
3235
|
}
|
|
3140
|
-
return;
|
|
3141
3236
|
}
|
|
3142
|
-
this.
|
|
3143
|
-
|
|
3144
|
-
await this.playSingleAd(firstPreloaded);
|
|
3145
|
-
} catch (error) {
|
|
3237
|
+
if (this.config.debugAdTiming) {
|
|
3238
|
+
console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout reached, no ads fetched");
|
|
3146
3239
|
}
|
|
3240
|
+
this.isShowingPlaceholder = false;
|
|
3241
|
+
this.placeholderStartTimeMs = null;
|
|
3242
|
+
this.ima.hidePlaceholder();
|
|
3243
|
+
this.handleAdPodComplete();
|
|
3147
3244
|
}
|
|
3148
3245
|
findCurrentOrNextBreak(nowMs) {
|
|
3149
3246
|
var _a;
|
|
@@ -3303,10 +3400,18 @@ var StormcloudVideoPlayer = class {
|
|
|
3303
3400
|
this.clearAdRequestWatchdog();
|
|
3304
3401
|
this.clearAdFailsafeTimer();
|
|
3305
3402
|
this.activeAdRequestToken = null;
|
|
3403
|
+
this.stopContinuousFetching();
|
|
3404
|
+
if (this.isShowingPlaceholder) {
|
|
3405
|
+
this.ima.hidePlaceholder();
|
|
3406
|
+
this.isShowingPlaceholder = false;
|
|
3407
|
+
this.placeholderStartTimeMs = null;
|
|
3408
|
+
}
|
|
3306
3409
|
this.preloadingAdUrls.clear();
|
|
3307
3410
|
this.vastToMediaUrlMap.clear();
|
|
3308
3411
|
this.preloadedMediaUrls.clear();
|
|
3309
3412
|
this.preloadingMediaUrls.clear();
|
|
3413
|
+
this.adRequestQueue = [];
|
|
3414
|
+
this.successfulAdRequests = [];
|
|
3310
3415
|
this.inAdBreak = false;
|
|
3311
3416
|
this.expectedAdBreakDurationMs = void 0;
|
|
3312
3417
|
this.currentAdBreakStartWallClockMs = void 0;
|
|
@@ -3333,61 +3438,14 @@ var StormcloudVideoPlayer = class {
|
|
|
3333
3438
|
}
|
|
3334
3439
|
}
|
|
3335
3440
|
handleAdFailure() {
|
|
3336
|
-
const remaining = this.getRemainingAdMs();
|
|
3337
|
-
const availableAds = this.adPodQueue.filter((url) => !this.failedVastUrls.has(url)).length;
|
|
3338
|
-
if (remaining > 500 && availableAds > 0) {
|
|
3339
|
-
if (this.isAdaptiveMode && this.currentAdIndex <= 1) {
|
|
3340
|
-
console.log("[ADAPTIVE-POD] \u23F3 First ad failed, waiting for sequential preload to catch up...");
|
|
3341
|
-
setTimeout(() => {
|
|
3342
|
-
this.tryNextAdWithRetry(0);
|
|
3343
|
-
}, 1500);
|
|
3344
|
-
return;
|
|
3345
|
-
}
|
|
3346
|
-
const nextPreloaded = this.findNextPreloadedAd();
|
|
3347
|
-
if (nextPreloaded) {
|
|
3348
|
-
this.currentAdIndex++;
|
|
3349
|
-
this.playSingleAd(nextPreloaded).catch(() => {
|
|
3350
|
-
this.handleAdPodComplete();
|
|
3351
|
-
});
|
|
3352
|
-
return;
|
|
3353
|
-
}
|
|
3354
|
-
}
|
|
3355
|
-
console.error("[AD-ERROR] All ads failed or time expired. Failed URLs:", this.failedVastUrls.size);
|
|
3356
|
-
this.handleAdPodComplete();
|
|
3357
|
-
}
|
|
3358
|
-
tryNextAdWithRetry(retryCount) {
|
|
3359
|
-
const maxRetries = 3;
|
|
3360
3441
|
const remaining = this.getRemainingAdMs();
|
|
3361
3442
|
if (this.config.debugAdTiming) {
|
|
3362
|
-
console.log(
|
|
3363
|
-
`[ADAPTIVE-POD] \u{1F50D} Retry attempt ${retryCount}: remaining=${remaining}ms, queue=${this.adPodQueue.length}, totalAds=${this.totalAdsInBreak}`
|
|
3364
|
-
);
|
|
3365
|
-
}
|
|
3366
|
-
if (remaining <= 500 || this.adPodQueue.length === 0) {
|
|
3367
|
-
console.log("[ADAPTIVE-POD] \u23F9\uFE0F No more time or ads available");
|
|
3368
|
-
this.handleAdPodComplete();
|
|
3369
|
-
return;
|
|
3443
|
+
console.log(`[CONTINUOUS-FETCH] Ad failure: remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}`);
|
|
3370
3444
|
}
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
this.currentAdIndex++;
|
|
3374
|
-
console.log(
|
|
3375
|
-
`[ADAPTIVE-POD] \u2705 Found preloaded ad after retry ${retryCount}, playing (${this.currentAdIndex}/${this.totalAdsInBreak})`
|
|
3376
|
-
);
|
|
3377
|
-
this.playSingleAd(nextPreloaded).catch(() => {
|
|
3378
|
-
this.handleAdPodComplete();
|
|
3379
|
-
});
|
|
3380
|
-
} else if (retryCount < maxRetries) {
|
|
3381
|
-
console.log(
|
|
3382
|
-
`[ADAPTIVE-POD] \u23F3 No preloaded ads yet (queue has ${this.adPodQueue.length} URLs), retry ${retryCount + 1}/${maxRetries} in 1s...`
|
|
3383
|
-
);
|
|
3384
|
-
setTimeout(() => {
|
|
3385
|
-
this.tryNextAdWithRetry(retryCount + 1);
|
|
3386
|
-
}, 1e3);
|
|
3445
|
+
if (remaining > 500) {
|
|
3446
|
+
this.tryNextAvailableAd();
|
|
3387
3447
|
} else {
|
|
3388
|
-
console.
|
|
3389
|
-
`[ADAPTIVE-POD] \u274C Max retries reached, no preloaded ads available (queue=${this.adPodQueue.length} URLs)`
|
|
3390
|
-
);
|
|
3448
|
+
console.error("[AD-ERROR] Ad failed and no time remaining. Failed URLs:", this.failedVastUrls.size);
|
|
3391
3449
|
this.handleAdPodComplete();
|
|
3392
3450
|
}
|
|
3393
3451
|
}
|
|
@@ -3985,6 +4043,7 @@ var StormcloudVideoPlayer = class {
|
|
|
3985
4043
|
}
|
|
3986
4044
|
destroy() {
|
|
3987
4045
|
var _a, _b;
|
|
4046
|
+
this.stopContinuousFetching();
|
|
3988
4047
|
this.clearAdStartTimer();
|
|
3989
4048
|
this.clearAdStopTimer();
|
|
3990
4049
|
this.clearAdFailsafeTimer();
|
|
@@ -3999,6 +4058,8 @@ var StormcloudVideoPlayer = class {
|
|
|
3999
4058
|
this.preloadedMediaUrls.clear();
|
|
4000
4059
|
this.preloadingMediaUrls.clear();
|
|
4001
4060
|
this.adPodAllUrls = [];
|
|
4061
|
+
this.adRequestQueue = [];
|
|
4062
|
+
this.successfulAdRequests = [];
|
|
4002
4063
|
}
|
|
4003
4064
|
};
|
|
4004
4065
|
|