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.
@@ -47,6 +47,8 @@ declare class StormcloudVideoPlayer {
47
47
  private maxPlaceholderDurationMs;
48
48
  private placeholderStartTimeMs;
49
49
  private isShowingPlaceholder;
50
+ private timeUpdateHandler?;
51
+ private emptiedHandler?;
50
52
  constructor(config: StormcloudVideoPlayerConfig);
51
53
  private createAdPlayer;
52
54
  load(): Promise<void>;
@@ -925,6 +925,8 @@ function createHlsAdPlayer(contentVideo, options) {
925
925
  let sessionId;
926
926
  const preloadedAds = /* @__PURE__ */ new Map();
927
927
  const preloadingAds = /* @__PURE__ */ new Map();
928
+ let destroyed = false;
929
+ let pendingTimeouts = [];
928
930
  let trackingFired = {
929
931
  impression: false,
930
932
  start: false,
@@ -1253,13 +1255,27 @@ function createHlsAdPlayer(contentVideo, options) {
1253
1255
  adPlaying = false;
1254
1256
  setAdPlayingFlag(false);
1255
1257
  emit("content_resume");
1256
- setTimeout(() => {
1257
- if (!adPlaying && adContainerEl) {
1258
- adContainerEl.style.display = "none";
1259
- adContainerEl.style.pointerEvents = "none";
1260
- console.log("[HlsAdPlayer] Ad container hidden after completion (waiting for next ad)");
1258
+ const timeoutId = window.setTimeout(() => {
1259
+ if (destroyed) {
1260
+ console.log("[HlsAdPlayer] Player destroyed, skipping post-completion check");
1261
+ return;
1262
+ }
1263
+ const stillInPod = contentVideo.dataset.stormcloudAdPlaying === "true";
1264
+ if (stillInPod) {
1265
+ console.log(
1266
+ "[HlsAdPlayer] Still in ad pod - keeping ad container visible (black screen)"
1267
+ );
1268
+ if (adContainerEl) {
1269
+ adContainerEl.style.display = "flex";
1270
+ adContainerEl.style.pointerEvents = "auto";
1271
+ }
1272
+ }
1273
+ const idx = pendingTimeouts.indexOf(timeoutId);
1274
+ if (idx !== -1) {
1275
+ pendingTimeouts.splice(idx, 1);
1261
1276
  }
1262
1277
  }, 50);
1278
+ pendingTimeouts.push(timeoutId);
1263
1279
  }
1264
1280
  function handleAdError() {
1265
1281
  console.log("[HlsAdPlayer] Handling ad error");
@@ -1489,6 +1505,11 @@ function createHlsAdPlayer(contentVideo, options) {
1489
1505
  },
1490
1506
  destroy() {
1491
1507
  console.log("[HlsAdPlayer] Destroying");
1508
+ destroyed = true;
1509
+ for (const timeoutId of pendingTimeouts) {
1510
+ clearTimeout(timeoutId);
1511
+ }
1512
+ pendingTimeouts = [];
1492
1513
  adPlaying = false;
1493
1514
  setAdPlayingFlag(false);
1494
1515
  contentVideo.muted = originalMutedState;
@@ -2401,10 +2422,11 @@ var StormcloudVideoPlayer = class {
2401
2422
  this.handleAdPodComplete();
2402
2423
  }
2403
2424
  });
2404
- this.video.addEventListener("timeupdate", () => {
2425
+ this.timeUpdateHandler = () => {
2405
2426
  this.onTimeUpdate(this.video.currentTime);
2406
- });
2407
- this.video.addEventListener("emptied", () => {
2427
+ };
2428
+ this.video.addEventListener("timeupdate", this.timeUpdateHandler);
2429
+ this.emptiedHandler = () => {
2408
2430
  if (this.nativeHlsMode && this.videoSrcProtection && !this.ima.isAdPlaying()) {
2409
2431
  if (this.config.debugAdTiming) {
2410
2432
  console.log(
@@ -2421,7 +2443,8 @@ var StormcloudVideoPlayer = class {
2421
2443
  });
2422
2444
  }
2423
2445
  }
2424
- });
2446
+ };
2447
+ this.video.addEventListener("emptied", this.emptiedHandler);
2425
2448
  }
2426
2449
  shouldUseNativeHls() {
2427
2450
  const streamType = this.getStreamType();
@@ -3113,7 +3136,7 @@ var StormcloudVideoPlayer = class {
3113
3136
  continue;
3114
3137
  }
3115
3138
  if (this.config.debugAdTiming) {
3116
- 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)...`);
3139
+ console.log(`[CONTINUOUS-FETCH] \u{1F4E1} Attempting to fetch ad (${this.successfulAdRequests.length + this.adRequestQueue.length + 1} total)...`);
3117
3140
  }
3118
3141
  try {
3119
3142
  const response = await fetch(newAdUrl, { mode: "cors" });
@@ -3129,8 +3152,7 @@ var StormcloudVideoPlayer = class {
3129
3152
  console.log("[CONTINUOUS-FETCH] \u26A0\uFE0F VAST response has no media files, skipping");
3130
3153
  }
3131
3154
  this.failedVastUrls.add(newAdUrl);
3132
- const retryDelay = this.adRequestQueue.length > 0 ? 1e3 : 500;
3133
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
3155
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
3134
3156
  continue;
3135
3157
  }
3136
3158
  if (this.config.debugAdTiming) {
@@ -3138,19 +3160,17 @@ var StormcloudVideoPlayer = class {
3138
3160
  }
3139
3161
  this.adRequestQueue.push(newAdUrl);
3140
3162
  this.totalAdsInBreak++;
3141
- const successDelay = this.adRequestQueue.length <= 1 ? 300 : 500;
3142
- await new Promise((resolve) => setTimeout(resolve, successDelay));
3163
+ await new Promise((resolve) => setTimeout(resolve, 500));
3143
3164
  } catch (error) {
3144
3165
  if (this.config.debugAdTiming) {
3145
3166
  console.log("[CONTINUOUS-FETCH] \u274C Ad fetch failed:", error.message);
3146
3167
  }
3147
3168
  this.failedVastUrls.add(newAdUrl);
3148
- const retryDelay = this.adRequestQueue.length > 0 ? 2e3 : 1e3;
3149
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
3169
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
3150
3170
  }
3151
3171
  }
3152
3172
  if (this.config.debugAdTiming) {
3153
- console.log(`[CONTINUOUS-FETCH] \u{1F6D1} Continuous fetch loop ended (fetched ${this.successfulAdRequests.length} ads, ${this.adRequestQueue.length} in queue)`);
3173
+ console.log("[CONTINUOUS-FETCH] \u{1F6D1} Continuous fetch loop ended");
3154
3174
  }
3155
3175
  }
3156
3176
  stopContinuousFetching() {
@@ -3188,27 +3208,20 @@ var StormcloudVideoPlayer = class {
3188
3208
  return;
3189
3209
  }
3190
3210
  }
3191
- const hasPlayedAtLeastOneAd = this.successfulAdRequests.length > 0;
3192
- const maxRetries = hasPlayedAtLeastOneAd ? 10 : 5;
3193
- const retryDelayMs = hasPlayedAtLeastOneAd ? 500 : 1e3;
3194
- const minRemainingForRetry = 1e3;
3195
- if (this.continuousFetchingActive && retryCount < maxRetries && remaining > minRemainingForRetry) {
3211
+ const maxRetries = 5;
3212
+ if (this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3) {
3196
3213
  if (this.config.debugAdTiming) {
3197
- console.log(`[CONTINUOUS-FETCH] \u23F3 Queue empty but fetching active, waiting ${retryDelayMs}ms... (retry ${retryCount + 1}/${maxRetries}, remaining: ${remaining}ms)`);
3214
+ console.log(`[CONTINUOUS-FETCH] \u23F3 Queue empty but fetching active, waiting... (retry ${retryCount + 1}/${maxRetries})`);
3198
3215
  }
3199
- await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
3216
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
3200
3217
  await this.tryNextAvailableAd(retryCount + 1);
3201
3218
  return;
3202
3219
  }
3203
- const minTimeForPlaceholder = 3e3;
3204
- if (!this.isShowingPlaceholder && remaining >= minTimeForPlaceholder && this.continuousFetchingActive) {
3205
- if (this.config.debugAdTiming) {
3206
- console.log(`[CONTINUOUS-FETCH] \u2B1B Last resort: showing placeholder (${remaining}ms remaining)`);
3207
- }
3220
+ if (!this.isShowingPlaceholder && remaining > 1e3) {
3208
3221
  this.showPlaceholderAndWaitForAds();
3209
3222
  } else {
3210
3223
  if (this.config.debugAdTiming) {
3211
- console.log(`[CONTINUOUS-FETCH] \u23F9\uFE0F No more ads available or insufficient time remaining (${remaining}ms), ending ad break`);
3224
+ console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F No more ads available, ending ad break");
3212
3225
  }
3213
3226
  this.handleAdPodComplete();
3214
3227
  }
@@ -3216,36 +3229,26 @@ var StormcloudVideoPlayer = class {
3216
3229
  async showPlaceholderAndWaitForAds() {
3217
3230
  const remaining = this.getRemainingAdMs();
3218
3231
  const waitTime = Math.min(this.maxPlaceholderDurationMs, remaining);
3219
- if (waitTime < 3e3) {
3220
- if (this.config.debugAdTiming) {
3221
- console.log(`[CONTINUOUS-FETCH] \u23F9\uFE0F Insufficient time for placeholder (${waitTime}ms), ending ad break`);
3222
- }
3232
+ if (waitTime < 1e3) {
3223
3233
  this.handleAdPodComplete();
3224
3234
  return;
3225
3235
  }
3226
3236
  if (this.config.debugAdTiming) {
3227
- console.log(`[CONTINUOUS-FETCH] \u2B1B Showing black placeholder for up to ${waitTime}ms while waiting for ads (last resort)`);
3237
+ console.log(`[CONTINUOUS-FETCH] \u2B1B Showing black placeholder for ${waitTime}ms while waiting for ads`);
3228
3238
  }
3229
3239
  this.isShowingPlaceholder = true;
3230
3240
  this.placeholderStartTimeMs = Date.now();
3231
3241
  this.ima.showPlaceholder();
3232
- const checkInterval = 300;
3242
+ const checkInterval = 500;
3233
3243
  const maxChecks = Math.floor(waitTime / checkInterval);
3234
3244
  for (let i = 0; i < maxChecks; i++) {
3235
3245
  await new Promise((resolve) => setTimeout(resolve, checkInterval));
3236
3246
  if (!this.inAdBreak) {
3237
- if (this.config.debugAdTiming) {
3238
- console.log("[CONTINUOUS-FETCH] \u2139\uFE0F Ad break ended during placeholder wait");
3239
- }
3240
- this.isShowingPlaceholder = false;
3241
- this.placeholderStartTimeMs = null;
3242
- this.ima.hidePlaceholder();
3243
3247
  return;
3244
3248
  }
3245
3249
  if (this.adRequestQueue.length > 0) {
3246
- const elapsedInPlaceholder = Date.now() - (this.placeholderStartTimeMs || Date.now());
3247
3250
  if (this.config.debugAdTiming) {
3248
- console.log(`[CONTINUOUS-FETCH] \u2705 New ad became available during placeholder (after ${elapsedInPlaceholder}ms)`);
3251
+ console.log("[CONTINUOUS-FETCH] \u2705 New ad became available during placeholder");
3249
3252
  }
3250
3253
  this.isShowingPlaceholder = false;
3251
3254
  this.placeholderStartTimeMs = null;
@@ -3264,9 +3267,8 @@ var StormcloudVideoPlayer = class {
3264
3267
  return;
3265
3268
  }
3266
3269
  }
3267
- const totalPlaceholderTime = Date.now() - (this.placeholderStartTimeMs || Date.now());
3268
3270
  if (this.config.debugAdTiming) {
3269
- console.log(`[CONTINUOUS-FETCH] \u23F0 Placeholder timeout reached after ${totalPlaceholderTime}ms, no ads fetched`);
3271
+ console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout reached, no ads fetched");
3270
3272
  }
3271
3273
  this.isShowingPlaceholder = false;
3272
3274
  this.placeholderStartTimeMs = null;
@@ -4078,6 +4080,15 @@ var StormcloudVideoPlayer = class {
4078
4080
  this.clearAdStartTimer();
4079
4081
  this.clearAdStopTimer();
4080
4082
  this.clearAdFailsafeTimer();
4083
+ this.clearAdRequestWatchdog();
4084
+ if (this.timeUpdateHandler) {
4085
+ this.video.removeEventListener("timeupdate", this.timeUpdateHandler);
4086
+ delete this.timeUpdateHandler;
4087
+ }
4088
+ if (this.emptiedHandler) {
4089
+ this.video.removeEventListener("emptied", this.emptiedHandler);
4090
+ delete this.emptiedHandler;
4091
+ }
4081
4092
  if (this.heartbeatInterval) {
4082
4093
  clearInterval(this.heartbeatInterval);
4083
4094
  this.heartbeatInterval = void 0;