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
|
@@ -41,6 +41,12 @@ declare class StormcloudVideoPlayer {
|
|
|
41
41
|
private targetAdBreakDurationMs;
|
|
42
42
|
private isAdaptiveMode;
|
|
43
43
|
private failedVastUrls;
|
|
44
|
+
private continuousFetchingActive;
|
|
45
|
+
private adRequestQueue;
|
|
46
|
+
private successfulAdRequests;
|
|
47
|
+
private maxPlaceholderDurationMs;
|
|
48
|
+
private placeholderStartTimeMs;
|
|
49
|
+
private isShowingPlaceholder;
|
|
44
50
|
constructor(config: StormcloudVideoPlayerConfig);
|
|
45
51
|
private createAdPlayer;
|
|
46
52
|
load(): Promise<void>;
|
|
@@ -68,7 +74,11 @@ declare class StormcloudVideoPlayer {
|
|
|
68
74
|
shouldShowNativeControls(): boolean;
|
|
69
75
|
private shouldContinueLiveStreamDuringAds;
|
|
70
76
|
private handleAdStart;
|
|
71
|
-
private
|
|
77
|
+
private startContinuousFetching;
|
|
78
|
+
private continuousFetchLoop;
|
|
79
|
+
private stopContinuousFetching;
|
|
80
|
+
private tryNextAvailableAd;
|
|
81
|
+
private showPlaceholderAndWaitForAds;
|
|
72
82
|
private findCurrentOrNextBreak;
|
|
73
83
|
private onTimeUpdate;
|
|
74
84
|
private handleMidAdJoin;
|
|
@@ -81,7 +91,6 @@ declare class StormcloudVideoPlayer {
|
|
|
81
91
|
private playSingleAd;
|
|
82
92
|
private handleAdPodComplete;
|
|
83
93
|
private handleAdFailure;
|
|
84
|
-
private tryNextAdWithRetry;
|
|
85
94
|
private startAdRequestWatchdog;
|
|
86
95
|
private clearAdRequestWatchdog;
|
|
87
96
|
private startAdFailsafeTimer;
|
|
@@ -2085,6 +2085,12 @@ var StormcloudVideoPlayer = class {
|
|
|
2085
2085
|
this.targetAdBreakDurationMs = null;
|
|
2086
2086
|
this.isAdaptiveMode = false;
|
|
2087
2087
|
this.failedVastUrls = /* @__PURE__ */ new Set();
|
|
2088
|
+
this.continuousFetchingActive = false;
|
|
2089
|
+
this.adRequestQueue = [];
|
|
2090
|
+
this.successfulAdRequests = [];
|
|
2091
|
+
this.maxPlaceholderDurationMs = 5e3;
|
|
2092
|
+
this.placeholderStartTimeMs = null;
|
|
2093
|
+
this.isShowingPlaceholder = false;
|
|
2088
2094
|
initializePolyfills();
|
|
2089
2095
|
const browserOverrides = getBrowserConfigOverrides();
|
|
2090
2096
|
this.config = { ...config, ...browserOverrides };
|
|
@@ -2391,16 +2397,11 @@ var StormcloudVideoPlayer = class {
|
|
|
2391
2397
|
return;
|
|
2392
2398
|
}
|
|
2393
2399
|
const remaining = this.getRemainingAdMs();
|
|
2394
|
-
if (
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
this.handleAdPodComplete();
|
|
2400
|
-
});
|
|
2401
|
-
} else {
|
|
2402
|
-
this.handleAdPodComplete();
|
|
2403
|
-
}
|
|
2400
|
+
if (this.config.debugAdTiming) {
|
|
2401
|
+
console.log(`[CONTINUOUS-FETCH] content_resume event: remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}`);
|
|
2402
|
+
}
|
|
2403
|
+
if (remaining > 500) {
|
|
2404
|
+
this.tryNextAvailableAd();
|
|
2404
2405
|
} else {
|
|
2405
2406
|
this.handleAdPodComplete();
|
|
2406
2407
|
}
|
|
@@ -3000,155 +3001,251 @@ var StormcloudVideoPlayer = class {
|
|
|
3000
3001
|
return true;
|
|
3001
3002
|
}
|
|
3002
3003
|
async handleAdStart(_marker) {
|
|
3003
|
-
var _a;
|
|
3004
3004
|
const scheduled = this.findCurrentOrNextBreak(
|
|
3005
3005
|
this.video.currentTime * 1e3
|
|
3006
3006
|
);
|
|
3007
3007
|
const tags = this.selectVastTagsForBreak(scheduled);
|
|
3008
|
-
let
|
|
3008
|
+
let baseVastUrl;
|
|
3009
3009
|
if (this.apiVastTagUrl) {
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3010
|
+
baseVastUrl = this.apiVastTagUrl;
|
|
3011
|
+
} else if (tags && tags.length > 0 && tags[0]) {
|
|
3012
|
+
baseVastUrl = tags[0];
|
|
3013
|
+
} else {
|
|
3014
|
+
return;
|
|
3015
|
+
}
|
|
3016
|
+
const adBreakDurationMs = _marker.durationSeconds != null ? _marker.durationSeconds * 1e3 : scheduled == null ? void 0 : scheduled.durationMs;
|
|
3017
|
+
if (this.isLiveStream && adBreakDurationMs != null && adBreakDurationMs > 0) {
|
|
3018
|
+
this.isAdaptiveMode = true;
|
|
3019
|
+
this.targetAdBreakDurationMs = adBreakDurationMs;
|
|
3020
|
+
this.fetchedAdDurations.clear();
|
|
3021
|
+
if (this.config.debugAdTiming) {
|
|
3022
|
+
console.log(
|
|
3023
|
+
`[CONTINUOUS-FETCH] \u{1F4FA} LIVE MODE: Target duration=${adBreakDurationMs}ms | Will continuously fetch ads during break`
|
|
3024
|
+
);
|
|
3025
|
+
}
|
|
3026
|
+
} else {
|
|
3027
|
+
this.isAdaptiveMode = false;
|
|
3028
|
+
this.targetAdBreakDurationMs = null;
|
|
3029
|
+
this.fetchedAdDurations.clear();
|
|
3030
|
+
if (this.config.debugAdTiming) {
|
|
3031
|
+
console.log(
|
|
3032
|
+
`[CONTINUOUS-FETCH] \u{1F3AC} VOD MODE: Using fixed ad strategy`
|
|
3033
|
+
);
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
this.adPodAllUrls = [];
|
|
3037
|
+
this.preloadingAdUrls.clear();
|
|
3038
|
+
this.vastToMediaUrlMap.clear();
|
|
3039
|
+
this.preloadedMediaUrls.clear();
|
|
3040
|
+
this.preloadingMediaUrls.clear();
|
|
3041
|
+
this.failedVastUrls.clear();
|
|
3042
|
+
this.adRequestQueue = [];
|
|
3043
|
+
this.successfulAdRequests = [];
|
|
3044
|
+
this.continuousFetchingActive = true;
|
|
3045
|
+
this.isShowingPlaceholder = false;
|
|
3046
|
+
this.placeholderStartTimeMs = null;
|
|
3047
|
+
const currentMuted = this.video.muted;
|
|
3048
|
+
const currentVolume = this.video.volume;
|
|
3049
|
+
this.ima.updateOriginalMutedState(currentMuted, currentVolume);
|
|
3050
|
+
this.inAdBreak = true;
|
|
3051
|
+
this.currentAdIndex = 0;
|
|
3052
|
+
this.totalAdsInBreak = 1;
|
|
3053
|
+
this.adPodQueue = [];
|
|
3054
|
+
if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
|
|
3055
|
+
this.expectedAdBreakDurationMs = adBreakDurationMs;
|
|
3056
|
+
this.currentAdBreakStartWallClockMs = Date.now();
|
|
3057
|
+
this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
|
|
3058
|
+
}
|
|
3059
|
+
if (this.config.debugAdTiming) {
|
|
3060
|
+
console.log("[CONTINUOUS-FETCH] \u{1F680} Immediately requesting first ad...");
|
|
3061
|
+
}
|
|
3062
|
+
const firstAdUrlArray = this.generateVastUrlsWithCorrelators(baseVastUrl, 1);
|
|
3063
|
+
const firstAdUrl = firstAdUrlArray[0];
|
|
3064
|
+
if (!firstAdUrl) {
|
|
3065
|
+
if (this.config.debugAdTiming) {
|
|
3066
|
+
console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Failed to generate first ad URL");
|
|
3067
|
+
}
|
|
3068
|
+
this.handleAdPodComplete();
|
|
3069
|
+
return;
|
|
3070
|
+
}
|
|
3071
|
+
try {
|
|
3072
|
+
await this.ima.requestAds(firstAdUrl);
|
|
3073
|
+
if (this.config.debugAdTiming) {
|
|
3074
|
+
console.log("[CONTINUOUS-FETCH] \u2705 First ad request successful, starting playback");
|
|
3075
|
+
}
|
|
3076
|
+
this.successfulAdRequests.push(firstAdUrl);
|
|
3077
|
+
this.currentAdIndex++;
|
|
3078
|
+
this.startContinuousFetching(baseVastUrl);
|
|
3079
|
+
await this.ima.play();
|
|
3080
|
+
} catch (error) {
|
|
3081
|
+
if (this.config.debugAdTiming) {
|
|
3082
|
+
console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F First ad request failed:", error);
|
|
3083
|
+
}
|
|
3084
|
+
this.failedVastUrls.add(firstAdUrl);
|
|
3085
|
+
this.startContinuousFetching(baseVastUrl);
|
|
3086
|
+
this.tryNextAvailableAd();
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
startContinuousFetching(baseVastUrl) {
|
|
3090
|
+
if (!this.continuousFetchingActive) {
|
|
3091
|
+
return;
|
|
3092
|
+
}
|
|
3093
|
+
if (this.config.debugAdTiming) {
|
|
3094
|
+
console.log("[CONTINUOUS-FETCH] \u{1F504} Starting continuous ad fetching loop");
|
|
3095
|
+
}
|
|
3096
|
+
this.continuousFetchLoop(baseVastUrl);
|
|
3097
|
+
}
|
|
3098
|
+
async continuousFetchLoop(baseVastUrl) {
|
|
3099
|
+
while (this.continuousFetchingActive && this.inAdBreak) {
|
|
3100
|
+
const remaining = this.getRemainingAdMs();
|
|
3101
|
+
if (remaining <= 0) {
|
|
3102
|
+
if (this.config.debugAdTiming) {
|
|
3103
|
+
console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F Ad break time expired, stopping fetch loop");
|
|
3029
3104
|
}
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
if (this.
|
|
3035
|
-
|
|
3105
|
+
break;
|
|
3106
|
+
}
|
|
3107
|
+
const maxQueueSize = 3;
|
|
3108
|
+
if (this.adRequestQueue.length >= maxQueueSize) {
|
|
3109
|
+
if (this.config.debugAdTiming) {
|
|
3110
|
+
console.log(`[CONTINUOUS-FETCH] \u23F8\uFE0F Queue full (${this.adRequestQueue.length}), pausing fetching...`);
|
|
3111
|
+
}
|
|
3112
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
3113
|
+
continue;
|
|
3114
|
+
}
|
|
3115
|
+
const newAdUrl = this.generateVastUrlsWithCorrelators(baseVastUrl, 1)[0];
|
|
3116
|
+
if (!newAdUrl || this.failedVastUrls.has(newAdUrl)) {
|
|
3117
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3118
|
+
continue;
|
|
3119
|
+
}
|
|
3120
|
+
if (this.config.debugAdTiming) {
|
|
3121
|
+
console.log(`[CONTINUOUS-FETCH] \u{1F4E1} Attempting to fetch ad (${this.successfulAdRequests.length + this.adRequestQueue.length + 1} total)...`);
|
|
3122
|
+
}
|
|
3123
|
+
try {
|
|
3124
|
+
const response = await fetch(newAdUrl, { mode: "cors" });
|
|
3125
|
+
if (!response.ok) {
|
|
3126
|
+
throw new Error(`Failed to fetch VAST: ${response.status}`);
|
|
3127
|
+
}
|
|
3128
|
+
const xmlText = await response.text();
|
|
3129
|
+
const parser = new DOMParser();
|
|
3130
|
+
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
|
|
3131
|
+
const mediaFiles = xmlDoc.querySelectorAll("MediaFile");
|
|
3132
|
+
if (mediaFiles.length === 0) {
|
|
3036
3133
|
if (this.config.debugAdTiming) {
|
|
3037
|
-
console.log(
|
|
3038
|
-
`[DEBUG-POD] \u{1F3AC} VOD MODE (FIXED): Using number_ads=${numberOfAds} from API`
|
|
3039
|
-
);
|
|
3134
|
+
console.log("[CONTINUOUS-FETCH] \u26A0\uFE0F VAST response has no media files, skipping");
|
|
3040
3135
|
}
|
|
3136
|
+
this.failedVastUrls.add(newAdUrl);
|
|
3137
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3138
|
+
continue;
|
|
3041
3139
|
}
|
|
3042
|
-
}
|
|
3043
|
-
if (numberOfAds > 1) {
|
|
3044
|
-
vastTagUrls = this.generateVastUrlsWithCorrelators(
|
|
3045
|
-
this.apiVastTagUrl,
|
|
3046
|
-
numberOfAds
|
|
3047
|
-
);
|
|
3048
3140
|
if (this.config.debugAdTiming) {
|
|
3049
|
-
console.log(
|
|
3050
|
-
`[DEBUG-POD] \u{1F504} Generated ${vastTagUrls.length} initial VAST URLs with unique correlators`
|
|
3051
|
-
);
|
|
3141
|
+
console.log(`[CONTINUOUS-FETCH] \u2705 Successfully fetched ad, adding to queue (queue size: ${this.adRequestQueue.length + 1})`);
|
|
3052
3142
|
}
|
|
3053
|
-
|
|
3054
|
-
|
|
3143
|
+
this.adRequestQueue.push(newAdUrl);
|
|
3144
|
+
this.totalAdsInBreak++;
|
|
3145
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
3146
|
+
} catch (error) {
|
|
3147
|
+
if (this.config.debugAdTiming) {
|
|
3148
|
+
console.log("[CONTINUOUS-FETCH] \u274C Ad fetch failed:", error.message);
|
|
3149
|
+
}
|
|
3150
|
+
this.failedVastUrls.add(newAdUrl);
|
|
3151
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
3055
3152
|
}
|
|
3056
|
-
}
|
|
3057
|
-
|
|
3058
|
-
|
|
3153
|
+
}
|
|
3154
|
+
if (this.config.debugAdTiming) {
|
|
3155
|
+
console.log("[CONTINUOUS-FETCH] \u{1F6D1} Continuous fetch loop ended");
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
stopContinuousFetching() {
|
|
3159
|
+
this.continuousFetchingActive = false;
|
|
3160
|
+
if (this.config.debugAdTiming) {
|
|
3161
|
+
console.log("[CONTINUOUS-FETCH] \u{1F6D1} Stopping continuous ad fetching");
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
async tryNextAvailableAd(retryCount = 0) {
|
|
3165
|
+
const remaining = this.getRemainingAdMs();
|
|
3166
|
+
if (remaining <= 500) {
|
|
3167
|
+
if (this.config.debugAdTiming) {
|
|
3168
|
+
console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F No time remaining, ending ad break");
|
|
3169
|
+
}
|
|
3170
|
+
this.handleAdPodComplete();
|
|
3059
3171
|
return;
|
|
3060
3172
|
}
|
|
3061
|
-
if (
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
this.vastToMediaUrlMap.clear();
|
|
3065
|
-
this.preloadedMediaUrls.clear();
|
|
3066
|
-
this.preloadingMediaUrls.clear();
|
|
3067
|
-
this.failedVastUrls.clear();
|
|
3068
|
-
const currentMuted = this.video.muted;
|
|
3069
|
-
const currentVolume = this.video.volume;
|
|
3070
|
-
this.ima.updateOriginalMutedState(currentMuted, currentVolume);
|
|
3071
|
-
this.inAdBreak = true;
|
|
3072
|
-
this.currentAdIndex = 0;
|
|
3073
|
-
this.totalAdsInBreak = vastTagUrls.length;
|
|
3074
|
-
this.adPodQueue = [...vastTagUrls];
|
|
3075
|
-
if (this.isAdaptiveMode) {
|
|
3173
|
+
if (this.adRequestQueue.length > 0) {
|
|
3174
|
+
const nextAdUrl = this.adRequestQueue.shift();
|
|
3175
|
+
if (nextAdUrl) {
|
|
3076
3176
|
if (this.config.debugAdTiming) {
|
|
3077
|
-
console.log(
|
|
3177
|
+
console.log(`[CONTINUOUS-FETCH] \u{1F3AC} Playing next queued ad (${this.currentAdIndex + 1}/${this.totalAdsInBreak}, ${this.adRequestQueue.length} remaining in queue)`);
|
|
3078
3178
|
}
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
if (url) {
|
|
3084
|
-
await this.preloadSingleAd(url);
|
|
3085
|
-
if (this.config.debugAdTiming) {
|
|
3086
|
-
console.log(`[ADAPTIVE-POD] \u2705 Preloaded ad ${i + 1}/${adsToPreloadBeforeStart}`);
|
|
3087
|
-
}
|
|
3088
|
-
}
|
|
3089
|
-
}
|
|
3090
|
-
if (this.config.debugAdTiming) {
|
|
3091
|
-
console.log("[ADAPTIVE-POD] \u{1F3AC} First ads preloaded, starting playback immediately");
|
|
3092
|
-
}
|
|
3093
|
-
} catch (error) {
|
|
3094
|
-
if (this.config.debugAdTiming) {
|
|
3095
|
-
console.warn(
|
|
3096
|
-
"[ADAPTIVE-POD] \u26A0\uFE0F Error preloading initial ads:",
|
|
3097
|
-
error
|
|
3098
|
-
);
|
|
3099
|
-
}
|
|
3100
|
-
}
|
|
3101
|
-
this.preloadAllAdsInBackground().catch((error) => {
|
|
3102
|
-
if (this.config.debugAdTiming) {
|
|
3103
|
-
console.warn(
|
|
3104
|
-
"[ADAPTIVE-POD] Error in background preloading:",
|
|
3105
|
-
error
|
|
3106
|
-
);
|
|
3107
|
-
}
|
|
3108
|
-
});
|
|
3109
|
-
await this.playAdPod();
|
|
3110
|
-
} else {
|
|
3111
|
-
this.preloadAllAdsInBackground().catch((error) => {
|
|
3112
|
-
if (this.config.debugAdTiming) {
|
|
3113
|
-
console.warn(
|
|
3114
|
-
"[StormcloudVideoPlayer] Error in background preloading:",
|
|
3115
|
-
error
|
|
3116
|
-
);
|
|
3117
|
-
}
|
|
3179
|
+
this.currentAdIndex++;
|
|
3180
|
+
this.successfulAdRequests.push(nextAdUrl);
|
|
3181
|
+
await this.playSingleAd(nextAdUrl).catch(() => {
|
|
3182
|
+
this.tryNextAvailableAd(0);
|
|
3118
3183
|
});
|
|
3119
|
-
|
|
3184
|
+
return;
|
|
3120
3185
|
}
|
|
3121
3186
|
}
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3187
|
+
const maxRetries = 5;
|
|
3188
|
+
if (this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3) {
|
|
3189
|
+
if (this.config.debugAdTiming) {
|
|
3190
|
+
console.log(`[CONTINUOUS-FETCH] \u23F3 Queue empty but fetching active, waiting... (retry ${retryCount + 1}/${maxRetries})`);
|
|
3191
|
+
}
|
|
3192
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3193
|
+
await this.tryNextAvailableAd(retryCount + 1);
|
|
3194
|
+
return;
|
|
3195
|
+
}
|
|
3196
|
+
if (!this.isShowingPlaceholder && remaining > 1e3) {
|
|
3197
|
+
this.showPlaceholderAndWaitForAds();
|
|
3198
|
+
} else {
|
|
3199
|
+
if (this.config.debugAdTiming) {
|
|
3200
|
+
console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F No more ads available, ending ad break");
|
|
3201
|
+
}
|
|
3202
|
+
this.handleAdPodComplete();
|
|
3126
3203
|
}
|
|
3127
3204
|
}
|
|
3128
|
-
async
|
|
3129
|
-
|
|
3205
|
+
async showPlaceholderAndWaitForAds() {
|
|
3206
|
+
const remaining = this.getRemainingAdMs();
|
|
3207
|
+
const waitTime = Math.min(this.maxPlaceholderDurationMs, remaining);
|
|
3208
|
+
if (waitTime < 1e3) {
|
|
3209
|
+
this.handleAdPodComplete();
|
|
3130
3210
|
return;
|
|
3131
3211
|
}
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3212
|
+
if (this.config.debugAdTiming) {
|
|
3213
|
+
console.log(`[CONTINUOUS-FETCH] \u2B1B Showing black placeholder for ${waitTime}ms while waiting for ads`);
|
|
3214
|
+
}
|
|
3215
|
+
this.isShowingPlaceholder = true;
|
|
3216
|
+
this.placeholderStartTimeMs = Date.now();
|
|
3217
|
+
this.ima.showPlaceholder();
|
|
3218
|
+
const checkInterval = 500;
|
|
3219
|
+
const maxChecks = Math.floor(waitTime / checkInterval);
|
|
3220
|
+
for (let i = 0; i < maxChecks; i++) {
|
|
3221
|
+
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
3222
|
+
if (!this.inAdBreak) {
|
|
3223
|
+
return;
|
|
3224
|
+
}
|
|
3225
|
+
if (this.adRequestQueue.length > 0) {
|
|
3226
|
+
if (this.config.debugAdTiming) {
|
|
3227
|
+
console.log("[CONTINUOUS-FETCH] \u2705 New ad became available during placeholder");
|
|
3228
|
+
}
|
|
3229
|
+
this.isShowingPlaceholder = false;
|
|
3230
|
+
this.placeholderStartTimeMs = null;
|
|
3231
|
+
this.ima.hidePlaceholder();
|
|
3232
|
+
const nextAdUrl = this.adRequestQueue.shift();
|
|
3233
|
+
if (nextAdUrl) {
|
|
3234
|
+
this.currentAdIndex++;
|
|
3235
|
+
await this.playSingleAd(nextAdUrl).catch(() => {
|
|
3236
|
+
this.tryNextAvailableAd();
|
|
3237
|
+
});
|
|
3143
3238
|
}
|
|
3239
|
+
return;
|
|
3144
3240
|
}
|
|
3145
|
-
return;
|
|
3146
3241
|
}
|
|
3147
|
-
this.
|
|
3148
|
-
|
|
3149
|
-
await this.playSingleAd(firstPreloaded);
|
|
3150
|
-
} catch (error) {
|
|
3242
|
+
if (this.config.debugAdTiming) {
|
|
3243
|
+
console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout reached, no ads fetched");
|
|
3151
3244
|
}
|
|
3245
|
+
this.isShowingPlaceholder = false;
|
|
3246
|
+
this.placeholderStartTimeMs = null;
|
|
3247
|
+
this.ima.hidePlaceholder();
|
|
3248
|
+
this.handleAdPodComplete();
|
|
3152
3249
|
}
|
|
3153
3250
|
findCurrentOrNextBreak(nowMs) {
|
|
3154
3251
|
var _a;
|
|
@@ -3308,10 +3405,18 @@ var StormcloudVideoPlayer = class {
|
|
|
3308
3405
|
this.clearAdRequestWatchdog();
|
|
3309
3406
|
this.clearAdFailsafeTimer();
|
|
3310
3407
|
this.activeAdRequestToken = null;
|
|
3408
|
+
this.stopContinuousFetching();
|
|
3409
|
+
if (this.isShowingPlaceholder) {
|
|
3410
|
+
this.ima.hidePlaceholder();
|
|
3411
|
+
this.isShowingPlaceholder = false;
|
|
3412
|
+
this.placeholderStartTimeMs = null;
|
|
3413
|
+
}
|
|
3311
3414
|
this.preloadingAdUrls.clear();
|
|
3312
3415
|
this.vastToMediaUrlMap.clear();
|
|
3313
3416
|
this.preloadedMediaUrls.clear();
|
|
3314
3417
|
this.preloadingMediaUrls.clear();
|
|
3418
|
+
this.adRequestQueue = [];
|
|
3419
|
+
this.successfulAdRequests = [];
|
|
3315
3420
|
this.inAdBreak = false;
|
|
3316
3421
|
this.expectedAdBreakDurationMs = void 0;
|
|
3317
3422
|
this.currentAdBreakStartWallClockMs = void 0;
|
|
@@ -3338,61 +3443,14 @@ var StormcloudVideoPlayer = class {
|
|
|
3338
3443
|
}
|
|
3339
3444
|
}
|
|
3340
3445
|
handleAdFailure() {
|
|
3341
|
-
const remaining = this.getRemainingAdMs();
|
|
3342
|
-
const availableAds = this.adPodQueue.filter((url) => !this.failedVastUrls.has(url)).length;
|
|
3343
|
-
if (remaining > 500 && availableAds > 0) {
|
|
3344
|
-
if (this.isAdaptiveMode && this.currentAdIndex <= 1) {
|
|
3345
|
-
console.log("[ADAPTIVE-POD] \u23F3 First ad failed, waiting for sequential preload to catch up...");
|
|
3346
|
-
setTimeout(() => {
|
|
3347
|
-
this.tryNextAdWithRetry(0);
|
|
3348
|
-
}, 1500);
|
|
3349
|
-
return;
|
|
3350
|
-
}
|
|
3351
|
-
const nextPreloaded = this.findNextPreloadedAd();
|
|
3352
|
-
if (nextPreloaded) {
|
|
3353
|
-
this.currentAdIndex++;
|
|
3354
|
-
this.playSingleAd(nextPreloaded).catch(() => {
|
|
3355
|
-
this.handleAdPodComplete();
|
|
3356
|
-
});
|
|
3357
|
-
return;
|
|
3358
|
-
}
|
|
3359
|
-
}
|
|
3360
|
-
console.error("[AD-ERROR] All ads failed or time expired. Failed URLs:", this.failedVastUrls.size);
|
|
3361
|
-
this.handleAdPodComplete();
|
|
3362
|
-
}
|
|
3363
|
-
tryNextAdWithRetry(retryCount) {
|
|
3364
|
-
const maxRetries = 3;
|
|
3365
3446
|
const remaining = this.getRemainingAdMs();
|
|
3366
3447
|
if (this.config.debugAdTiming) {
|
|
3367
|
-
console.log(
|
|
3368
|
-
`[ADAPTIVE-POD] \u{1F50D} Retry attempt ${retryCount}: remaining=${remaining}ms, queue=${this.adPodQueue.length}, totalAds=${this.totalAdsInBreak}`
|
|
3369
|
-
);
|
|
3370
|
-
}
|
|
3371
|
-
if (remaining <= 500 || this.adPodQueue.length === 0) {
|
|
3372
|
-
console.log("[ADAPTIVE-POD] \u23F9\uFE0F No more time or ads available");
|
|
3373
|
-
this.handleAdPodComplete();
|
|
3374
|
-
return;
|
|
3448
|
+
console.log(`[CONTINUOUS-FETCH] Ad failure: remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}`);
|
|
3375
3449
|
}
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
this.currentAdIndex++;
|
|
3379
|
-
console.log(
|
|
3380
|
-
`[ADAPTIVE-POD] \u2705 Found preloaded ad after retry ${retryCount}, playing (${this.currentAdIndex}/${this.totalAdsInBreak})`
|
|
3381
|
-
);
|
|
3382
|
-
this.playSingleAd(nextPreloaded).catch(() => {
|
|
3383
|
-
this.handleAdPodComplete();
|
|
3384
|
-
});
|
|
3385
|
-
} else if (retryCount < maxRetries) {
|
|
3386
|
-
console.log(
|
|
3387
|
-
`[ADAPTIVE-POD] \u23F3 No preloaded ads yet (queue has ${this.adPodQueue.length} URLs), retry ${retryCount + 1}/${maxRetries} in 1s...`
|
|
3388
|
-
);
|
|
3389
|
-
setTimeout(() => {
|
|
3390
|
-
this.tryNextAdWithRetry(retryCount + 1);
|
|
3391
|
-
}, 1e3);
|
|
3450
|
+
if (remaining > 500) {
|
|
3451
|
+
this.tryNextAvailableAd();
|
|
3392
3452
|
} else {
|
|
3393
|
-
console.
|
|
3394
|
-
`[ADAPTIVE-POD] \u274C Max retries reached, no preloaded ads available (queue=${this.adPodQueue.length} URLs)`
|
|
3395
|
-
);
|
|
3453
|
+
console.error("[AD-ERROR] Ad failed and no time remaining. Failed URLs:", this.failedVastUrls.size);
|
|
3396
3454
|
this.handleAdPodComplete();
|
|
3397
3455
|
}
|
|
3398
3456
|
}
|
|
@@ -3990,6 +4048,7 @@ var StormcloudVideoPlayer = class {
|
|
|
3990
4048
|
}
|
|
3991
4049
|
destroy() {
|
|
3992
4050
|
var _a, _b;
|
|
4051
|
+
this.stopContinuousFetching();
|
|
3993
4052
|
this.clearAdStartTimer();
|
|
3994
4053
|
this.clearAdStopTimer();
|
|
3995
4054
|
this.clearAdFailsafeTimer();
|
|
@@ -4004,6 +4063,8 @@ var StormcloudVideoPlayer = class {
|
|
|
4004
4063
|
this.preloadedMediaUrls.clear();
|
|
4005
4064
|
this.preloadingMediaUrls.clear();
|
|
4006
4065
|
this.adPodAllUrls = [];
|
|
4066
|
+
this.adRequestQueue = [];
|
|
4067
|
+
this.successfulAdRequests = [];
|
|
4007
4068
|
}
|
|
4008
4069
|
};
|
|
4009
4070
|
|