stormcloud-video-player 0.8.34 → 0.8.35

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.
Files changed (37) hide show
  1. package/dist/stormcloud-vp.min.js +1 -1
  2. package/lib/index.cjs +105 -16
  3. package/lib/index.cjs.map +1 -1
  4. package/lib/index.d.cts +2 -0
  5. package/lib/index.d.ts +2 -0
  6. package/lib/index.js +105 -16
  7. package/lib/index.js.map +1 -1
  8. package/lib/player/AdBreakOrchestrator.cjs +36 -16
  9. package/lib/player/AdBreakOrchestrator.cjs.map +1 -1
  10. package/lib/player/AdBreakOrchestrator.d.cts +3 -7
  11. package/lib/player/AdConfigManager.d.cts +1 -1
  12. package/lib/player/AdTimingService.d.cts +1 -1
  13. package/lib/player/HlsEngine.d.cts +1 -1
  14. package/lib/player/PlayerControls.d.cts +1 -1
  15. package/lib/player/Scte35CueManager.d.cts +1 -1
  16. package/lib/player/Scte35Parser.d.cts +1 -1
  17. package/lib/player/StormcloudVideoPlayer.cjs +105 -16
  18. package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
  19. package/lib/player/StormcloudVideoPlayer.d.cts +1 -1
  20. package/lib/player/playerTypes.d.cts +1 -1
  21. package/lib/players/HlsPlayer.cjs +105 -16
  22. package/lib/players/HlsPlayer.cjs.map +1 -1
  23. package/lib/players/HlsPlayer.d.cts +1 -1
  24. package/lib/players/index.cjs +105 -16
  25. package/lib/players/index.cjs.map +1 -1
  26. package/lib/sdk/hlsAdPlayer.cjs +62 -0
  27. package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
  28. package/lib/sdk/hlsAdPlayer.d.cts +1 -1
  29. package/lib/{types-CSHvCbhZ.d.cts → types-cTqIKw_D.d.cts} +1 -0
  30. package/lib/ui/StormcloudVideoPlayer.cjs +105 -16
  31. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  32. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
  33. package/lib/utils/browserCompat.cjs +1 -0
  34. package/lib/utils/browserCompat.cjs.map +1 -1
  35. package/lib/utils/browserCompat.d.cts +1 -0
  36. package/lib/utils/tracking.d.cts +1 -1
  37. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { S as Scte35Marker, a as StormcloudVideoPlayerConfig, A as AdController } from '../types-CSHvCbhZ.cjs';
1
+ import { S as Scte35Marker, a as StormcloudVideoPlayerConfig, A as AdController } from '../types-cTqIKw_D.cjs';
2
2
  import { AdTimingService } from './AdTimingService.cjs';
3
3
  import { AdConfigManager } from './AdConfigManager.cjs';
4
4
  import { Scte35CueManager } from './Scte35CueManager.cjs';
@@ -14,6 +14,7 @@ interface AdBreakOrchestratorHost {
14
14
  setAdPlayer(player: AdController): void;
15
15
  createAdPlayer(continueLiveStreamDuringAds: boolean): AdController;
16
16
  shouldContinueLiveStreamDuringAds(): boolean;
17
+ isLiveStream(): boolean;
17
18
  getLiveSyncPosition(): number | undefined;
18
19
  generatePodVastUrl(baseUrl: string, breakDurationMs?: number): string;
19
20
  }
@@ -43,12 +44,6 @@ declare class AdBreakOrchestrator {
43
44
  constructor(host: AdBreakOrchestratorHost, timing: AdTimingService, adConfig: AdConfigManager, cueManager: Scte35CueManager, placeholder: PlaceholderLayer);
44
45
  private get debug();
45
46
  private get analyticsContext();
46
- /**
47
- * Produces the next pod ad-request URL for the current break. Every request
48
- * (initial and continuous-fetch top-up) carries pod macros (pmad/pmnd/pmxd,
49
- * no ppos); pmxd tracks the remaining break time so top-up pods only ask for
50
- * what still fits.
51
- */
52
47
  private nextAdRequestUrl;
53
48
  attachImaEventListeners(): void;
54
49
  recreateAdController(): void;
@@ -63,6 +58,7 @@ declare class AdBreakOrchestrator {
63
58
  playSingleAd(vastTagUrl: string): Promise<void>;
64
59
  private showPlaceholderAndWaitForAds;
65
60
  handleAdPodComplete(): void;
61
+ private resumeContentPlayback;
66
62
  handleAdFailure(): void;
67
63
  }
68
64
 
@@ -1,4 +1,4 @@
1
- import { b as AdBreak, a as StormcloudVideoPlayerConfig, A as AdController } from '../types-CSHvCbhZ.cjs';
1
+ import { b as AdBreak, a as StormcloudVideoPlayerConfig, A as AdController } from '../types-cTqIKw_D.cjs';
2
2
  import { PalNonceManager } from '../sdk/pal.cjs';
3
3
  import { VastConsentSignals } from '../utils/vastMacros.cjs';
4
4
 
@@ -1,4 +1,4 @@
1
- import { a as StormcloudVideoPlayerConfig, S as Scte35Marker, A as AdController } from '../types-CSHvCbhZ.cjs';
1
+ import { a as StormcloudVideoPlayerConfig, S as Scte35Marker, A as AdController } from '../types-cTqIKw_D.cjs';
2
2
 
3
3
  interface AdTimingCallbacks {
4
4
  onAdStopTimerFired(): void;
@@ -1,5 +1,5 @@
1
1
  import Hls from 'hls.js';
2
- import { a as StormcloudVideoPlayerConfig, S as Scte35Marker } from '../types-CSHvCbhZ.cjs';
2
+ import { a as StormcloudVideoPlayerConfig, S as Scte35Marker } from '../types-cTqIKw_D.cjs';
3
3
  import { Scte35CueContext } from './playerTypes.cjs';
4
4
  import { Scte35CueManager } from './Scte35CueManager.cjs';
5
5
  import { AdTimingService } from './AdTimingService.cjs';
@@ -1,4 +1,4 @@
1
- import { A as AdController } from '../types-CSHvCbhZ.cjs';
1
+ import { A as AdController } from '../types-cTqIKw_D.cjs';
2
2
 
3
3
  declare function getAdAudioVolume(adPlayer: AdController): number;
4
4
  declare function toggleMute(video: HTMLVideoElement, adPlayer: AdController, debug: boolean): void;
@@ -1,4 +1,4 @@
1
- import { S as Scte35Marker } from '../types-CSHvCbhZ.cjs';
1
+ import { S as Scte35Marker } from '../types-cTqIKw_D.cjs';
2
2
  import { Scte35CueSource, Scte35CueReadiness, Scte35CueContext } from './playerTypes.cjs';
3
3
 
4
4
  interface Scte35CueManagerCallbacks {
@@ -1,4 +1,4 @@
1
- import { S as Scte35Marker, I as Id3TagInfo } from '../types-CSHvCbhZ.cjs';
1
+ import { S as Scte35Marker, I as Id3TagInfo } from '../types-cTqIKw_D.cjs';
2
2
 
3
3
  declare function parseCueOutDuration(value: string): number | undefined;
4
4
  declare function parseCueOutCont(value: string): {
@@ -368,6 +368,11 @@ function createHlsAdPlayer(contentVideo, options) {
368
368
  var preloadingAds = /* @__PURE__ */ new Map();
369
369
  var destroyed = false;
370
370
  var pendingTimeouts = [];
371
+ var STALL_TIMEOUT_MS = 4e3;
372
+ var STALL_CHECK_INTERVAL_MS = 1e3;
373
+ var stallWatchdogId;
374
+ var lastAdProgressTime = 0;
375
+ var lastAdProgressPosition = 0;
371
376
  var trackingFired = {
372
377
  impression: false,
373
378
  start: false,
@@ -1000,6 +1005,7 @@ function createHlsAdPlayer(contentVideo, options) {
1000
1005
  if (!adVideoElement || !currentAd) return;
1001
1006
  adVideoElement.addEventListener("timeupdate", function() {
1002
1007
  if (!currentAd || !adVideoElement) return;
1008
+ noteAdProgress();
1003
1009
  var progress = adVideoElement.currentTime / currentAd.duration;
1004
1010
  if (progress >= 0.25 && !trackingFired.firstQuartile) {
1005
1011
  trackingFired.firstQuartile = true;
@@ -1015,6 +1021,7 @@ function createHlsAdPlayer(contentVideo, options) {
1015
1021
  }
1016
1022
  });
1017
1023
  adVideoElement.addEventListener("playing", function() {
1024
+ startStallWatchdog();
1018
1025
  if (!currentAd || trackingFired.start) return;
1019
1026
  trackingFired.start = true;
1020
1027
  fireTrackingPixels(currentAd.trackingUrls.start);
@@ -1155,8 +1162,52 @@ function createHlsAdPlayer(contentVideo, options) {
1155
1162
  return false;
1156
1163
  }
1157
1164
  }
1165
+ function clearStallWatchdog() {
1166
+ if (stallWatchdogId != null) {
1167
+ clearInterval(stallWatchdogId);
1168
+ stallWatchdogId = void 0;
1169
+ }
1170
+ }
1171
+ function noteAdProgress() {
1172
+ lastAdProgressTime = Date.now();
1173
+ if (adVideoElement) {
1174
+ lastAdProgressPosition = adVideoElement.currentTime;
1175
+ }
1176
+ }
1177
+ function handleAdStall() {
1178
+ console.warn("[HlsAdPlayer] Ad playback stalled (no progress) - recovering to avoid a blank frame");
1179
+ clearStallWatchdog();
1180
+ if (currentAd) {
1181
+ fireTrackingPixels(currentAd.trackingUrls.error);
1182
+ }
1183
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1184
+ return;
1185
+ }
1186
+ handleAdError();
1187
+ }
1188
+ function startStallWatchdog() {
1189
+ clearStallWatchdog();
1190
+ noteAdProgress();
1191
+ stallWatchdogId = window.setInterval(function() {
1192
+ if (!adPlaying || !adVideoElement) {
1193
+ return;
1194
+ }
1195
+ if (adVideoElement.currentTime > lastAdProgressPosition + 0.05) {
1196
+ noteAdProgress();
1197
+ return;
1198
+ }
1199
+ if (adVideoElement.paused) {
1200
+ noteAdProgress();
1201
+ return;
1202
+ }
1203
+ if (Date.now() - lastAdProgressTime >= STALL_TIMEOUT_MS) {
1204
+ handleAdStall();
1205
+ }
1206
+ }, STALL_CHECK_INTERVAL_MS);
1207
+ }
1158
1208
  function handleAdComplete() {
1159
1209
  console.log("[HlsAdPlayer] Handling ad completion");
1210
+ clearStallWatchdog();
1160
1211
  adPlaying = false;
1161
1212
  setAdPlayingFlag(false);
1162
1213
  contentVideo.muted = true;
@@ -1164,6 +1215,10 @@ function createHlsAdPlayer(contentVideo, options) {
1164
1215
  adContainerEl.style.display = "none";
1165
1216
  adContainerEl.style.pointerEvents = "none";
1166
1217
  }
1218
+ if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
1219
+ contentVideo.style.visibility = "visible";
1220
+ contentVideo.style.opacity = "1";
1221
+ }
1167
1222
  if (options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) {
1168
1223
  if (contentVideo.paused) {
1169
1224
  console.log("[HlsAdPlayer] Content video paused in live mode, resuming playback");
@@ -1176,6 +1231,7 @@ function createHlsAdPlayer(contentVideo, options) {
1176
1231
  }
1177
1232
  function handleAdError() {
1178
1233
  console.log("[HlsAdPlayer] Handling ad error");
1234
+ clearStallWatchdog();
1179
1235
  adPlaying = false;
1180
1236
  setAdPlayingFlag(false);
1181
1237
  contentVideo.muted = true;
@@ -1183,6 +1239,10 @@ function createHlsAdPlayer(contentVideo, options) {
1183
1239
  adContainerEl.style.display = "none";
1184
1240
  adContainerEl.style.pointerEvents = "none";
1185
1241
  }
1242
+ if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
1243
+ contentVideo.style.visibility = "visible";
1244
+ contentVideo.style.opacity = "1";
1245
+ }
1186
1246
  emit("ad_error");
1187
1247
  }
1188
1248
  return {
@@ -1380,6 +1440,7 @@ function createHlsAdPlayer(contentVideo, options) {
1380
1440
  var previousMutedState;
1381
1441
  return _ts_generator(this, function(_state) {
1382
1442
  console.log("[HlsAdPlayer] Stopping ad");
1443
+ clearStallWatchdog();
1383
1444
  adPlaying = false;
1384
1445
  setAdPlayingFlag(false);
1385
1446
  previousMutedState = contentVideo.muted;
@@ -1418,6 +1479,7 @@ function createHlsAdPlayer(contentVideo, options) {
1418
1479
  destroy: function destroy() {
1419
1480
  console.log("[HlsAdPlayer] Destroying");
1420
1481
  destroyed = true;
1482
+ clearStallWatchdog();
1421
1483
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1422
1484
  try {
1423
1485
  for(var _iterator = pendingTimeouts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
@@ -3809,6 +3871,7 @@ function getBrowserConfigOverrides() {
3809
3871
  var overrides = {};
3810
3872
  if (browser.isSmartTV) {
3811
3873
  overrides.allowNativeHls = true;
3874
+ overrides.pauseContentDuringAds = true;
3812
3875
  }
3813
3876
  return overrides;
3814
3877
  }
@@ -6691,12 +6754,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6691
6754
  // ───────────────────────────────────────────────────────────────
6692
6755
  // IMA event listeners
6693
6756
  // ───────────────────────────────────────────────────────────────
6694
- /**
6695
- * Produces the next pod ad-request URL for the current break. Every request
6696
- * (initial and continuous-fetch top-up) carries pod macros (pmad/pmnd/pmxd,
6697
- * no ppos); pmxd tracks the remaining break time so top-up pods only ask for
6698
- * what still fits.
6699
- */ key: "nextAdRequestUrl",
6757
+ key: "nextAdRequestUrl",
6700
6758
  value: function nextAdRequestUrl(baseVastUrl) {
6701
6759
  var remaining = Math.min(this.timing.getWallClockRemainingAdMs(), this.timing.getDurationBudgetRemainingMs());
6702
6760
  var breakDurationMs = remaining > 0 ? remaining : void 0;
@@ -7825,7 +7883,9 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7825
7883
  if (Math.abs(this.host.video.volume - restoredVolume) > 0.01) {
7826
7884
  this.host.video.volume = restoredVolume;
7827
7885
  }
7828
- if (this.host.shouldContinueLiveStreamDuringAds()) {
7886
+ this.host.video.style.visibility = "visible";
7887
+ this.host.video.style.opacity = "1";
7888
+ if (this.host.isLiveStream()) {
7829
7889
  var liveSyncPos = this.host.getLiveSyncPosition();
7830
7890
  if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {
7831
7891
  if (this.debug) {
@@ -7833,16 +7893,39 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7833
7893
  }
7834
7894
  this.host.video.currentTime = liveSyncPos;
7835
7895
  }
7836
- if (this.host.video.paused) {
7837
- var _this_host_video_play;
7838
- if (this.debug) console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
7839
- (_this_host_video_play = this.host.video.play()) === null || _this_host_video_play === void 0 ? void 0 : _this_host_video_play.catch(function() {});
7840
- } else {
7841
- if (this.debug) console.log("[StormcloudVideoPlayer] Content video already playing in live mode after ads");
7896
+ }
7897
+ this.resumeContentPlayback();
7898
+ }
7899
+ },
7900
+ {
7901
+ key: "resumeContentPlayback",
7902
+ value: function resumeContentPlayback() {
7903
+ var _this = this;
7904
+ var attempt = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 0;
7905
+ var maxAttempts = 3;
7906
+ if (!this.host.video.paused) {
7907
+ if (this.debug && attempt === 0) {
7908
+ console.log("[StormcloudVideoPlayer] Content video already playing after ads");
7842
7909
  }
7843
- } else if (this.host.video.paused) {
7844
- var _this_host_video_play1;
7845
- (_this_host_video_play1 = this.host.video.play()) === null || _this_host_video_play1 === void 0 ? void 0 : _this_host_video_play1.catch(function() {});
7910
+ return;
7911
+ }
7912
+ if (this.debug) {
7913
+ console.log("[StormcloudVideoPlayer] Resuming content playback after ads (attempt ".concat(attempt + 1, "/").concat(maxAttempts, ")"));
7914
+ }
7915
+ var playResult = this.host.video.play();
7916
+ if (playResult && typeof playResult.catch === "function") {
7917
+ playResult.catch(function(error) {
7918
+ if (attempt + 1 >= maxAttempts) {
7919
+ if (_this.debug) {
7920
+ console.warn("[StormcloudVideoPlayer] Failed to resume content playback after ads:", error);
7921
+ }
7922
+ return;
7923
+ }
7924
+ var backoffMs = 250 * (attempt + 1);
7925
+ window.setTimeout(function() {
7926
+ _this.resumeContentPlayback(attempt + 1);
7927
+ }, backoffMs);
7928
+ });
7846
7929
  }
7847
7930
  }
7848
7931
  },
@@ -7956,6 +8039,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7956
8039
  shouldContinueLiveStreamDuringAds: function shouldContinueLiveStreamDuringAds() {
7957
8040
  return _this.shouldContinueLiveStreamDuringAds();
7958
8041
  },
8042
+ isLiveStream: function isLiveStream() {
8043
+ return _this.hlsEngine.isLiveStream;
8044
+ },
7959
8045
  getLiveSyncPosition: function getLiveSyncPosition() {
7960
8046
  return _this.hlsEngine.getLiveSyncPosition();
7961
8047
  },
@@ -8178,6 +8264,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8178
8264
  {
8179
8265
  key: "shouldContinueLiveStreamDuringAds",
8180
8266
  value: function shouldContinueLiveStreamDuringAds() {
8267
+ if (this.config.pauseContentDuringAds) {
8268
+ return false;
8269
+ }
8181
8270
  if (this.hlsEngine.isLiveStream) {
8182
8271
  return true;
8183
8272
  }