stormcloud-video-player 0.3.13 → 0.3.14

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.
@@ -964,6 +964,8 @@ function createHlsAdPlayer(contentVideo, options) {
964
964
  let sessionId;
965
965
  const preloadedAds = /* @__PURE__ */ new Map();
966
966
  const preloadingAds = /* @__PURE__ */ new Map();
967
+ let destroyed = false;
968
+ let pendingTimeouts = [];
967
969
  let trackingFired = {
968
970
  impression: false,
969
971
  start: false,
@@ -1292,13 +1294,27 @@ function createHlsAdPlayer(contentVideo, options) {
1292
1294
  adPlaying = false;
1293
1295
  setAdPlayingFlag(false);
1294
1296
  emit("content_resume");
1295
- setTimeout(() => {
1296
- if (!adPlaying && adContainerEl) {
1297
- adContainerEl.style.display = "none";
1298
- adContainerEl.style.pointerEvents = "none";
1299
- console.log("[HlsAdPlayer] Ad container hidden after completion (waiting for next ad)");
1297
+ const timeoutId = window.setTimeout(() => {
1298
+ if (destroyed) {
1299
+ console.log("[HlsAdPlayer] Player destroyed, skipping post-completion check");
1300
+ return;
1301
+ }
1302
+ const stillInPod = contentVideo.dataset.stormcloudAdPlaying === "true";
1303
+ if (stillInPod) {
1304
+ console.log(
1305
+ "[HlsAdPlayer] Still in ad pod - keeping ad container visible (black screen)"
1306
+ );
1307
+ if (adContainerEl) {
1308
+ adContainerEl.style.display = "flex";
1309
+ adContainerEl.style.pointerEvents = "auto";
1310
+ }
1311
+ }
1312
+ const idx = pendingTimeouts.indexOf(timeoutId);
1313
+ if (idx !== -1) {
1314
+ pendingTimeouts.splice(idx, 1);
1300
1315
  }
1301
1316
  }, 50);
1317
+ pendingTimeouts.push(timeoutId);
1302
1318
  }
1303
1319
  function handleAdError() {
1304
1320
  console.log("[HlsAdPlayer] Handling ad error");
@@ -1528,6 +1544,11 @@ function createHlsAdPlayer(contentVideo, options) {
1528
1544
  },
1529
1545
  destroy() {
1530
1546
  console.log("[HlsAdPlayer] Destroying");
1547
+ destroyed = true;
1548
+ for (const timeoutId of pendingTimeouts) {
1549
+ clearTimeout(timeoutId);
1550
+ }
1551
+ pendingTimeouts = [];
1531
1552
  adPlaying = false;
1532
1553
  setAdPlayingFlag(false);
1533
1554
  contentVideo.muted = originalMutedState;
@@ -2440,10 +2461,11 @@ var StormcloudVideoPlayer = class {
2440
2461
  this.handleAdPodComplete();
2441
2462
  }
2442
2463
  });
2443
- this.video.addEventListener("timeupdate", () => {
2464
+ this.timeUpdateHandler = () => {
2444
2465
  this.onTimeUpdate(this.video.currentTime);
2445
- });
2446
- this.video.addEventListener("emptied", () => {
2466
+ };
2467
+ this.video.addEventListener("timeupdate", this.timeUpdateHandler);
2468
+ this.emptiedHandler = () => {
2447
2469
  if (this.nativeHlsMode && this.videoSrcProtection && !this.ima.isAdPlaying()) {
2448
2470
  if (this.config.debugAdTiming) {
2449
2471
  console.log(
@@ -2460,7 +2482,8 @@ var StormcloudVideoPlayer = class {
2460
2482
  });
2461
2483
  }
2462
2484
  }
2463
- });
2485
+ };
2486
+ this.video.addEventListener("emptied", this.emptiedHandler);
2464
2487
  }
2465
2488
  shouldUseNativeHls() {
2466
2489
  const streamType = this.getStreamType();
@@ -3152,7 +3175,7 @@ var StormcloudVideoPlayer = class {
3152
3175
  continue;
3153
3176
  }
3154
3177
  if (this.config.debugAdTiming) {
3155
- console.log(`[CONTINUOUS-FETCH] \u{1F4E1} Attempting to fetch ad #${this.successfulAdRequests.length + this.adRequestQueue.length + 1} (queue: ${this.adRequestQueue.length}, remaining: ${Math.round(remaining / 1e3)}s)...`);
3178
+ console.log(`[CONTINUOUS-FETCH] \u{1F4E1} Attempting to fetch ad (${this.successfulAdRequests.length + this.adRequestQueue.length + 1} total)...`);
3156
3179
  }
3157
3180
  try {
3158
3181
  const response = await fetch(newAdUrl, { mode: "cors" });
@@ -3168,8 +3191,7 @@ var StormcloudVideoPlayer = class {
3168
3191
  console.log("[CONTINUOUS-FETCH] \u26A0\uFE0F VAST response has no media files, skipping");
3169
3192
  }
3170
3193
  this.failedVastUrls.add(newAdUrl);
3171
- const retryDelay = this.adRequestQueue.length > 0 ? 1e3 : 500;
3172
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
3194
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
3173
3195
  continue;
3174
3196
  }
3175
3197
  if (this.config.debugAdTiming) {
@@ -3177,19 +3199,17 @@ var StormcloudVideoPlayer = class {
3177
3199
  }
3178
3200
  this.adRequestQueue.push(newAdUrl);
3179
3201
  this.totalAdsInBreak++;
3180
- const successDelay = this.adRequestQueue.length <= 1 ? 300 : 500;
3181
- await new Promise((resolve) => setTimeout(resolve, successDelay));
3202
+ await new Promise((resolve) => setTimeout(resolve, 500));
3182
3203
  } catch (error) {
3183
3204
  if (this.config.debugAdTiming) {
3184
3205
  console.log("[CONTINUOUS-FETCH] \u274C Ad fetch failed:", error.message);
3185
3206
  }
3186
3207
  this.failedVastUrls.add(newAdUrl);
3187
- const retryDelay = this.adRequestQueue.length > 0 ? 2e3 : 1e3;
3188
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
3208
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
3189
3209
  }
3190
3210
  }
3191
3211
  if (this.config.debugAdTiming) {
3192
- console.log(`[CONTINUOUS-FETCH] \u{1F6D1} Continuous fetch loop ended (fetched ${this.successfulAdRequests.length} ads, ${this.adRequestQueue.length} in queue)`);
3212
+ console.log("[CONTINUOUS-FETCH] \u{1F6D1} Continuous fetch loop ended");
3193
3213
  }
3194
3214
  }
3195
3215
  stopContinuousFetching() {
@@ -3227,27 +3247,20 @@ var StormcloudVideoPlayer = class {
3227
3247
  return;
3228
3248
  }
3229
3249
  }
3230
- const hasPlayedAtLeastOneAd = this.successfulAdRequests.length > 0;
3231
- const maxRetries = hasPlayedAtLeastOneAd ? 10 : 5;
3232
- const retryDelayMs = hasPlayedAtLeastOneAd ? 500 : 1e3;
3233
- const minRemainingForRetry = 1e3;
3234
- if (this.continuousFetchingActive && retryCount < maxRetries && remaining > minRemainingForRetry) {
3250
+ const maxRetries = 5;
3251
+ if (this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3) {
3235
3252
  if (this.config.debugAdTiming) {
3236
- console.log(`[CONTINUOUS-FETCH] \u23F3 Queue empty but fetching active, waiting ${retryDelayMs}ms... (retry ${retryCount + 1}/${maxRetries}, remaining: ${remaining}ms)`);
3253
+ console.log(`[CONTINUOUS-FETCH] \u23F3 Queue empty but fetching active, waiting... (retry ${retryCount + 1}/${maxRetries})`);
3237
3254
  }
3238
- await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
3255
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
3239
3256
  await this.tryNextAvailableAd(retryCount + 1);
3240
3257
  return;
3241
3258
  }
3242
- const minTimeForPlaceholder = 3e3;
3243
- if (!this.isShowingPlaceholder && remaining >= minTimeForPlaceholder && this.continuousFetchingActive) {
3244
- if (this.config.debugAdTiming) {
3245
- console.log(`[CONTINUOUS-FETCH] \u2B1B Last resort: showing placeholder (${remaining}ms remaining)`);
3246
- }
3259
+ if (!this.isShowingPlaceholder && remaining > 1e3) {
3247
3260
  this.showPlaceholderAndWaitForAds();
3248
3261
  } else {
3249
3262
  if (this.config.debugAdTiming) {
3250
- console.log(`[CONTINUOUS-FETCH] \u23F9\uFE0F No more ads available or insufficient time remaining (${remaining}ms), ending ad break`);
3263
+ console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F No more ads available, ending ad break");
3251
3264
  }
3252
3265
  this.handleAdPodComplete();
3253
3266
  }
@@ -3255,36 +3268,26 @@ var StormcloudVideoPlayer = class {
3255
3268
  async showPlaceholderAndWaitForAds() {
3256
3269
  const remaining = this.getRemainingAdMs();
3257
3270
  const waitTime = Math.min(this.maxPlaceholderDurationMs, remaining);
3258
- if (waitTime < 3e3) {
3259
- if (this.config.debugAdTiming) {
3260
- console.log(`[CONTINUOUS-FETCH] \u23F9\uFE0F Insufficient time for placeholder (${waitTime}ms), ending ad break`);
3261
- }
3271
+ if (waitTime < 1e3) {
3262
3272
  this.handleAdPodComplete();
3263
3273
  return;
3264
3274
  }
3265
3275
  if (this.config.debugAdTiming) {
3266
- console.log(`[CONTINUOUS-FETCH] \u2B1B Showing black placeholder for up to ${waitTime}ms while waiting for ads (last resort)`);
3276
+ console.log(`[CONTINUOUS-FETCH] \u2B1B Showing black placeholder for ${waitTime}ms while waiting for ads`);
3267
3277
  }
3268
3278
  this.isShowingPlaceholder = true;
3269
3279
  this.placeholderStartTimeMs = Date.now();
3270
3280
  this.ima.showPlaceholder();
3271
- const checkInterval = 300;
3281
+ const checkInterval = 500;
3272
3282
  const maxChecks = Math.floor(waitTime / checkInterval);
3273
3283
  for (let i = 0; i < maxChecks; i++) {
3274
3284
  await new Promise((resolve) => setTimeout(resolve, checkInterval));
3275
3285
  if (!this.inAdBreak) {
3276
- if (this.config.debugAdTiming) {
3277
- console.log("[CONTINUOUS-FETCH] \u2139\uFE0F Ad break ended during placeholder wait");
3278
- }
3279
- this.isShowingPlaceholder = false;
3280
- this.placeholderStartTimeMs = null;
3281
- this.ima.hidePlaceholder();
3282
3286
  return;
3283
3287
  }
3284
3288
  if (this.adRequestQueue.length > 0) {
3285
- const elapsedInPlaceholder = Date.now() - (this.placeholderStartTimeMs || Date.now());
3286
3289
  if (this.config.debugAdTiming) {
3287
- console.log(`[CONTINUOUS-FETCH] \u2705 New ad became available during placeholder (after ${elapsedInPlaceholder}ms)`);
3290
+ console.log("[CONTINUOUS-FETCH] \u2705 New ad became available during placeholder");
3288
3291
  }
3289
3292
  this.isShowingPlaceholder = false;
3290
3293
  this.placeholderStartTimeMs = null;
@@ -3303,9 +3306,8 @@ var StormcloudVideoPlayer = class {
3303
3306
  return;
3304
3307
  }
3305
3308
  }
3306
- const totalPlaceholderTime = Date.now() - (this.placeholderStartTimeMs || Date.now());
3307
3309
  if (this.config.debugAdTiming) {
3308
- console.log(`[CONTINUOUS-FETCH] \u23F0 Placeholder timeout reached after ${totalPlaceholderTime}ms, no ads fetched`);
3310
+ console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout reached, no ads fetched");
3309
3311
  }
3310
3312
  this.isShowingPlaceholder = false;
3311
3313
  this.placeholderStartTimeMs = null;
@@ -4117,6 +4119,15 @@ var StormcloudVideoPlayer = class {
4117
4119
  this.clearAdStartTimer();
4118
4120
  this.clearAdStopTimer();
4119
4121
  this.clearAdFailsafeTimer();
4122
+ this.clearAdRequestWatchdog();
4123
+ if (this.timeUpdateHandler) {
4124
+ this.video.removeEventListener("timeupdate", this.timeUpdateHandler);
4125
+ delete this.timeUpdateHandler;
4126
+ }
4127
+ if (this.emptiedHandler) {
4128
+ this.video.removeEventListener("emptied", this.emptiedHandler);
4129
+ delete this.emptiedHandler;
4130
+ }
4120
4131
  if (this.heartbeatInterval) {
4121
4132
  clearInterval(this.heartbeatInterval);
4122
4133
  this.heartbeatInterval = void 0;