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.d.cts CHANGED
@@ -148,6 +148,8 @@ declare class StormcloudVideoPlayer {
148
148
  private maxPlaceholderDurationMs;
149
149
  private placeholderStartTimeMs;
150
150
  private isShowingPlaceholder;
151
+ private timeUpdateHandler?;
152
+ private emptiedHandler?;
151
153
  constructor(config: StormcloudVideoPlayerConfig);
152
154
  private createAdPlayer;
153
155
  load(): Promise<void>;
package/lib/index.d.ts CHANGED
@@ -148,6 +148,8 @@ declare class StormcloudVideoPlayer {
148
148
  private maxPlaceholderDurationMs;
149
149
  private placeholderStartTimeMs;
150
150
  private isShowingPlaceholder;
151
+ private timeUpdateHandler?;
152
+ private emptiedHandler?;
151
153
  constructor(config: StormcloudVideoPlayerConfig);
152
154
  private createAdPlayer;
153
155
  load(): Promise<void>;
package/lib/index.js CHANGED
@@ -920,6 +920,8 @@ function createHlsAdPlayer(contentVideo, options) {
920
920
  let sessionId;
921
921
  const preloadedAds = /* @__PURE__ */ new Map();
922
922
  const preloadingAds = /* @__PURE__ */ new Map();
923
+ let destroyed = false;
924
+ let pendingTimeouts = [];
923
925
  let trackingFired = {
924
926
  impression: false,
925
927
  start: false,
@@ -1248,13 +1250,27 @@ function createHlsAdPlayer(contentVideo, options) {
1248
1250
  adPlaying = false;
1249
1251
  setAdPlayingFlag(false);
1250
1252
  emit("content_resume");
1251
- setTimeout(() => {
1252
- if (!adPlaying && adContainerEl) {
1253
- adContainerEl.style.display = "none";
1254
- adContainerEl.style.pointerEvents = "none";
1255
- console.log("[HlsAdPlayer] Ad container hidden after completion (waiting for next ad)");
1253
+ const timeoutId = window.setTimeout(() => {
1254
+ if (destroyed) {
1255
+ console.log("[HlsAdPlayer] Player destroyed, skipping post-completion check");
1256
+ return;
1257
+ }
1258
+ const stillInPod = contentVideo.dataset.stormcloudAdPlaying === "true";
1259
+ if (stillInPod) {
1260
+ console.log(
1261
+ "[HlsAdPlayer] Still in ad pod - keeping ad container visible (black screen)"
1262
+ );
1263
+ if (adContainerEl) {
1264
+ adContainerEl.style.display = "flex";
1265
+ adContainerEl.style.pointerEvents = "auto";
1266
+ }
1267
+ }
1268
+ const idx = pendingTimeouts.indexOf(timeoutId);
1269
+ if (idx !== -1) {
1270
+ pendingTimeouts.splice(idx, 1);
1256
1271
  }
1257
1272
  }, 50);
1273
+ pendingTimeouts.push(timeoutId);
1258
1274
  }
1259
1275
  function handleAdError() {
1260
1276
  console.log("[HlsAdPlayer] Handling ad error");
@@ -1484,6 +1500,11 @@ function createHlsAdPlayer(contentVideo, options) {
1484
1500
  },
1485
1501
  destroy() {
1486
1502
  console.log("[HlsAdPlayer] Destroying");
1503
+ destroyed = true;
1504
+ for (const timeoutId of pendingTimeouts) {
1505
+ clearTimeout(timeoutId);
1506
+ }
1507
+ pendingTimeouts = [];
1487
1508
  adPlaying = false;
1488
1509
  setAdPlayingFlag(false);
1489
1510
  contentVideo.muted = originalMutedState;
@@ -2396,10 +2417,11 @@ var StormcloudVideoPlayer = class {
2396
2417
  this.handleAdPodComplete();
2397
2418
  }
2398
2419
  });
2399
- this.video.addEventListener("timeupdate", () => {
2420
+ this.timeUpdateHandler = () => {
2400
2421
  this.onTimeUpdate(this.video.currentTime);
2401
- });
2402
- this.video.addEventListener("emptied", () => {
2422
+ };
2423
+ this.video.addEventListener("timeupdate", this.timeUpdateHandler);
2424
+ this.emptiedHandler = () => {
2403
2425
  if (this.nativeHlsMode && this.videoSrcProtection && !this.ima.isAdPlaying()) {
2404
2426
  if (this.config.debugAdTiming) {
2405
2427
  console.log(
@@ -2416,7 +2438,8 @@ var StormcloudVideoPlayer = class {
2416
2438
  });
2417
2439
  }
2418
2440
  }
2419
- });
2441
+ };
2442
+ this.video.addEventListener("emptied", this.emptiedHandler);
2420
2443
  }
2421
2444
  shouldUseNativeHls() {
2422
2445
  const streamType = this.getStreamType();
@@ -3108,7 +3131,7 @@ var StormcloudVideoPlayer = class {
3108
3131
  continue;
3109
3132
  }
3110
3133
  if (this.config.debugAdTiming) {
3111
- 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)...`);
3134
+ console.log(`[CONTINUOUS-FETCH] \u{1F4E1} Attempting to fetch ad (${this.successfulAdRequests.length + this.adRequestQueue.length + 1} total)...`);
3112
3135
  }
3113
3136
  try {
3114
3137
  const response = await fetch(newAdUrl, { mode: "cors" });
@@ -3124,8 +3147,7 @@ var StormcloudVideoPlayer = class {
3124
3147
  console.log("[CONTINUOUS-FETCH] \u26A0\uFE0F VAST response has no media files, skipping");
3125
3148
  }
3126
3149
  this.failedVastUrls.add(newAdUrl);
3127
- const retryDelay = this.adRequestQueue.length > 0 ? 1e3 : 500;
3128
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
3150
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
3129
3151
  continue;
3130
3152
  }
3131
3153
  if (this.config.debugAdTiming) {
@@ -3133,19 +3155,17 @@ var StormcloudVideoPlayer = class {
3133
3155
  }
3134
3156
  this.adRequestQueue.push(newAdUrl);
3135
3157
  this.totalAdsInBreak++;
3136
- const successDelay = this.adRequestQueue.length <= 1 ? 300 : 500;
3137
- await new Promise((resolve) => setTimeout(resolve, successDelay));
3158
+ await new Promise((resolve) => setTimeout(resolve, 500));
3138
3159
  } catch (error) {
3139
3160
  if (this.config.debugAdTiming) {
3140
3161
  console.log("[CONTINUOUS-FETCH] \u274C Ad fetch failed:", error.message);
3141
3162
  }
3142
3163
  this.failedVastUrls.add(newAdUrl);
3143
- const retryDelay = this.adRequestQueue.length > 0 ? 2e3 : 1e3;
3144
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
3164
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
3145
3165
  }
3146
3166
  }
3147
3167
  if (this.config.debugAdTiming) {
3148
- console.log(`[CONTINUOUS-FETCH] \u{1F6D1} Continuous fetch loop ended (fetched ${this.successfulAdRequests.length} ads, ${this.adRequestQueue.length} in queue)`);
3168
+ console.log("[CONTINUOUS-FETCH] \u{1F6D1} Continuous fetch loop ended");
3149
3169
  }
3150
3170
  }
3151
3171
  stopContinuousFetching() {
@@ -3183,27 +3203,20 @@ var StormcloudVideoPlayer = class {
3183
3203
  return;
3184
3204
  }
3185
3205
  }
3186
- const hasPlayedAtLeastOneAd = this.successfulAdRequests.length > 0;
3187
- const maxRetries = hasPlayedAtLeastOneAd ? 10 : 5;
3188
- const retryDelayMs = hasPlayedAtLeastOneAd ? 500 : 1e3;
3189
- const minRemainingForRetry = 1e3;
3190
- if (this.continuousFetchingActive && retryCount < maxRetries && remaining > minRemainingForRetry) {
3206
+ const maxRetries = 5;
3207
+ if (this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3) {
3191
3208
  if (this.config.debugAdTiming) {
3192
- console.log(`[CONTINUOUS-FETCH] \u23F3 Queue empty but fetching active, waiting ${retryDelayMs}ms... (retry ${retryCount + 1}/${maxRetries}, remaining: ${remaining}ms)`);
3209
+ console.log(`[CONTINUOUS-FETCH] \u23F3 Queue empty but fetching active, waiting... (retry ${retryCount + 1}/${maxRetries})`);
3193
3210
  }
3194
- await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
3211
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
3195
3212
  await this.tryNextAvailableAd(retryCount + 1);
3196
3213
  return;
3197
3214
  }
3198
- const minTimeForPlaceholder = 3e3;
3199
- if (!this.isShowingPlaceholder && remaining >= minTimeForPlaceholder && this.continuousFetchingActive) {
3200
- if (this.config.debugAdTiming) {
3201
- console.log(`[CONTINUOUS-FETCH] \u2B1B Last resort: showing placeholder (${remaining}ms remaining)`);
3202
- }
3215
+ if (!this.isShowingPlaceholder && remaining > 1e3) {
3203
3216
  this.showPlaceholderAndWaitForAds();
3204
3217
  } else {
3205
3218
  if (this.config.debugAdTiming) {
3206
- console.log(`[CONTINUOUS-FETCH] \u23F9\uFE0F No more ads available or insufficient time remaining (${remaining}ms), ending ad break`);
3219
+ console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F No more ads available, ending ad break");
3207
3220
  }
3208
3221
  this.handleAdPodComplete();
3209
3222
  }
@@ -3211,36 +3224,26 @@ var StormcloudVideoPlayer = class {
3211
3224
  async showPlaceholderAndWaitForAds() {
3212
3225
  const remaining = this.getRemainingAdMs();
3213
3226
  const waitTime = Math.min(this.maxPlaceholderDurationMs, remaining);
3214
- if (waitTime < 3e3) {
3215
- if (this.config.debugAdTiming) {
3216
- console.log(`[CONTINUOUS-FETCH] \u23F9\uFE0F Insufficient time for placeholder (${waitTime}ms), ending ad break`);
3217
- }
3227
+ if (waitTime < 1e3) {
3218
3228
  this.handleAdPodComplete();
3219
3229
  return;
3220
3230
  }
3221
3231
  if (this.config.debugAdTiming) {
3222
- console.log(`[CONTINUOUS-FETCH] \u2B1B Showing black placeholder for up to ${waitTime}ms while waiting for ads (last resort)`);
3232
+ console.log(`[CONTINUOUS-FETCH] \u2B1B Showing black placeholder for ${waitTime}ms while waiting for ads`);
3223
3233
  }
3224
3234
  this.isShowingPlaceholder = true;
3225
3235
  this.placeholderStartTimeMs = Date.now();
3226
3236
  this.ima.showPlaceholder();
3227
- const checkInterval = 300;
3237
+ const checkInterval = 500;
3228
3238
  const maxChecks = Math.floor(waitTime / checkInterval);
3229
3239
  for (let i = 0; i < maxChecks; i++) {
3230
3240
  await new Promise((resolve) => setTimeout(resolve, checkInterval));
3231
3241
  if (!this.inAdBreak) {
3232
- if (this.config.debugAdTiming) {
3233
- console.log("[CONTINUOUS-FETCH] \u2139\uFE0F Ad break ended during placeholder wait");
3234
- }
3235
- this.isShowingPlaceholder = false;
3236
- this.placeholderStartTimeMs = null;
3237
- this.ima.hidePlaceholder();
3238
3242
  return;
3239
3243
  }
3240
3244
  if (this.adRequestQueue.length > 0) {
3241
- const elapsedInPlaceholder = Date.now() - (this.placeholderStartTimeMs || Date.now());
3242
3245
  if (this.config.debugAdTiming) {
3243
- console.log(`[CONTINUOUS-FETCH] \u2705 New ad became available during placeholder (after ${elapsedInPlaceholder}ms)`);
3246
+ console.log("[CONTINUOUS-FETCH] \u2705 New ad became available during placeholder");
3244
3247
  }
3245
3248
  this.isShowingPlaceholder = false;
3246
3249
  this.placeholderStartTimeMs = null;
@@ -3259,9 +3262,8 @@ var StormcloudVideoPlayer = class {
3259
3262
  return;
3260
3263
  }
3261
3264
  }
3262
- const totalPlaceholderTime = Date.now() - (this.placeholderStartTimeMs || Date.now());
3263
3265
  if (this.config.debugAdTiming) {
3264
- console.log(`[CONTINUOUS-FETCH] \u23F0 Placeholder timeout reached after ${totalPlaceholderTime}ms, no ads fetched`);
3266
+ console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout reached, no ads fetched");
3265
3267
  }
3266
3268
  this.isShowingPlaceholder = false;
3267
3269
  this.placeholderStartTimeMs = null;
@@ -4073,6 +4075,15 @@ var StormcloudVideoPlayer = class {
4073
4075
  this.clearAdStartTimer();
4074
4076
  this.clearAdStopTimer();
4075
4077
  this.clearAdFailsafeTimer();
4078
+ this.clearAdRequestWatchdog();
4079
+ if (this.timeUpdateHandler) {
4080
+ this.video.removeEventListener("timeupdate", this.timeUpdateHandler);
4081
+ delete this.timeUpdateHandler;
4082
+ }
4083
+ if (this.emptiedHandler) {
4084
+ this.video.removeEventListener("emptied", this.emptiedHandler);
4085
+ delete this.emptiedHandler;
4086
+ }
4076
4087
  if (this.heartbeatInterval) {
4077
4088
  clearInterval(this.heartbeatInterval);
4078
4089
  this.heartbeatInterval = void 0;