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
|
@@ -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;
|
|
@@ -344,12 +344,8 @@ function createImaController(video, options) {
|
|
|
344
344
|
let adsLoadedReject;
|
|
345
345
|
function makeAdsRequest(google, vastTagUrl) {
|
|
346
346
|
const adsRequest = new google.ima.AdsRequest();
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
adsRequest.adsResponse = preloadedResponse;
|
|
350
|
-
} else {
|
|
351
|
-
adsRequest.adTagUrl = vastTagUrl;
|
|
352
|
-
}
|
|
347
|
+
console.log("[IMA] \u{1F4E1} Requesting VAST from URL (letting IMA fetch fresh)");
|
|
348
|
+
adsRequest.adTagUrl = vastTagUrl;
|
|
353
349
|
const videoWidth = video.offsetWidth || video.clientWidth || 640;
|
|
354
350
|
const videoHeight = video.offsetHeight || video.clientHeight || 360;
|
|
355
351
|
adsRequest.linearAdSlotWidth = videoWidth;
|
|
@@ -372,11 +368,8 @@ function createImaController(video, options) {
|
|
|
372
368
|
console.warn("[IMA] Failed to call setAdWillPlayMuted:", error);
|
|
373
369
|
}
|
|
374
370
|
}
|
|
375
|
-
adsRequest.vastLoadTimeout =
|
|
371
|
+
adsRequest.vastLoadTimeout = 1e4;
|
|
376
372
|
adsLoader.requestAds(adsRequest);
|
|
377
|
-
if (preloadedResponse) {
|
|
378
|
-
preloadedVast.delete(vastTagUrl);
|
|
379
|
-
}
|
|
380
373
|
}
|
|
381
374
|
function ensurePlaceholderContainer() {
|
|
382
375
|
var _a;
|
|
@@ -2092,6 +2085,12 @@ var StormcloudVideoPlayer = class {
|
|
|
2092
2085
|
this.targetAdBreakDurationMs = null;
|
|
2093
2086
|
this.isAdaptiveMode = false;
|
|
2094
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;
|
|
2095
2094
|
initializePolyfills();
|
|
2096
2095
|
const browserOverrides = getBrowserConfigOverrides();
|
|
2097
2096
|
this.config = { ...config, ...browserOverrides };
|
|
@@ -2398,16 +2397,11 @@ var StormcloudVideoPlayer = class {
|
|
|
2398
2397
|
return;
|
|
2399
2398
|
}
|
|
2400
2399
|
const remaining = this.getRemainingAdMs();
|
|
2401
|
-
if (
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
this.handleAdPodComplete();
|
|
2407
|
-
});
|
|
2408
|
-
} else {
|
|
2409
|
-
this.handleAdPodComplete();
|
|
2410
|
-
}
|
|
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();
|
|
2411
2405
|
} else {
|
|
2412
2406
|
this.handleAdPodComplete();
|
|
2413
2407
|
}
|
|
@@ -3007,155 +3001,240 @@ var StormcloudVideoPlayer = class {
|
|
|
3007
3001
|
return true;
|
|
3008
3002
|
}
|
|
3009
3003
|
async handleAdStart(_marker) {
|
|
3010
|
-
var _a;
|
|
3011
3004
|
const scheduled = this.findCurrentOrNextBreak(
|
|
3012
3005
|
this.video.currentTime * 1e3
|
|
3013
3006
|
);
|
|
3014
3007
|
const tags = this.selectVastTagsForBreak(scheduled);
|
|
3015
|
-
let
|
|
3008
|
+
let baseVastUrl;
|
|
3016
3009
|
if (this.apiVastTagUrl) {
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
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");
|
|
3036
3104
|
}
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
this.
|
|
3041
|
-
|
|
3042
|
-
|
|
3105
|
+
break;
|
|
3106
|
+
}
|
|
3107
|
+
if (this.adRequestQueue.length > 0) {
|
|
3108
|
+
if (this.config.debugAdTiming) {
|
|
3109
|
+
console.log(`[CONTINUOUS-FETCH] \u23F3 Already have ${this.adRequestQueue.length} ads queued, waiting...`);
|
|
3110
|
+
}
|
|
3111
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
3112
|
+
continue;
|
|
3113
|
+
}
|
|
3114
|
+
const newAdUrl = this.generateVastUrlsWithCorrelators(baseVastUrl, 1)[0];
|
|
3115
|
+
if (!newAdUrl || this.failedVastUrls.has(newAdUrl)) {
|
|
3116
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3117
|
+
continue;
|
|
3118
|
+
}
|
|
3119
|
+
if (this.config.debugAdTiming) {
|
|
3120
|
+
console.log("[CONTINUOUS-FETCH] \u{1F4E1} Attempting to fetch additional ad...");
|
|
3121
|
+
}
|
|
3122
|
+
try {
|
|
3123
|
+
const response = await fetch(newAdUrl, { mode: "cors" });
|
|
3124
|
+
if (!response.ok) {
|
|
3125
|
+
throw new Error(`Failed to fetch VAST: ${response.status}`);
|
|
3126
|
+
}
|
|
3127
|
+
const xmlText = await response.text();
|
|
3128
|
+
const parser = new DOMParser();
|
|
3129
|
+
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
|
|
3130
|
+
const mediaFiles = xmlDoc.querySelectorAll("MediaFile");
|
|
3131
|
+
if (mediaFiles.length === 0) {
|
|
3043
3132
|
if (this.config.debugAdTiming) {
|
|
3044
|
-
console.log(
|
|
3045
|
-
`[DEBUG-POD] \u{1F3AC} VOD MODE (FIXED): Using number_ads=${numberOfAds} from API`
|
|
3046
|
-
);
|
|
3133
|
+
console.log("[CONTINUOUS-FETCH] \u26A0\uFE0F VAST response has no media files, skipping");
|
|
3047
3134
|
}
|
|
3135
|
+
this.failedVastUrls.add(newAdUrl);
|
|
3136
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
3137
|
+
continue;
|
|
3048
3138
|
}
|
|
3049
|
-
}
|
|
3050
|
-
if (numberOfAds > 1) {
|
|
3051
|
-
vastTagUrls = this.generateVastUrlsWithCorrelators(
|
|
3052
|
-
this.apiVastTagUrl,
|
|
3053
|
-
numberOfAds
|
|
3054
|
-
);
|
|
3055
3139
|
if (this.config.debugAdTiming) {
|
|
3056
|
-
console.log(
|
|
3057
|
-
`[DEBUG-POD] \u{1F504} Generated ${vastTagUrls.length} initial VAST URLs with unique correlators`
|
|
3058
|
-
);
|
|
3140
|
+
console.log("[CONTINUOUS-FETCH] \u2705 Successfully fetched new ad, adding to queue");
|
|
3059
3141
|
}
|
|
3060
|
-
|
|
3061
|
-
|
|
3142
|
+
this.adRequestQueue.push(newAdUrl);
|
|
3143
|
+
this.totalAdsInBreak++;
|
|
3144
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
3145
|
+
} catch (error) {
|
|
3146
|
+
if (this.config.debugAdTiming) {
|
|
3147
|
+
console.log("[CONTINUOUS-FETCH] \u274C Ad fetch failed:", error.message);
|
|
3148
|
+
}
|
|
3149
|
+
this.failedVastUrls.add(newAdUrl);
|
|
3150
|
+
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
3062
3151
|
}
|
|
3063
|
-
}
|
|
3064
|
-
|
|
3065
|
-
|
|
3152
|
+
}
|
|
3153
|
+
if (this.config.debugAdTiming) {
|
|
3154
|
+
console.log("[CONTINUOUS-FETCH] \u{1F6D1} Continuous fetch loop ended");
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
stopContinuousFetching() {
|
|
3158
|
+
this.continuousFetchingActive = false;
|
|
3159
|
+
if (this.config.debugAdTiming) {
|
|
3160
|
+
console.log("[CONTINUOUS-FETCH] \u{1F6D1} Stopping continuous ad fetching");
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
async tryNextAvailableAd() {
|
|
3164
|
+
const remaining = this.getRemainingAdMs();
|
|
3165
|
+
if (remaining <= 500) {
|
|
3166
|
+
if (this.config.debugAdTiming) {
|
|
3167
|
+
console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F No time remaining, ending ad break");
|
|
3168
|
+
}
|
|
3169
|
+
this.handleAdPodComplete();
|
|
3066
3170
|
return;
|
|
3067
3171
|
}
|
|
3068
|
-
if (
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
this.vastToMediaUrlMap.clear();
|
|
3072
|
-
this.preloadedMediaUrls.clear();
|
|
3073
|
-
this.preloadingMediaUrls.clear();
|
|
3074
|
-
this.failedVastUrls.clear();
|
|
3075
|
-
const currentMuted = this.video.muted;
|
|
3076
|
-
const currentVolume = this.video.volume;
|
|
3077
|
-
this.ima.updateOriginalMutedState(currentMuted, currentVolume);
|
|
3078
|
-
this.inAdBreak = true;
|
|
3079
|
-
this.currentAdIndex = 0;
|
|
3080
|
-
this.totalAdsInBreak = vastTagUrls.length;
|
|
3081
|
-
this.adPodQueue = [...vastTagUrls];
|
|
3082
|
-
if (this.isAdaptiveMode) {
|
|
3172
|
+
if (this.adRequestQueue.length > 0) {
|
|
3173
|
+
const nextAdUrl = this.adRequestQueue.shift();
|
|
3174
|
+
if (nextAdUrl) {
|
|
3083
3175
|
if (this.config.debugAdTiming) {
|
|
3084
|
-
console.log("[
|
|
3176
|
+
console.log("[CONTINUOUS-FETCH] \u{1F3AC} Playing next queued ad");
|
|
3085
3177
|
}
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
const url = this.adPodAllUrls[i];
|
|
3090
|
-
if (url) {
|
|
3091
|
-
await this.preloadSingleAd(url);
|
|
3092
|
-
if (this.config.debugAdTiming) {
|
|
3093
|
-
console.log(`[ADAPTIVE-POD] \u2705 Preloaded ad ${i + 1}/${adsToPreloadBeforeStart}`);
|
|
3094
|
-
}
|
|
3095
|
-
}
|
|
3096
|
-
}
|
|
3097
|
-
if (this.config.debugAdTiming) {
|
|
3098
|
-
console.log("[ADAPTIVE-POD] \u{1F3AC} First ads preloaded, starting playback immediately");
|
|
3099
|
-
}
|
|
3100
|
-
} catch (error) {
|
|
3101
|
-
if (this.config.debugAdTiming) {
|
|
3102
|
-
console.warn(
|
|
3103
|
-
"[ADAPTIVE-POD] \u26A0\uFE0F Error preloading initial ads:",
|
|
3104
|
-
error
|
|
3105
|
-
);
|
|
3106
|
-
}
|
|
3107
|
-
}
|
|
3108
|
-
this.preloadAllAdsInBackground().catch((error) => {
|
|
3109
|
-
if (this.config.debugAdTiming) {
|
|
3110
|
-
console.warn(
|
|
3111
|
-
"[ADAPTIVE-POD] Error in background preloading:",
|
|
3112
|
-
error
|
|
3113
|
-
);
|
|
3114
|
-
}
|
|
3115
|
-
});
|
|
3116
|
-
await this.playAdPod();
|
|
3117
|
-
} else {
|
|
3118
|
-
this.preloadAllAdsInBackground().catch((error) => {
|
|
3119
|
-
if (this.config.debugAdTiming) {
|
|
3120
|
-
console.warn(
|
|
3121
|
-
"[StormcloudVideoPlayer] Error in background preloading:",
|
|
3122
|
-
error
|
|
3123
|
-
);
|
|
3124
|
-
}
|
|
3178
|
+
this.currentAdIndex++;
|
|
3179
|
+
await this.playSingleAd(nextAdUrl).catch(() => {
|
|
3180
|
+
this.tryNextAvailableAd();
|
|
3125
3181
|
});
|
|
3126
|
-
|
|
3182
|
+
return;
|
|
3127
3183
|
}
|
|
3128
3184
|
}
|
|
3129
|
-
if (this.
|
|
3130
|
-
this.
|
|
3131
|
-
|
|
3132
|
-
|
|
3185
|
+
if (!this.isShowingPlaceholder && remaining > 1e3) {
|
|
3186
|
+
this.showPlaceholderAndWaitForAds();
|
|
3187
|
+
} else {
|
|
3188
|
+
if (this.config.debugAdTiming) {
|
|
3189
|
+
console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F No more ads available, ending ad break");
|
|
3190
|
+
}
|
|
3191
|
+
this.handleAdPodComplete();
|
|
3133
3192
|
}
|
|
3134
3193
|
}
|
|
3135
|
-
async
|
|
3136
|
-
|
|
3194
|
+
async showPlaceholderAndWaitForAds() {
|
|
3195
|
+
const remaining = this.getRemainingAdMs();
|
|
3196
|
+
const waitTime = Math.min(this.maxPlaceholderDurationMs, remaining);
|
|
3197
|
+
if (waitTime < 1e3) {
|
|
3198
|
+
this.handleAdPodComplete();
|
|
3137
3199
|
return;
|
|
3138
3200
|
}
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3201
|
+
if (this.config.debugAdTiming) {
|
|
3202
|
+
console.log(`[CONTINUOUS-FETCH] \u2B1B Showing black placeholder for ${waitTime}ms while waiting for ads`);
|
|
3203
|
+
}
|
|
3204
|
+
this.isShowingPlaceholder = true;
|
|
3205
|
+
this.placeholderStartTimeMs = Date.now();
|
|
3206
|
+
this.ima.showPlaceholder();
|
|
3207
|
+
const checkInterval = 500;
|
|
3208
|
+
const maxChecks = Math.floor(waitTime / checkInterval);
|
|
3209
|
+
for (let i = 0; i < maxChecks; i++) {
|
|
3210
|
+
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
3211
|
+
if (!this.inAdBreak) {
|
|
3212
|
+
return;
|
|
3213
|
+
}
|
|
3214
|
+
if (this.adRequestQueue.length > 0) {
|
|
3215
|
+
if (this.config.debugAdTiming) {
|
|
3216
|
+
console.log("[CONTINUOUS-FETCH] \u2705 New ad became available during placeholder");
|
|
3217
|
+
}
|
|
3218
|
+
this.isShowingPlaceholder = false;
|
|
3219
|
+
this.placeholderStartTimeMs = null;
|
|
3220
|
+
this.ima.hidePlaceholder();
|
|
3221
|
+
const nextAdUrl = this.adRequestQueue.shift();
|
|
3222
|
+
if (nextAdUrl) {
|
|
3223
|
+
this.currentAdIndex++;
|
|
3224
|
+
await this.playSingleAd(nextAdUrl).catch(() => {
|
|
3225
|
+
this.tryNextAvailableAd();
|
|
3226
|
+
});
|
|
3150
3227
|
}
|
|
3228
|
+
return;
|
|
3151
3229
|
}
|
|
3152
|
-
return;
|
|
3153
3230
|
}
|
|
3154
|
-
this.
|
|
3155
|
-
|
|
3156
|
-
await this.playSingleAd(firstPreloaded);
|
|
3157
|
-
} catch (error) {
|
|
3231
|
+
if (this.config.debugAdTiming) {
|
|
3232
|
+
console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout reached, no ads fetched");
|
|
3158
3233
|
}
|
|
3234
|
+
this.isShowingPlaceholder = false;
|
|
3235
|
+
this.placeholderStartTimeMs = null;
|
|
3236
|
+
this.ima.hidePlaceholder();
|
|
3237
|
+
this.handleAdPodComplete();
|
|
3159
3238
|
}
|
|
3160
3239
|
findCurrentOrNextBreak(nowMs) {
|
|
3161
3240
|
var _a;
|
|
@@ -3315,10 +3394,18 @@ var StormcloudVideoPlayer = class {
|
|
|
3315
3394
|
this.clearAdRequestWatchdog();
|
|
3316
3395
|
this.clearAdFailsafeTimer();
|
|
3317
3396
|
this.activeAdRequestToken = null;
|
|
3397
|
+
this.stopContinuousFetching();
|
|
3398
|
+
if (this.isShowingPlaceholder) {
|
|
3399
|
+
this.ima.hidePlaceholder();
|
|
3400
|
+
this.isShowingPlaceholder = false;
|
|
3401
|
+
this.placeholderStartTimeMs = null;
|
|
3402
|
+
}
|
|
3318
3403
|
this.preloadingAdUrls.clear();
|
|
3319
3404
|
this.vastToMediaUrlMap.clear();
|
|
3320
3405
|
this.preloadedMediaUrls.clear();
|
|
3321
3406
|
this.preloadingMediaUrls.clear();
|
|
3407
|
+
this.adRequestQueue = [];
|
|
3408
|
+
this.successfulAdRequests = [];
|
|
3322
3409
|
this.inAdBreak = false;
|
|
3323
3410
|
this.expectedAdBreakDurationMs = void 0;
|
|
3324
3411
|
this.currentAdBreakStartWallClockMs = void 0;
|
|
@@ -3345,61 +3432,14 @@ var StormcloudVideoPlayer = class {
|
|
|
3345
3432
|
}
|
|
3346
3433
|
}
|
|
3347
3434
|
handleAdFailure() {
|
|
3348
|
-
const remaining = this.getRemainingAdMs();
|
|
3349
|
-
const availableAds = this.adPodQueue.filter((url) => !this.failedVastUrls.has(url)).length;
|
|
3350
|
-
if (remaining > 500 && availableAds > 0) {
|
|
3351
|
-
if (this.isAdaptiveMode && this.currentAdIndex <= 1) {
|
|
3352
|
-
console.log("[ADAPTIVE-POD] \u23F3 First ad failed, waiting for sequential preload to catch up...");
|
|
3353
|
-
setTimeout(() => {
|
|
3354
|
-
this.tryNextAdWithRetry(0);
|
|
3355
|
-
}, 1500);
|
|
3356
|
-
return;
|
|
3357
|
-
}
|
|
3358
|
-
const nextPreloaded = this.findNextPreloadedAd();
|
|
3359
|
-
if (nextPreloaded) {
|
|
3360
|
-
this.currentAdIndex++;
|
|
3361
|
-
this.playSingleAd(nextPreloaded).catch(() => {
|
|
3362
|
-
this.handleAdPodComplete();
|
|
3363
|
-
});
|
|
3364
|
-
return;
|
|
3365
|
-
}
|
|
3366
|
-
}
|
|
3367
|
-
console.error("[AD-ERROR] All ads failed or time expired. Failed URLs:", this.failedVastUrls.size);
|
|
3368
|
-
this.handleAdPodComplete();
|
|
3369
|
-
}
|
|
3370
|
-
tryNextAdWithRetry(retryCount) {
|
|
3371
|
-
const maxRetries = 3;
|
|
3372
3435
|
const remaining = this.getRemainingAdMs();
|
|
3373
3436
|
if (this.config.debugAdTiming) {
|
|
3374
|
-
console.log(
|
|
3375
|
-
`[ADAPTIVE-POD] \u{1F50D} Retry attempt ${retryCount}: remaining=${remaining}ms, queue=${this.adPodQueue.length}, totalAds=${this.totalAdsInBreak}`
|
|
3376
|
-
);
|
|
3437
|
+
console.log(`[CONTINUOUS-FETCH] Ad failure: remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}`);
|
|
3377
3438
|
}
|
|
3378
|
-
if (remaining
|
|
3379
|
-
|
|
3380
|
-
this.handleAdPodComplete();
|
|
3381
|
-
return;
|
|
3382
|
-
}
|
|
3383
|
-
const nextPreloaded = this.findNextPreloadedAd();
|
|
3384
|
-
if (nextPreloaded) {
|
|
3385
|
-
this.currentAdIndex++;
|
|
3386
|
-
console.log(
|
|
3387
|
-
`[ADAPTIVE-POD] \u2705 Found preloaded ad after retry ${retryCount}, playing (${this.currentAdIndex}/${this.totalAdsInBreak})`
|
|
3388
|
-
);
|
|
3389
|
-
this.playSingleAd(nextPreloaded).catch(() => {
|
|
3390
|
-
this.handleAdPodComplete();
|
|
3391
|
-
});
|
|
3392
|
-
} else if (retryCount < maxRetries) {
|
|
3393
|
-
console.log(
|
|
3394
|
-
`[ADAPTIVE-POD] \u23F3 No preloaded ads yet (queue has ${this.adPodQueue.length} URLs), retry ${retryCount + 1}/${maxRetries} in 1s...`
|
|
3395
|
-
);
|
|
3396
|
-
setTimeout(() => {
|
|
3397
|
-
this.tryNextAdWithRetry(retryCount + 1);
|
|
3398
|
-
}, 1e3);
|
|
3439
|
+
if (remaining > 500) {
|
|
3440
|
+
this.tryNextAvailableAd();
|
|
3399
3441
|
} else {
|
|
3400
|
-
console.
|
|
3401
|
-
`[ADAPTIVE-POD] \u274C Max retries reached, no preloaded ads available (queue=${this.adPodQueue.length} URLs)`
|
|
3402
|
-
);
|
|
3442
|
+
console.error("[AD-ERROR] Ad failed and no time remaining. Failed URLs:", this.failedVastUrls.size);
|
|
3403
3443
|
this.handleAdPodComplete();
|
|
3404
3444
|
}
|
|
3405
3445
|
}
|
|
@@ -3796,6 +3836,12 @@ var StormcloudVideoPlayer = class {
|
|
|
3796
3836
|
);
|
|
3797
3837
|
}
|
|
3798
3838
|
mediaUrls = await this.fetchAndParseVastXml(vastTagUrl);
|
|
3839
|
+
if (this.config.debugAdTiming) {
|
|
3840
|
+
console.log(
|
|
3841
|
+
`[StormcloudVideoPlayer] Extracted ${mediaUrls.length} media URLs:`,
|
|
3842
|
+
mediaUrls
|
|
3843
|
+
);
|
|
3844
|
+
}
|
|
3799
3845
|
if (mediaUrls.length > 0) {
|
|
3800
3846
|
this.vastToMediaUrlMap.set(vastTagUrl, mediaUrls);
|
|
3801
3847
|
}
|
|
@@ -3991,6 +4037,7 @@ var StormcloudVideoPlayer = class {
|
|
|
3991
4037
|
}
|
|
3992
4038
|
destroy() {
|
|
3993
4039
|
var _a, _b;
|
|
4040
|
+
this.stopContinuousFetching();
|
|
3994
4041
|
this.clearAdStartTimer();
|
|
3995
4042
|
this.clearAdStopTimer();
|
|
3996
4043
|
this.clearAdFailsafeTimer();
|
|
@@ -4005,6 +4052,8 @@ var StormcloudVideoPlayer = class {
|
|
|
4005
4052
|
this.preloadedMediaUrls.clear();
|
|
4006
4053
|
this.preloadingMediaUrls.clear();
|
|
4007
4054
|
this.adPodAllUrls = [];
|
|
4055
|
+
this.adRequestQueue = [];
|
|
4056
|
+
this.successfulAdRequests = [];
|
|
4008
4057
|
}
|
|
4009
4058
|
};
|
|
4010
4059
|
|