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.
package/lib/index.cjs CHANGED
@@ -989,6 +989,8 @@ function createHlsAdPlayer(contentVideo, options) {
989
989
  let sessionId;
990
990
  const preloadedAds = /* @__PURE__ */ new Map();
991
991
  const preloadingAds = /* @__PURE__ */ new Map();
992
+ let destroyed = false;
993
+ let pendingTimeouts = [];
992
994
  let trackingFired = {
993
995
  impression: false,
994
996
  start: false,
@@ -1317,13 +1319,27 @@ function createHlsAdPlayer(contentVideo, options) {
1317
1319
  adPlaying = false;
1318
1320
  setAdPlayingFlag(false);
1319
1321
  emit("content_resume");
1320
- setTimeout(() => {
1321
- if (!adPlaying && adContainerEl) {
1322
- adContainerEl.style.display = "none";
1323
- adContainerEl.style.pointerEvents = "none";
1324
- console.log("[HlsAdPlayer] Ad container hidden after completion (waiting for next ad)");
1322
+ const timeoutId = window.setTimeout(() => {
1323
+ if (destroyed) {
1324
+ console.log("[HlsAdPlayer] Player destroyed, skipping post-completion check");
1325
+ return;
1326
+ }
1327
+ const stillInPod = contentVideo.dataset.stormcloudAdPlaying === "true";
1328
+ if (stillInPod) {
1329
+ console.log(
1330
+ "[HlsAdPlayer] Still in ad pod - keeping ad container visible (black screen)"
1331
+ );
1332
+ if (adContainerEl) {
1333
+ adContainerEl.style.display = "flex";
1334
+ adContainerEl.style.pointerEvents = "auto";
1335
+ }
1336
+ }
1337
+ const idx = pendingTimeouts.indexOf(timeoutId);
1338
+ if (idx !== -1) {
1339
+ pendingTimeouts.splice(idx, 1);
1325
1340
  }
1326
1341
  }, 50);
1342
+ pendingTimeouts.push(timeoutId);
1327
1343
  }
1328
1344
  function handleAdError() {
1329
1345
  console.log("[HlsAdPlayer] Handling ad error");
@@ -1553,6 +1569,11 @@ function createHlsAdPlayer(contentVideo, options) {
1553
1569
  },
1554
1570
  destroy() {
1555
1571
  console.log("[HlsAdPlayer] Destroying");
1572
+ destroyed = true;
1573
+ for (const timeoutId of pendingTimeouts) {
1574
+ clearTimeout(timeoutId);
1575
+ }
1576
+ pendingTimeouts = [];
1556
1577
  adPlaying = false;
1557
1578
  setAdPlayingFlag(false);
1558
1579
  contentVideo.muted = originalMutedState;
@@ -2465,10 +2486,11 @@ var StormcloudVideoPlayer = class {
2465
2486
  this.handleAdPodComplete();
2466
2487
  }
2467
2488
  });
2468
- this.video.addEventListener("timeupdate", () => {
2489
+ this.timeUpdateHandler = () => {
2469
2490
  this.onTimeUpdate(this.video.currentTime);
2470
- });
2471
- this.video.addEventListener("emptied", () => {
2491
+ };
2492
+ this.video.addEventListener("timeupdate", this.timeUpdateHandler);
2493
+ this.emptiedHandler = () => {
2472
2494
  if (this.nativeHlsMode && this.videoSrcProtection && !this.ima.isAdPlaying()) {
2473
2495
  if (this.config.debugAdTiming) {
2474
2496
  console.log(
@@ -2485,7 +2507,8 @@ var StormcloudVideoPlayer = class {
2485
2507
  });
2486
2508
  }
2487
2509
  }
2488
- });
2510
+ };
2511
+ this.video.addEventListener("emptied", this.emptiedHandler);
2489
2512
  }
2490
2513
  shouldUseNativeHls() {
2491
2514
  const streamType = this.getStreamType();
@@ -3177,7 +3200,7 @@ var StormcloudVideoPlayer = class {
3177
3200
  continue;
3178
3201
  }
3179
3202
  if (this.config.debugAdTiming) {
3180
- 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)...`);
3203
+ console.log(`[CONTINUOUS-FETCH] \u{1F4E1} Attempting to fetch ad (${this.successfulAdRequests.length + this.adRequestQueue.length + 1} total)...`);
3181
3204
  }
3182
3205
  try {
3183
3206
  const response = await fetch(newAdUrl, { mode: "cors" });
@@ -3193,8 +3216,7 @@ var StormcloudVideoPlayer = class {
3193
3216
  console.log("[CONTINUOUS-FETCH] \u26A0\uFE0F VAST response has no media files, skipping");
3194
3217
  }
3195
3218
  this.failedVastUrls.add(newAdUrl);
3196
- const retryDelay = this.adRequestQueue.length > 0 ? 1e3 : 500;
3197
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
3219
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
3198
3220
  continue;
3199
3221
  }
3200
3222
  if (this.config.debugAdTiming) {
@@ -3202,19 +3224,17 @@ var StormcloudVideoPlayer = class {
3202
3224
  }
3203
3225
  this.adRequestQueue.push(newAdUrl);
3204
3226
  this.totalAdsInBreak++;
3205
- const successDelay = this.adRequestQueue.length <= 1 ? 300 : 500;
3206
- await new Promise((resolve) => setTimeout(resolve, successDelay));
3227
+ await new Promise((resolve) => setTimeout(resolve, 500));
3207
3228
  } catch (error) {
3208
3229
  if (this.config.debugAdTiming) {
3209
3230
  console.log("[CONTINUOUS-FETCH] \u274C Ad fetch failed:", error.message);
3210
3231
  }
3211
3232
  this.failedVastUrls.add(newAdUrl);
3212
- const retryDelay = this.adRequestQueue.length > 0 ? 2e3 : 1e3;
3213
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
3233
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
3214
3234
  }
3215
3235
  }
3216
3236
  if (this.config.debugAdTiming) {
3217
- console.log(`[CONTINUOUS-FETCH] \u{1F6D1} Continuous fetch loop ended (fetched ${this.successfulAdRequests.length} ads, ${this.adRequestQueue.length} in queue)`);
3237
+ console.log("[CONTINUOUS-FETCH] \u{1F6D1} Continuous fetch loop ended");
3218
3238
  }
3219
3239
  }
3220
3240
  stopContinuousFetching() {
@@ -3252,27 +3272,20 @@ var StormcloudVideoPlayer = class {
3252
3272
  return;
3253
3273
  }
3254
3274
  }
3255
- const hasPlayedAtLeastOneAd = this.successfulAdRequests.length > 0;
3256
- const maxRetries = hasPlayedAtLeastOneAd ? 10 : 5;
3257
- const retryDelayMs = hasPlayedAtLeastOneAd ? 500 : 1e3;
3258
- const minRemainingForRetry = 1e3;
3259
- if (this.continuousFetchingActive && retryCount < maxRetries && remaining > minRemainingForRetry) {
3275
+ const maxRetries = 5;
3276
+ if (this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3) {
3260
3277
  if (this.config.debugAdTiming) {
3261
- console.log(`[CONTINUOUS-FETCH] \u23F3 Queue empty but fetching active, waiting ${retryDelayMs}ms... (retry ${retryCount + 1}/${maxRetries}, remaining: ${remaining}ms)`);
3278
+ console.log(`[CONTINUOUS-FETCH] \u23F3 Queue empty but fetching active, waiting... (retry ${retryCount + 1}/${maxRetries})`);
3262
3279
  }
3263
- await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
3280
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
3264
3281
  await this.tryNextAvailableAd(retryCount + 1);
3265
3282
  return;
3266
3283
  }
3267
- const minTimeForPlaceholder = 3e3;
3268
- if (!this.isShowingPlaceholder && remaining >= minTimeForPlaceholder && this.continuousFetchingActive) {
3269
- if (this.config.debugAdTiming) {
3270
- console.log(`[CONTINUOUS-FETCH] \u2B1B Last resort: showing placeholder (${remaining}ms remaining)`);
3271
- }
3284
+ if (!this.isShowingPlaceholder && remaining > 1e3) {
3272
3285
  this.showPlaceholderAndWaitForAds();
3273
3286
  } else {
3274
3287
  if (this.config.debugAdTiming) {
3275
- console.log(`[CONTINUOUS-FETCH] \u23F9\uFE0F No more ads available or insufficient time remaining (${remaining}ms), ending ad break`);
3288
+ console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F No more ads available, ending ad break");
3276
3289
  }
3277
3290
  this.handleAdPodComplete();
3278
3291
  }
@@ -3280,36 +3293,26 @@ var StormcloudVideoPlayer = class {
3280
3293
  async showPlaceholderAndWaitForAds() {
3281
3294
  const remaining = this.getRemainingAdMs();
3282
3295
  const waitTime = Math.min(this.maxPlaceholderDurationMs, remaining);
3283
- if (waitTime < 3e3) {
3284
- if (this.config.debugAdTiming) {
3285
- console.log(`[CONTINUOUS-FETCH] \u23F9\uFE0F Insufficient time for placeholder (${waitTime}ms), ending ad break`);
3286
- }
3296
+ if (waitTime < 1e3) {
3287
3297
  this.handleAdPodComplete();
3288
3298
  return;
3289
3299
  }
3290
3300
  if (this.config.debugAdTiming) {
3291
- console.log(`[CONTINUOUS-FETCH] \u2B1B Showing black placeholder for up to ${waitTime}ms while waiting for ads (last resort)`);
3301
+ console.log(`[CONTINUOUS-FETCH] \u2B1B Showing black placeholder for ${waitTime}ms while waiting for ads`);
3292
3302
  }
3293
3303
  this.isShowingPlaceholder = true;
3294
3304
  this.placeholderStartTimeMs = Date.now();
3295
3305
  this.ima.showPlaceholder();
3296
- const checkInterval = 300;
3306
+ const checkInterval = 500;
3297
3307
  const maxChecks = Math.floor(waitTime / checkInterval);
3298
3308
  for (let i = 0; i < maxChecks; i++) {
3299
3309
  await new Promise((resolve) => setTimeout(resolve, checkInterval));
3300
3310
  if (!this.inAdBreak) {
3301
- if (this.config.debugAdTiming) {
3302
- console.log("[CONTINUOUS-FETCH] \u2139\uFE0F Ad break ended during placeholder wait");
3303
- }
3304
- this.isShowingPlaceholder = false;
3305
- this.placeholderStartTimeMs = null;
3306
- this.ima.hidePlaceholder();
3307
3311
  return;
3308
3312
  }
3309
3313
  if (this.adRequestQueue.length > 0) {
3310
- const elapsedInPlaceholder = Date.now() - (this.placeholderStartTimeMs || Date.now());
3311
3314
  if (this.config.debugAdTiming) {
3312
- console.log(`[CONTINUOUS-FETCH] \u2705 New ad became available during placeholder (after ${elapsedInPlaceholder}ms)`);
3315
+ console.log("[CONTINUOUS-FETCH] \u2705 New ad became available during placeholder");
3313
3316
  }
3314
3317
  this.isShowingPlaceholder = false;
3315
3318
  this.placeholderStartTimeMs = null;
@@ -3328,9 +3331,8 @@ var StormcloudVideoPlayer = class {
3328
3331
  return;
3329
3332
  }
3330
3333
  }
3331
- const totalPlaceholderTime = Date.now() - (this.placeholderStartTimeMs || Date.now());
3332
3334
  if (this.config.debugAdTiming) {
3333
- console.log(`[CONTINUOUS-FETCH] \u23F0 Placeholder timeout reached after ${totalPlaceholderTime}ms, no ads fetched`);
3335
+ console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout reached, no ads fetched");
3334
3336
  }
3335
3337
  this.isShowingPlaceholder = false;
3336
3338
  this.placeholderStartTimeMs = null;
@@ -4142,6 +4144,15 @@ var StormcloudVideoPlayer = class {
4142
4144
  this.clearAdStartTimer();
4143
4145
  this.clearAdStopTimer();
4144
4146
  this.clearAdFailsafeTimer();
4147
+ this.clearAdRequestWatchdog();
4148
+ if (this.timeUpdateHandler) {
4149
+ this.video.removeEventListener("timeupdate", this.timeUpdateHandler);
4150
+ delete this.timeUpdateHandler;
4151
+ }
4152
+ if (this.emptiedHandler) {
4153
+ this.video.removeEventListener("emptied", this.emptiedHandler);
4154
+ delete this.emptiedHandler;
4155
+ }
4145
4156
  if (this.heartbeatInterval) {
4146
4157
  clearInterval(this.heartbeatInterval);
4147
4158
  this.heartbeatInterval = void 0;