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.
@@ -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 playAdPod;
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
- const preloadedResponse = preloadedVast.get(vastTagUrl);
348
- if (preloadedResponse) {
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 = 5e3;
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 (remaining > 500 && this.adPodQueue.length > 0) {
2402
- const nextPreloaded = this.findNextPreloadedAd();
2403
- if (nextPreloaded) {
2404
- this.currentAdIndex++;
2405
- this.playSingleAd(nextPreloaded).catch(() => {
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 vastTagUrls = [];
3008
+ let baseVastUrl;
3016
3009
  if (this.apiVastTagUrl) {
3017
- let numberOfAds = 1;
3018
- if (this.isLiveStream) {
3019
- const adBreakDurationMs = _marker.durationSeconds != null ? _marker.durationSeconds * 1e3 : scheduled == null ? void 0 : scheduled.durationMs;
3020
- if (adBreakDurationMs != null && adBreakDurationMs > 0) {
3021
- this.isAdaptiveMode = true;
3022
- this.targetAdBreakDurationMs = adBreakDurationMs;
3023
- this.fetchedAdDurations.clear();
3024
- numberOfAds = 2;
3025
- if (this.config.debugAdTiming) {
3026
- console.log(
3027
- `[ADAPTIVE-POD] \u{1F4FA} LIVE MODE (ADAPTIVE): Target duration=${adBreakDurationMs}ms | Starting with ${numberOfAds} ads, will fetch actual durations and add more dynamically`
3028
- );
3029
- }
3030
- } else {
3031
- if (this.config.debugAdTiming) {
3032
- console.warn(
3033
- "[DEBUG-POD] \u26A0\uFE0F LIVE MODE: No duration available, defaulting to 1 ad"
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
- } else {
3038
- this.isAdaptiveMode = false;
3039
- this.targetAdBreakDurationMs = null;
3040
- this.fetchedAdDurations.clear();
3041
- if (this.apiNumberAds && this.apiNumberAds > 1) {
3042
- numberOfAds = this.apiNumberAds;
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
- } else {
3061
- vastTagUrls = [this.apiVastTagUrl];
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
- } else if (tags && tags.length > 0) {
3064
- vastTagUrls = tags;
3065
- } else {
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 (vastTagUrls.length > 0) {
3069
- this.adPodAllUrls = [...vastTagUrls];
3070
- this.preloadingAdUrls.clear();
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("[ADAPTIVE-POD] \u{1F680} Preloading first 2 ads before starting playback...");
3176
+ console.log("[CONTINUOUS-FETCH] \u{1F3AC} Playing next queued ad");
3085
3177
  }
3086
- try {
3087
- const adsToPreloadBeforeStart = Math.min(2, this.adPodAllUrls.length);
3088
- for (let i = 0; i < adsToPreloadBeforeStart; i++) {
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
- await this.playAdPod();
3182
+ return;
3127
3183
  }
3128
3184
  }
3129
- if (this.expectedAdBreakDurationMs == null && (scheduled == null ? void 0 : scheduled.durationMs) != null) {
3130
- this.expectedAdBreakDurationMs = scheduled.durationMs;
3131
- this.currentAdBreakStartWallClockMs = (_a = this.currentAdBreakStartWallClockMs) != null ? _a : Date.now();
3132
- this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
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 playAdPod() {
3136
- if (this.adPodQueue.length === 0) {
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
- const waitTime = this.isAdaptiveMode ? 50 : 500;
3140
- await new Promise((resolve) => setTimeout(resolve, waitTime));
3141
- const firstPreloaded = this.findNextPreloadedAd();
3142
- if (!firstPreloaded) {
3143
- const firstAd = this.adPodQueue.shift();
3144
- if (firstAd) {
3145
- this.currentAdIndex++;
3146
- try {
3147
- await this.playSingleAd(firstAd);
3148
- } catch (error) {
3149
- return;
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.currentAdIndex++;
3155
- try {
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 <= 500 || this.adPodQueue.length === 0) {
3379
- console.log("[ADAPTIVE-POD] \u23F9\uFE0F No more time or ads available");
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.log(
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