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
@@ -1,4 +1,4 @@
1
- import { a as StormcloudVideoPlayerConfig } from '../types-CSHvCbhZ.cjs';
1
+ import { a as StormcloudVideoPlayerConfig } from '../types-cTqIKw_D.cjs';
2
2
 
3
3
  declare class StormcloudVideoPlayer {
4
4
  private readonly video;
@@ -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
 
3
3
  type Scte35CueSource = "manifest" | "id3" | "ts";
4
4
  type Scte35CueReadiness = "early" | "playback";
@@ -419,6 +419,11 @@ function createHlsAdPlayer(contentVideo, options) {
419
419
  var preloadingAds = /* @__PURE__ */ new Map();
420
420
  var destroyed = false;
421
421
  var pendingTimeouts = [];
422
+ var STALL_TIMEOUT_MS = 4e3;
423
+ var STALL_CHECK_INTERVAL_MS = 1e3;
424
+ var stallWatchdogId;
425
+ var lastAdProgressTime = 0;
426
+ var lastAdProgressPosition = 0;
422
427
  var trackingFired = {
423
428
  impression: false,
424
429
  start: false,
@@ -1051,6 +1056,7 @@ function createHlsAdPlayer(contentVideo, options) {
1051
1056
  if (!adVideoElement || !currentAd) return;
1052
1057
  adVideoElement.addEventListener("timeupdate", function() {
1053
1058
  if (!currentAd || !adVideoElement) return;
1059
+ noteAdProgress();
1054
1060
  var progress = adVideoElement.currentTime / currentAd.duration;
1055
1061
  if (progress >= 0.25 && !trackingFired.firstQuartile) {
1056
1062
  trackingFired.firstQuartile = true;
@@ -1066,6 +1072,7 @@ function createHlsAdPlayer(contentVideo, options) {
1066
1072
  }
1067
1073
  });
1068
1074
  adVideoElement.addEventListener("playing", function() {
1075
+ startStallWatchdog();
1069
1076
  if (!currentAd || trackingFired.start) return;
1070
1077
  trackingFired.start = true;
1071
1078
  fireTrackingPixels(currentAd.trackingUrls.start);
@@ -1206,8 +1213,52 @@ function createHlsAdPlayer(contentVideo, options) {
1206
1213
  return false;
1207
1214
  }
1208
1215
  }
1216
+ function clearStallWatchdog() {
1217
+ if (stallWatchdogId != null) {
1218
+ clearInterval(stallWatchdogId);
1219
+ stallWatchdogId = void 0;
1220
+ }
1221
+ }
1222
+ function noteAdProgress() {
1223
+ lastAdProgressTime = Date.now();
1224
+ if (adVideoElement) {
1225
+ lastAdProgressPosition = adVideoElement.currentTime;
1226
+ }
1227
+ }
1228
+ function handleAdStall() {
1229
+ console.warn("[HlsAdPlayer] Ad playback stalled (no progress) - recovering to avoid a blank frame");
1230
+ clearStallWatchdog();
1231
+ if (currentAd) {
1232
+ fireTrackingPixels(currentAd.trackingUrls.error);
1233
+ }
1234
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1235
+ return;
1236
+ }
1237
+ handleAdError();
1238
+ }
1239
+ function startStallWatchdog() {
1240
+ clearStallWatchdog();
1241
+ noteAdProgress();
1242
+ stallWatchdogId = window.setInterval(function() {
1243
+ if (!adPlaying || !adVideoElement) {
1244
+ return;
1245
+ }
1246
+ if (adVideoElement.currentTime > lastAdProgressPosition + 0.05) {
1247
+ noteAdProgress();
1248
+ return;
1249
+ }
1250
+ if (adVideoElement.paused) {
1251
+ noteAdProgress();
1252
+ return;
1253
+ }
1254
+ if (Date.now() - lastAdProgressTime >= STALL_TIMEOUT_MS) {
1255
+ handleAdStall();
1256
+ }
1257
+ }, STALL_CHECK_INTERVAL_MS);
1258
+ }
1209
1259
  function handleAdComplete() {
1210
1260
  console.log("[HlsAdPlayer] Handling ad completion");
1261
+ clearStallWatchdog();
1211
1262
  adPlaying = false;
1212
1263
  setAdPlayingFlag(false);
1213
1264
  contentVideo.muted = true;
@@ -1215,6 +1266,10 @@ function createHlsAdPlayer(contentVideo, options) {
1215
1266
  adContainerEl.style.display = "none";
1216
1267
  adContainerEl.style.pointerEvents = "none";
1217
1268
  }
1269
+ if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
1270
+ contentVideo.style.visibility = "visible";
1271
+ contentVideo.style.opacity = "1";
1272
+ }
1218
1273
  if (options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) {
1219
1274
  if (contentVideo.paused) {
1220
1275
  console.log("[HlsAdPlayer] Content video paused in live mode, resuming playback");
@@ -1227,6 +1282,7 @@ function createHlsAdPlayer(contentVideo, options) {
1227
1282
  }
1228
1283
  function handleAdError() {
1229
1284
  console.log("[HlsAdPlayer] Handling ad error");
1285
+ clearStallWatchdog();
1230
1286
  adPlaying = false;
1231
1287
  setAdPlayingFlag(false);
1232
1288
  contentVideo.muted = true;
@@ -1234,6 +1290,10 @@ function createHlsAdPlayer(contentVideo, options) {
1234
1290
  adContainerEl.style.display = "none";
1235
1291
  adContainerEl.style.pointerEvents = "none";
1236
1292
  }
1293
+ if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
1294
+ contentVideo.style.visibility = "visible";
1295
+ contentVideo.style.opacity = "1";
1296
+ }
1237
1297
  emit("ad_error");
1238
1298
  }
1239
1299
  return {
@@ -1431,6 +1491,7 @@ function createHlsAdPlayer(contentVideo, options) {
1431
1491
  var previousMutedState;
1432
1492
  return _ts_generator(this, function(_state) {
1433
1493
  console.log("[HlsAdPlayer] Stopping ad");
1494
+ clearStallWatchdog();
1434
1495
  adPlaying = false;
1435
1496
  setAdPlayingFlag(false);
1436
1497
  previousMutedState = contentVideo.muted;
@@ -1469,6 +1530,7 @@ function createHlsAdPlayer(contentVideo, options) {
1469
1530
  destroy: function destroy() {
1470
1531
  console.log("[HlsAdPlayer] Destroying");
1471
1532
  destroyed = true;
1533
+ clearStallWatchdog();
1472
1534
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1473
1535
  try {
1474
1536
  for(var _iterator = pendingTimeouts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
@@ -1844,6 +1906,9 @@ var DEFAULT_MQTT_CONFIG = {
1844
1906
  qos: 1
1845
1907
  };
1846
1908
  var mqttConfig = _object_spread({}, DEFAULT_MQTT_CONFIG);
1909
+ function applyMQTTConfig(overrides) {
1910
+ Object.assign(mqttConfig, overrides);
1911
+ }
1847
1912
  function isMQTTEnabled() {
1848
1913
  return mqttConfig.enabled;
1849
1914
  }
@@ -3860,6 +3925,7 @@ function getBrowserConfigOverrides() {
3860
3925
  var overrides = {};
3861
3926
  if (browser.isSmartTV) {
3862
3927
  overrides.allowNativeHls = true;
3928
+ overrides.pauseContentDuringAds = true;
3863
3929
  }
3864
3930
  return overrides;
3865
3931
  }
@@ -5014,6 +5080,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
5014
5080
  ];
5015
5081
  case 4:
5016
5082
  data = _state.sent();
5083
+ this.applyMqttConfigFromResponse(data);
5017
5084
  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;
5018
5085
  if (imaPayload) {
5019
5086
  decodedPayload = imaPayload;
@@ -5047,6 +5114,20 @@ var AdConfigManager = /*#__PURE__*/ function() {
5047
5114
  }).call(this);
5048
5115
  }
5049
5116
  },
5117
+ {
5118
+ key: "applyMqttConfigFromResponse",
5119
+ value: function applyMqttConfigFromResponse(data) {
5120
+ var _data_response;
5121
+ var mqttConfig2 = (_data_response = data.response) === null || _data_response === void 0 ? void 0 : _data_response.mqtt_config;
5122
+ if (!mqttConfig2 || (typeof mqttConfig2 === "undefined" ? "undefined" : _type_of(mqttConfig2)) !== "object") {
5123
+ return;
5124
+ }
5125
+ applyMQTTConfig(mqttConfig2);
5126
+ if (this.debug) {
5127
+ console.log("[StormcloudVideoPlayer] Applied MQTT config from /ads/web:", mqttConfig2);
5128
+ }
5129
+ }
5130
+ },
5050
5131
  {
5051
5132
  key: "fetchAndParseVmap",
5052
5133
  value: function fetchAndParseVmap(vmapUrl) {
@@ -6742,12 +6823,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6742
6823
  // ───────────────────────────────────────────────────────────────
6743
6824
  // IMA event listeners
6744
6825
  // ───────────────────────────────────────────────────────────────
6745
- /**
6746
- * Produces the next pod ad-request URL for the current break. Every request
6747
- * (initial and continuous-fetch top-up) carries pod macros (pmad/pmnd/pmxd,
6748
- * no ppos); pmxd tracks the remaining break time so top-up pods only ask for
6749
- * what still fits.
6750
- */ key: "nextAdRequestUrl",
6826
+ key: "nextAdRequestUrl",
6751
6827
  value: function nextAdRequestUrl(baseVastUrl) {
6752
6828
  var remaining = Math.min(this.timing.getWallClockRemainingAdMs(), this.timing.getDurationBudgetRemainingMs());
6753
6829
  var breakDurationMs = remaining > 0 ? remaining : void 0;
@@ -7876,7 +7952,9 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7876
7952
  if (Math.abs(this.host.video.volume - restoredVolume) > 0.01) {
7877
7953
  this.host.video.volume = restoredVolume;
7878
7954
  }
7879
- if (this.host.shouldContinueLiveStreamDuringAds()) {
7955
+ this.host.video.style.visibility = "visible";
7956
+ this.host.video.style.opacity = "1";
7957
+ if (this.host.isLiveStream()) {
7880
7958
  var liveSyncPos = this.host.getLiveSyncPosition();
7881
7959
  if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {
7882
7960
  if (this.debug) {
@@ -7884,16 +7962,39 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7884
7962
  }
7885
7963
  this.host.video.currentTime = liveSyncPos;
7886
7964
  }
7887
- if (this.host.video.paused) {
7888
- var _this_host_video_play;
7889
- if (this.debug) console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
7890
- (_this_host_video_play = this.host.video.play()) === null || _this_host_video_play === void 0 ? void 0 : _this_host_video_play.catch(function() {});
7891
- } else {
7892
- if (this.debug) console.log("[StormcloudVideoPlayer] Content video already playing in live mode after ads");
7965
+ }
7966
+ this.resumeContentPlayback();
7967
+ }
7968
+ },
7969
+ {
7970
+ key: "resumeContentPlayback",
7971
+ value: function resumeContentPlayback() {
7972
+ var _this = this;
7973
+ var attempt = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 0;
7974
+ var maxAttempts = 3;
7975
+ if (!this.host.video.paused) {
7976
+ if (this.debug && attempt === 0) {
7977
+ console.log("[StormcloudVideoPlayer] Content video already playing after ads");
7893
7978
  }
7894
- } else if (this.host.video.paused) {
7895
- var _this_host_video_play1;
7896
- (_this_host_video_play1 = this.host.video.play()) === null || _this_host_video_play1 === void 0 ? void 0 : _this_host_video_play1.catch(function() {});
7979
+ return;
7980
+ }
7981
+ if (this.debug) {
7982
+ console.log("[StormcloudVideoPlayer] Resuming content playback after ads (attempt ".concat(attempt + 1, "/").concat(maxAttempts, ")"));
7983
+ }
7984
+ var playResult = this.host.video.play();
7985
+ if (playResult && typeof playResult.catch === "function") {
7986
+ playResult.catch(function(error) {
7987
+ if (attempt + 1 >= maxAttempts) {
7988
+ if (_this.debug) {
7989
+ console.warn("[StormcloudVideoPlayer] Failed to resume content playback after ads:", error);
7990
+ }
7991
+ return;
7992
+ }
7993
+ var backoffMs = 250 * (attempt + 1);
7994
+ window.setTimeout(function() {
7995
+ _this.resumeContentPlayback(attempt + 1);
7996
+ }, backoffMs);
7997
+ });
7897
7998
  }
7898
7999
  }
7899
8000
  },
@@ -8007,6 +8108,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8007
8108
  shouldContinueLiveStreamDuringAds: function shouldContinueLiveStreamDuringAds() {
8008
8109
  return _this.shouldContinueLiveStreamDuringAds();
8009
8110
  },
8111
+ isLiveStream: function isLiveStream() {
8112
+ return _this.hlsEngine.isLiveStream;
8113
+ },
8010
8114
  getLiveSyncPosition: function getLiveSyncPosition() {
8011
8115
  return _this.hlsEngine.getLiveSyncPosition();
8012
8116
  },
@@ -8229,6 +8333,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8229
8333
  {
8230
8334
  key: "shouldContinueLiveStreamDuringAds",
8231
8335
  value: function shouldContinueLiveStreamDuringAds() {
8336
+ if (this.config.pauseContentDuringAds) {
8337
+ return false;
8338
+ }
8232
8339
  if (this.hlsEngine.isLiveStream) {
8233
8340
  return true;
8234
8341
  }