stormcloud-video-player 0.2.18 → 0.2.20

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
@@ -24,6 +24,8 @@ interface StormcloudVideoPlayerConfig {
24
24
  immediateManifestAds?: boolean;
25
25
  debugAdTiming?: boolean;
26
26
  adFailsafeTimeoutMs?: number;
27
+ adBreakCheckIntervalMs?: number;
28
+ maxAdBreakExtensionMs?: number;
27
29
  showCustomControls?: boolean;
28
30
  onVolumeToggle?: () => void;
29
31
  onFullscreenToggle?: () => void;
@@ -120,6 +122,7 @@ declare class StormcloudVideoPlayer {
120
122
  private bufferedSegmentsCount;
121
123
  private shouldAutoplayAfterBuffering;
122
124
  private hasInitialBufferCompleted;
125
+ private isTransitioningBetweenAds;
123
126
  constructor(config: StormcloudVideoPlayerConfig);
124
127
  private createAdPlayer;
125
128
  load(): Promise<void>;
package/lib/index.d.ts CHANGED
@@ -24,6 +24,8 @@ interface StormcloudVideoPlayerConfig {
24
24
  immediateManifestAds?: boolean;
25
25
  debugAdTiming?: boolean;
26
26
  adFailsafeTimeoutMs?: number;
27
+ adBreakCheckIntervalMs?: number;
28
+ maxAdBreakExtensionMs?: number;
27
29
  showCustomControls?: boolean;
28
30
  onVolumeToggle?: () => void;
29
31
  onFullscreenToggle?: () => void;
@@ -120,6 +122,7 @@ declare class StormcloudVideoPlayer {
120
122
  private bufferedSegmentsCount;
121
123
  private shouldAutoplayAfterBuffering;
122
124
  private hasInitialBufferCompleted;
125
+ private isTransitioningBetweenAds;
123
126
  constructor(config: StormcloudVideoPlayerConfig);
124
127
  private createAdPlayer;
125
128
  load(): Promise<void>;
package/lib/index.js CHANGED
@@ -523,28 +523,42 @@ function createImaController(video, options) {
523
523
  adsManager.addEventListener(
524
524
  AdEvent.CONTENT_RESUME_REQUESTED,
525
525
  () => {
526
- var _a;
527
526
  console.log("[IMA] Content resume requested");
528
527
  adPlaying = false;
529
- video.muted = originalMutedState;
530
528
  setAdPlayingFlag(false);
531
- if (adContainerEl) {
532
- adContainerEl.style.pointerEvents = "none";
533
- adContainerEl.style.display = "none";
534
- console.log(
535
- "[IMA] Ad container hidden - pointer events disabled"
536
- );
537
- }
538
- if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
539
- (_a = video.play()) == null ? void 0 : _a.catch(() => {
540
- });
541
- console.log("[IMA] Video resumed (VOD mode)");
542
- } else {
543
- console.log(
544
- "[IMA] Video unmuted (Live mode - was never paused)"
545
- );
546
- }
547
529
  emit("content_resume");
530
+ setTimeout(() => {
531
+ var _a;
532
+ const stillInAdPod = video.dataset.stormcloudAdPlaying === "true";
533
+ if (stillInAdPod) {
534
+ console.log(
535
+ "[IMA] Next ad started - keeping content muted/paused and ad container visible"
536
+ );
537
+ if (adContainerEl) {
538
+ adContainerEl.style.display = "flex";
539
+ adContainerEl.style.pointerEvents = "auto";
540
+ }
541
+ return;
542
+ }
543
+ console.log("[IMA] No next ad - resuming content");
544
+ video.muted = originalMutedState;
545
+ if (adContainerEl) {
546
+ adContainerEl.style.pointerEvents = "none";
547
+ adContainerEl.style.display = "none";
548
+ console.log(
549
+ "[IMA] Ad container hidden - pointer events disabled"
550
+ );
551
+ }
552
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
553
+ (_a = video.play()) == null ? void 0 : _a.catch(() => {
554
+ });
555
+ console.log("[IMA] Video resumed (VOD mode)");
556
+ } else {
557
+ console.log(
558
+ "[IMA] Video unmuted (Live mode - was never paused)"
559
+ );
560
+ }
561
+ }, 100);
548
562
  }
549
563
  );
550
564
  adsManager.addEventListener(AdEvent.ALL_ADS_COMPLETED, () => {
@@ -1119,24 +1133,38 @@ function createHlsAdPlayer(contentVideo, options) {
1119
1133
  console.log("[HlsAdPlayer] Handling ad completion");
1120
1134
  adPlaying = false;
1121
1135
  setAdPlayingFlag(false);
1122
- const previousMutedState = contentVideo.muted;
1123
- contentVideo.muted = originalMutedState;
1124
- console.log(
1125
- `[HlsAdPlayer] Restored mute state: ${previousMutedState} -> ${originalMutedState}`
1126
- );
1127
- if (adContainerEl) {
1128
- adContainerEl.style.display = "none";
1129
- adContainerEl.style.pointerEvents = "none";
1130
- }
1131
- if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
1132
- contentVideo.play().catch(() => {
1133
- });
1134
- console.log("[HlsAdPlayer] Content resumed (VOD mode)");
1135
- } else {
1136
- console.log("[HlsAdPlayer] Content unmuted (Live mode)");
1137
- }
1138
1136
  emit("content_resume");
1139
1137
  emit("all_ads_completed");
1138
+ setTimeout(() => {
1139
+ const stillInAdPod = contentVideo.dataset.stormcloudAdPlaying === "true";
1140
+ if (stillInAdPod) {
1141
+ console.log(
1142
+ "[HlsAdPlayer] Next ad started - keeping content muted/paused and ad container visible"
1143
+ );
1144
+ if (adContainerEl) {
1145
+ adContainerEl.style.display = "flex";
1146
+ adContainerEl.style.pointerEvents = "auto";
1147
+ }
1148
+ return;
1149
+ }
1150
+ console.log("[HlsAdPlayer] No next ad - resuming content");
1151
+ const previousMutedState = contentVideo.muted;
1152
+ contentVideo.muted = originalMutedState;
1153
+ console.log(
1154
+ `[HlsAdPlayer] Restored mute state: ${previousMutedState} -> ${originalMutedState}`
1155
+ );
1156
+ if (adContainerEl) {
1157
+ adContainerEl.style.display = "none";
1158
+ adContainerEl.style.pointerEvents = "none";
1159
+ }
1160
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
1161
+ contentVideo.play().catch(() => {
1162
+ });
1163
+ console.log("[HlsAdPlayer] Content resumed (VOD mode)");
1164
+ } else {
1165
+ console.log("[HlsAdPlayer] Content unmuted (Live mode)");
1166
+ }
1167
+ }, 100);
1140
1168
  }
1141
1169
  function handleAdError() {
1142
1170
  console.log("[HlsAdPlayer] Handling ad error");
@@ -1874,6 +1902,7 @@ var StormcloudVideoPlayer = class {
1874
1902
  this.bufferedSegmentsCount = 0;
1875
1903
  this.shouldAutoplayAfterBuffering = false;
1876
1904
  this.hasInitialBufferCompleted = false;
1905
+ this.isTransitioningBetweenAds = false;
1877
1906
  initializePolyfills();
1878
1907
  const browserOverrides = getBrowserConfigOverrides();
1879
1908
  this.config = { ...config, ...browserOverrides };
@@ -2175,29 +2204,60 @@ var StormcloudVideoPlayer = class {
2175
2204
  this.ima.on("content_resume", () => {
2176
2205
  if (this.config.debugAdTiming) {
2177
2206
  console.log(
2178
- "[StormcloudVideoPlayer] IMA content_resume event received"
2207
+ "[StormcloudVideoPlayer] IMA content_resume event received",
2208
+ {
2209
+ inAdBreak: this.inAdBreak,
2210
+ isTransitioningBetweenAds: this.isTransitioningBetweenAds,
2211
+ pendingAds: this.adPodQueue.length
2212
+ }
2179
2213
  );
2180
2214
  }
2181
2215
  this.clearAdFailsafeTimer();
2182
- if (!this.inAdBreak) return;
2216
+ if (this.isTransitioningBetweenAds) {
2217
+ if (this.config.debugAdTiming) {
2218
+ console.log(
2219
+ "[StormcloudVideoPlayer] Transitioning between ads - keeping content muted/paused"
2220
+ );
2221
+ }
2222
+ return;
2223
+ }
2224
+ if (!this.inAdBreak) {
2225
+ if (this.config.debugAdTiming) {
2226
+ console.log(
2227
+ "[StormcloudVideoPlayer] Not in ad break, allowing normal content resume"
2228
+ );
2229
+ }
2230
+ return;
2231
+ }
2183
2232
  const remaining = this.getRemainingAdMs();
2184
2233
  if (remaining > 500 && this.adPodQueue.length > 0) {
2234
+ this.isTransitioningBetweenAds = true;
2235
+ this.video.muted = true;
2236
+ if (!this.shouldContinueLiveStreamDuringAds()) {
2237
+ this.video.pause();
2238
+ }
2185
2239
  const next = this.adPodQueue.shift();
2186
2240
  this.currentAdIndex++;
2187
2241
  if (this.config.debugAdTiming) {
2188
2242
  console.log(
2189
- `[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak})`
2243
+ `[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak}) - keeping content muted/paused`
2190
2244
  );
2191
2245
  }
2192
2246
  this.playSingleAd(next).catch(() => {
2247
+ }).finally(() => {
2248
+ this.isTransitioningBetweenAds = false;
2193
2249
  });
2194
2250
  } else {
2195
2251
  if (this.config.debugAdTiming) {
2196
2252
  console.log("[StormcloudVideoPlayer] Ad pod completed");
2197
2253
  }
2254
+ this.inAdBreak = false;
2255
+ this.expectedAdBreakDurationMs = void 0;
2256
+ this.currentAdBreakStartWallClockMs = void 0;
2198
2257
  this.currentAdIndex = 0;
2199
2258
  this.totalAdsInBreak = 0;
2200
2259
  this.showAds = false;
2260
+ this.clearAdStopTimer();
2201
2261
  }
2202
2262
  });
2203
2263
  this.video.addEventListener("timeupdate", () => {
@@ -2871,14 +2931,66 @@ var StormcloudVideoPlayer = class {
2871
2931
  }
2872
2932
  }
2873
2933
  ensureAdStoppedByTimer() {
2934
+ var _a, _b;
2874
2935
  if (!this.inAdBreak) return;
2936
+ this.adStopTimerId = void 0;
2937
+ const adPlaying = this.ima.isAdPlaying();
2938
+ const pendingAds = this.adPodQueue.length > 0;
2939
+ const checkIntervalMs = Math.max(
2940
+ 250,
2941
+ Math.floor((_a = this.config.adBreakCheckIntervalMs) != null ? _a : 1e3)
2942
+ );
2943
+ const maxExtensionMsConfig = this.config.maxAdBreakExtensionMs;
2944
+ const maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 6e4;
2945
+ let elapsedSinceStartMs = 0;
2946
+ if (this.currentAdBreakStartWallClockMs != null) {
2947
+ elapsedSinceStartMs = Date.now() - this.currentAdBreakStartWallClockMs;
2948
+ }
2949
+ const expectedDurationMs = (_b = this.expectedAdBreakDurationMs) != null ? _b : 0;
2950
+ const overrunMs = Math.max(0, elapsedSinceStartMs - expectedDurationMs);
2951
+ const shouldExtendAdBreak = (adPlaying || pendingAds || this.showAds) && overrunMs < maxExtensionMs;
2952
+ if (shouldExtendAdBreak) {
2953
+ if (this.config.debugAdTiming) {
2954
+ console.log(
2955
+ "[StormcloudVideoPlayer] Extending ad break beyond scheduled duration",
2956
+ {
2957
+ adPlaying,
2958
+ pendingAds,
2959
+ showAds: this.showAds,
2960
+ overrunMs,
2961
+ checkIntervalMs,
2962
+ maxExtensionMs
2963
+ }
2964
+ );
2965
+ }
2966
+ this.scheduleAdStopCountdown(checkIntervalMs);
2967
+ return;
2968
+ }
2969
+ if (this.config.debugAdTiming) {
2970
+ console.log("[StormcloudVideoPlayer] Ending ad break via timer", {
2971
+ adPlaying,
2972
+ pendingAds,
2973
+ showAds: this.showAds,
2974
+ overrunMs,
2975
+ maxExtensionMs
2976
+ });
2977
+ }
2875
2978
  this.inAdBreak = false;
2876
2979
  this.expectedAdBreakDurationMs = void 0;
2877
2980
  this.currentAdBreakStartWallClockMs = void 0;
2878
- this.adStopTimerId = void 0;
2879
- if (this.ima.isAdPlaying()) {
2981
+ this.showAds = false;
2982
+ this.adPodQueue = [];
2983
+ this.currentAdIndex = 0;
2984
+ this.totalAdsInBreak = 0;
2985
+ this.clearAdFailsafeTimer();
2986
+ if (adPlaying) {
2880
2987
  this.ima.stop().catch(() => {
2881
2988
  });
2989
+ return;
2990
+ }
2991
+ const originalMutedState = this.ima.getOriginalMutedState();
2992
+ if (this.video.muted !== originalMutedState) {
2993
+ this.video.muted = originalMutedState;
2882
2994
  }
2883
2995
  }
2884
2996
  scheduleAdStartIn(delayMs) {