stormcloud-video-player 0.8.34 → 0.8.36

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 (39) hide show
  1. package/dist/stormcloud-vp.min.js +1 -1
  2. package/lib/index.cjs +120 -16
  3. package/lib/index.cjs.map +1 -1
  4. package/lib/index.d.cts +21 -19
  5. package/lib/index.d.ts +21 -19
  6. package/lib/index.js +120 -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.cjs +18 -0
  12. package/lib/player/AdConfigManager.cjs.map +1 -1
  13. package/lib/player/AdConfigManager.d.cts +2 -1
  14. package/lib/player/AdTimingService.d.cts +1 -1
  15. package/lib/player/HlsEngine.d.cts +1 -1
  16. package/lib/player/PlayerControls.d.cts +1 -1
  17. package/lib/player/Scte35CueManager.d.cts +1 -1
  18. package/lib/player/Scte35Parser.d.cts +1 -1
  19. package/lib/player/StormcloudVideoPlayer.cjs +123 -16
  20. package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
  21. package/lib/player/StormcloudVideoPlayer.d.cts +1 -1
  22. package/lib/player/playerTypes.d.cts +1 -1
  23. package/lib/players/HlsPlayer.cjs +123 -16
  24. package/lib/players/HlsPlayer.cjs.map +1 -1
  25. package/lib/players/HlsPlayer.d.cts +1 -1
  26. package/lib/players/index.cjs +123 -16
  27. package/lib/players/index.cjs.map +1 -1
  28. package/lib/sdk/hlsAdPlayer.cjs +62 -0
  29. package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
  30. package/lib/sdk/hlsAdPlayer.d.cts +1 -1
  31. package/lib/{types-CSHvCbhZ.d.cts → types-cTqIKw_D.d.cts} +1 -0
  32. package/lib/ui/StormcloudVideoPlayer.cjs +123 -16
  33. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  34. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
  35. package/lib/utils/browserCompat.cjs +1 -0
  36. package/lib/utils/browserCompat.cjs.map +1 -1
  37. package/lib/utils/browserCompat.d.cts +1 -0
  38. package/lib/utils/tracking.d.cts +1 -1
  39. package/package.json +1 -1
@@ -405,6 +405,11 @@ function createHlsAdPlayer(contentVideo, options) {
405
405
  var preloadingAds = /* @__PURE__ */ new Map();
406
406
  var destroyed = false;
407
407
  var pendingTimeouts = [];
408
+ var STALL_TIMEOUT_MS = 4e3;
409
+ var STALL_CHECK_INTERVAL_MS = 1e3;
410
+ var stallWatchdogId;
411
+ var lastAdProgressTime = 0;
412
+ var lastAdProgressPosition = 0;
408
413
  var trackingFired = {
409
414
  impression: false,
410
415
  start: false,
@@ -1037,6 +1042,7 @@ function createHlsAdPlayer(contentVideo, options) {
1037
1042
  if (!adVideoElement || !currentAd) return;
1038
1043
  adVideoElement.addEventListener("timeupdate", function() {
1039
1044
  if (!currentAd || !adVideoElement) return;
1045
+ noteAdProgress();
1040
1046
  var progress = adVideoElement.currentTime / currentAd.duration;
1041
1047
  if (progress >= 0.25 && !trackingFired.firstQuartile) {
1042
1048
  trackingFired.firstQuartile = true;
@@ -1052,6 +1058,7 @@ function createHlsAdPlayer(contentVideo, options) {
1052
1058
  }
1053
1059
  });
1054
1060
  adVideoElement.addEventListener("playing", function() {
1061
+ startStallWatchdog();
1055
1062
  if (!currentAd || trackingFired.start) return;
1056
1063
  trackingFired.start = true;
1057
1064
  fireTrackingPixels(currentAd.trackingUrls.start);
@@ -1192,8 +1199,52 @@ function createHlsAdPlayer(contentVideo, options) {
1192
1199
  return false;
1193
1200
  }
1194
1201
  }
1202
+ function clearStallWatchdog() {
1203
+ if (stallWatchdogId != null) {
1204
+ clearInterval(stallWatchdogId);
1205
+ stallWatchdogId = void 0;
1206
+ }
1207
+ }
1208
+ function noteAdProgress() {
1209
+ lastAdProgressTime = Date.now();
1210
+ if (adVideoElement) {
1211
+ lastAdProgressPosition = adVideoElement.currentTime;
1212
+ }
1213
+ }
1214
+ function handleAdStall() {
1215
+ console.warn("[HlsAdPlayer] Ad playback stalled (no progress) - recovering to avoid a blank frame");
1216
+ clearStallWatchdog();
1217
+ if (currentAd) {
1218
+ fireTrackingPixels(currentAd.trackingUrls.error);
1219
+ }
1220
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1221
+ return;
1222
+ }
1223
+ handleAdError();
1224
+ }
1225
+ function startStallWatchdog() {
1226
+ clearStallWatchdog();
1227
+ noteAdProgress();
1228
+ stallWatchdogId = window.setInterval(function() {
1229
+ if (!adPlaying || !adVideoElement) {
1230
+ return;
1231
+ }
1232
+ if (adVideoElement.currentTime > lastAdProgressPosition + 0.05) {
1233
+ noteAdProgress();
1234
+ return;
1235
+ }
1236
+ if (adVideoElement.paused) {
1237
+ noteAdProgress();
1238
+ return;
1239
+ }
1240
+ if (Date.now() - lastAdProgressTime >= STALL_TIMEOUT_MS) {
1241
+ handleAdStall();
1242
+ }
1243
+ }, STALL_CHECK_INTERVAL_MS);
1244
+ }
1195
1245
  function handleAdComplete() {
1196
1246
  console.log("[HlsAdPlayer] Handling ad completion");
1247
+ clearStallWatchdog();
1197
1248
  adPlaying = false;
1198
1249
  setAdPlayingFlag(false);
1199
1250
  contentVideo.muted = true;
@@ -1201,6 +1252,10 @@ function createHlsAdPlayer(contentVideo, options) {
1201
1252
  adContainerEl.style.display = "none";
1202
1253
  adContainerEl.style.pointerEvents = "none";
1203
1254
  }
1255
+ if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
1256
+ contentVideo.style.visibility = "visible";
1257
+ contentVideo.style.opacity = "1";
1258
+ }
1204
1259
  if (options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) {
1205
1260
  if (contentVideo.paused) {
1206
1261
  console.log("[HlsAdPlayer] Content video paused in live mode, resuming playback");
@@ -1213,6 +1268,7 @@ function createHlsAdPlayer(contentVideo, options) {
1213
1268
  }
1214
1269
  function handleAdError() {
1215
1270
  console.log("[HlsAdPlayer] Handling ad error");
1271
+ clearStallWatchdog();
1216
1272
  adPlaying = false;
1217
1273
  setAdPlayingFlag(false);
1218
1274
  contentVideo.muted = true;
@@ -1220,6 +1276,10 @@ function createHlsAdPlayer(contentVideo, options) {
1220
1276
  adContainerEl.style.display = "none";
1221
1277
  adContainerEl.style.pointerEvents = "none";
1222
1278
  }
1279
+ if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
1280
+ contentVideo.style.visibility = "visible";
1281
+ contentVideo.style.opacity = "1";
1282
+ }
1223
1283
  emit("ad_error");
1224
1284
  }
1225
1285
  return {
@@ -1417,6 +1477,7 @@ function createHlsAdPlayer(contentVideo, options) {
1417
1477
  var previousMutedState;
1418
1478
  return _ts_generator(this, function(_state) {
1419
1479
  console.log("[HlsAdPlayer] Stopping ad");
1480
+ clearStallWatchdog();
1420
1481
  adPlaying = false;
1421
1482
  setAdPlayingFlag(false);
1422
1483
  previousMutedState = contentVideo.muted;
@@ -1455,6 +1516,7 @@ function createHlsAdPlayer(contentVideo, options) {
1455
1516
  destroy: function destroy() {
1456
1517
  console.log("[HlsAdPlayer] Destroying");
1457
1518
  destroyed = true;
1519
+ clearStallWatchdog();
1458
1520
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1459
1521
  try {
1460
1522
  for(var _iterator = pendingTimeouts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
@@ -1830,6 +1892,9 @@ var DEFAULT_MQTT_CONFIG = {
1830
1892
  qos: 1
1831
1893
  };
1832
1894
  var mqttConfig = _object_spread({}, DEFAULT_MQTT_CONFIG);
1895
+ function applyMQTTConfig(overrides) {
1896
+ Object.assign(mqttConfig, overrides);
1897
+ }
1833
1898
  function isMQTTEnabled() {
1834
1899
  return mqttConfig.enabled;
1835
1900
  }
@@ -3846,6 +3911,7 @@ function getBrowserConfigOverrides() {
3846
3911
  var overrides = {};
3847
3912
  if (browser.isSmartTV) {
3848
3913
  overrides.allowNativeHls = true;
3914
+ overrides.pauseContentDuringAds = true;
3849
3915
  }
3850
3916
  return overrides;
3851
3917
  }
@@ -5000,6 +5066,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
5000
5066
  ];
5001
5067
  case 4:
5002
5068
  data = _state.sent();
5069
+ this.applyMqttConfigFromResponse(data);
5003
5070
  imaPayload = (_data_response = data.response) === null || _data_response === void 0 ? void 0 : (_data_response_ima = _data_response.ima) === null || _data_response_ima === void 0 ? void 0 : (_data_response_ima_publisherdeskima = _data_response_ima["publisherdesk.ima"]) === null || _data_response_ima_publisherdeskima === void 0 ? void 0 : _data_response_ima_publisherdeskima.payload;
5004
5071
  if (imaPayload) {
5005
5072
  decodedPayload = imaPayload;
@@ -5033,6 +5100,20 @@ var AdConfigManager = /*#__PURE__*/ function() {
5033
5100
  }).call(this);
5034
5101
  }
5035
5102
  },
5103
+ {
5104
+ key: "applyMqttConfigFromResponse",
5105
+ value: function applyMqttConfigFromResponse(data) {
5106
+ var _data_response;
5107
+ var mqttConfig2 = (_data_response = data.response) === null || _data_response === void 0 ? void 0 : _data_response.mqtt_config;
5108
+ if (!mqttConfig2 || (typeof mqttConfig2 === "undefined" ? "undefined" : _type_of(mqttConfig2)) !== "object") {
5109
+ return;
5110
+ }
5111
+ applyMQTTConfig(mqttConfig2);
5112
+ if (this.debug) {
5113
+ console.log("[StormcloudVideoPlayer] Applied MQTT config from /ads/web:", mqttConfig2);
5114
+ }
5115
+ }
5116
+ },
5036
5117
  {
5037
5118
  key: "fetchAndParseVmap",
5038
5119
  value: function fetchAndParseVmap(vmapUrl) {
@@ -6728,12 +6809,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6728
6809
  // ───────────────────────────────────────────────────────────────
6729
6810
  // IMA event listeners
6730
6811
  // ───────────────────────────────────────────────────────────────
6731
- /**
6732
- * Produces the next pod ad-request URL for the current break. Every request
6733
- * (initial and continuous-fetch top-up) carries pod macros (pmad/pmnd/pmxd,
6734
- * no ppos); pmxd tracks the remaining break time so top-up pods only ask for
6735
- * what still fits.
6736
- */ key: "nextAdRequestUrl",
6812
+ key: "nextAdRequestUrl",
6737
6813
  value: function nextAdRequestUrl(baseVastUrl) {
6738
6814
  var remaining = Math.min(this.timing.getWallClockRemainingAdMs(), this.timing.getDurationBudgetRemainingMs());
6739
6815
  var breakDurationMs = remaining > 0 ? remaining : void 0;
@@ -7862,7 +7938,9 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7862
7938
  if (Math.abs(this.host.video.volume - restoredVolume) > 0.01) {
7863
7939
  this.host.video.volume = restoredVolume;
7864
7940
  }
7865
- if (this.host.shouldContinueLiveStreamDuringAds()) {
7941
+ this.host.video.style.visibility = "visible";
7942
+ this.host.video.style.opacity = "1";
7943
+ if (this.host.isLiveStream()) {
7866
7944
  var liveSyncPos = this.host.getLiveSyncPosition();
7867
7945
  if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {
7868
7946
  if (this.debug) {
@@ -7870,16 +7948,39 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7870
7948
  }
7871
7949
  this.host.video.currentTime = liveSyncPos;
7872
7950
  }
7873
- if (this.host.video.paused) {
7874
- var _this_host_video_play;
7875
- if (this.debug) console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
7876
- (_this_host_video_play = this.host.video.play()) === null || _this_host_video_play === void 0 ? void 0 : _this_host_video_play.catch(function() {});
7877
- } else {
7878
- if (this.debug) console.log("[StormcloudVideoPlayer] Content video already playing in live mode after ads");
7951
+ }
7952
+ this.resumeContentPlayback();
7953
+ }
7954
+ },
7955
+ {
7956
+ key: "resumeContentPlayback",
7957
+ value: function resumeContentPlayback() {
7958
+ var _this = this;
7959
+ var attempt = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 0;
7960
+ var maxAttempts = 3;
7961
+ if (!this.host.video.paused) {
7962
+ if (this.debug && attempt === 0) {
7963
+ console.log("[StormcloudVideoPlayer] Content video already playing after ads");
7879
7964
  }
7880
- } else if (this.host.video.paused) {
7881
- var _this_host_video_play1;
7882
- (_this_host_video_play1 = this.host.video.play()) === null || _this_host_video_play1 === void 0 ? void 0 : _this_host_video_play1.catch(function() {});
7965
+ return;
7966
+ }
7967
+ if (this.debug) {
7968
+ console.log("[StormcloudVideoPlayer] Resuming content playback after ads (attempt ".concat(attempt + 1, "/").concat(maxAttempts, ")"));
7969
+ }
7970
+ var playResult = this.host.video.play();
7971
+ if (playResult && typeof playResult.catch === "function") {
7972
+ playResult.catch(function(error) {
7973
+ if (attempt + 1 >= maxAttempts) {
7974
+ if (_this.debug) {
7975
+ console.warn("[StormcloudVideoPlayer] Failed to resume content playback after ads:", error);
7976
+ }
7977
+ return;
7978
+ }
7979
+ var backoffMs = 250 * (attempt + 1);
7980
+ window.setTimeout(function() {
7981
+ _this.resumeContentPlayback(attempt + 1);
7982
+ }, backoffMs);
7983
+ });
7883
7984
  }
7884
7985
  }
7885
7986
  },
@@ -7993,6 +8094,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7993
8094
  shouldContinueLiveStreamDuringAds: function shouldContinueLiveStreamDuringAds() {
7994
8095
  return _this.shouldContinueLiveStreamDuringAds();
7995
8096
  },
8097
+ isLiveStream: function isLiveStream() {
8098
+ return _this.hlsEngine.isLiveStream;
8099
+ },
7996
8100
  getLiveSyncPosition: function getLiveSyncPosition() {
7997
8101
  return _this.hlsEngine.getLiveSyncPosition();
7998
8102
  },
@@ -8215,6 +8319,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8215
8319
  {
8216
8320
  key: "shouldContinueLiveStreamDuringAds",
8217
8321
  value: function shouldContinueLiveStreamDuringAds() {
8322
+ if (this.config.pauseContentDuringAds) {
8323
+ return false;
8324
+ }
8218
8325
  if (this.hlsEngine.isLiveStream) {
8219
8326
  return true;
8220
8327
  }