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
package/lib/index.cjs CHANGED
@@ -606,6 +606,11 @@ function createHlsAdPlayer(contentVideo, options) {
606
606
  var preloadingAds = /* @__PURE__ */ new Map();
607
607
  var destroyed = false;
608
608
  var pendingTimeouts = [];
609
+ var STALL_TIMEOUT_MS = 4e3;
610
+ var STALL_CHECK_INTERVAL_MS = 1e3;
611
+ var stallWatchdogId;
612
+ var lastAdProgressTime = 0;
613
+ var lastAdProgressPosition = 0;
609
614
  var trackingFired = {
610
615
  impression: false,
611
616
  start: false,
@@ -1238,6 +1243,7 @@ function createHlsAdPlayer(contentVideo, options) {
1238
1243
  if (!adVideoElement || !currentAd) return;
1239
1244
  adVideoElement.addEventListener("timeupdate", function() {
1240
1245
  if (!currentAd || !adVideoElement) return;
1246
+ noteAdProgress();
1241
1247
  var progress = adVideoElement.currentTime / currentAd.duration;
1242
1248
  if (progress >= 0.25 && !trackingFired.firstQuartile) {
1243
1249
  trackingFired.firstQuartile = true;
@@ -1253,6 +1259,7 @@ function createHlsAdPlayer(contentVideo, options) {
1253
1259
  }
1254
1260
  });
1255
1261
  adVideoElement.addEventListener("playing", function() {
1262
+ startStallWatchdog();
1256
1263
  if (!currentAd || trackingFired.start) return;
1257
1264
  trackingFired.start = true;
1258
1265
  fireTrackingPixels(currentAd.trackingUrls.start);
@@ -1393,8 +1400,52 @@ function createHlsAdPlayer(contentVideo, options) {
1393
1400
  return false;
1394
1401
  }
1395
1402
  }
1403
+ function clearStallWatchdog() {
1404
+ if (stallWatchdogId != null) {
1405
+ clearInterval(stallWatchdogId);
1406
+ stallWatchdogId = void 0;
1407
+ }
1408
+ }
1409
+ function noteAdProgress() {
1410
+ lastAdProgressTime = Date.now();
1411
+ if (adVideoElement) {
1412
+ lastAdProgressPosition = adVideoElement.currentTime;
1413
+ }
1414
+ }
1415
+ function handleAdStall() {
1416
+ console.warn("[HlsAdPlayer] Ad playback stalled (no progress) - recovering to avoid a blank frame");
1417
+ clearStallWatchdog();
1418
+ if (currentAd) {
1419
+ fireTrackingPixels(currentAd.trackingUrls.error);
1420
+ }
1421
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1422
+ return;
1423
+ }
1424
+ handleAdError();
1425
+ }
1426
+ function startStallWatchdog() {
1427
+ clearStallWatchdog();
1428
+ noteAdProgress();
1429
+ stallWatchdogId = window.setInterval(function() {
1430
+ if (!adPlaying || !adVideoElement) {
1431
+ return;
1432
+ }
1433
+ if (adVideoElement.currentTime > lastAdProgressPosition + 0.05) {
1434
+ noteAdProgress();
1435
+ return;
1436
+ }
1437
+ if (adVideoElement.paused) {
1438
+ noteAdProgress();
1439
+ return;
1440
+ }
1441
+ if (Date.now() - lastAdProgressTime >= STALL_TIMEOUT_MS) {
1442
+ handleAdStall();
1443
+ }
1444
+ }, STALL_CHECK_INTERVAL_MS);
1445
+ }
1396
1446
  function handleAdComplete() {
1397
1447
  console.log("[HlsAdPlayer] Handling ad completion");
1448
+ clearStallWatchdog();
1398
1449
  adPlaying = false;
1399
1450
  setAdPlayingFlag(false);
1400
1451
  contentVideo.muted = true;
@@ -1402,6 +1453,10 @@ function createHlsAdPlayer(contentVideo, options) {
1402
1453
  adContainerEl.style.display = "none";
1403
1454
  adContainerEl.style.pointerEvents = "none";
1404
1455
  }
1456
+ if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
1457
+ contentVideo.style.visibility = "visible";
1458
+ contentVideo.style.opacity = "1";
1459
+ }
1405
1460
  if (options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) {
1406
1461
  if (contentVideo.paused) {
1407
1462
  console.log("[HlsAdPlayer] Content video paused in live mode, resuming playback");
@@ -1414,6 +1469,7 @@ function createHlsAdPlayer(contentVideo, options) {
1414
1469
  }
1415
1470
  function handleAdError() {
1416
1471
  console.log("[HlsAdPlayer] Handling ad error");
1472
+ clearStallWatchdog();
1417
1473
  adPlaying = false;
1418
1474
  setAdPlayingFlag(false);
1419
1475
  contentVideo.muted = true;
@@ -1421,6 +1477,10 @@ function createHlsAdPlayer(contentVideo, options) {
1421
1477
  adContainerEl.style.display = "none";
1422
1478
  adContainerEl.style.pointerEvents = "none";
1423
1479
  }
1480
+ if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
1481
+ contentVideo.style.visibility = "visible";
1482
+ contentVideo.style.opacity = "1";
1483
+ }
1424
1484
  emit("ad_error");
1425
1485
  }
1426
1486
  return {
@@ -1618,6 +1678,7 @@ function createHlsAdPlayer(contentVideo, options) {
1618
1678
  var previousMutedState;
1619
1679
  return _ts_generator(this, function(_state) {
1620
1680
  console.log("[HlsAdPlayer] Stopping ad");
1681
+ clearStallWatchdog();
1621
1682
  adPlaying = false;
1622
1683
  setAdPlayingFlag(false);
1623
1684
  previousMutedState = contentVideo.muted;
@@ -1656,6 +1717,7 @@ function createHlsAdPlayer(contentVideo, options) {
1656
1717
  destroy: function destroy() {
1657
1718
  console.log("[HlsAdPlayer] Destroying");
1658
1719
  destroyed = true;
1720
+ clearStallWatchdog();
1659
1721
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1660
1722
  try {
1661
1723
  for(var _iterator = pendingTimeouts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
@@ -4071,6 +4133,7 @@ function getBrowserConfigOverrides() {
4071
4133
  var overrides = {};
4072
4134
  if (browser.isSmartTV) {
4073
4135
  overrides.allowNativeHls = true;
4136
+ overrides.pauseContentDuringAds = true;
4074
4137
  }
4075
4138
  return overrides;
4076
4139
  }
@@ -5248,6 +5311,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
5248
5311
  ];
5249
5312
  case 4:
5250
5313
  data = _state.sent();
5314
+ this.applyMqttConfigFromResponse(data);
5251
5315
  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;
5252
5316
  if (imaPayload) {
5253
5317
  decodedPayload = imaPayload;
@@ -5281,6 +5345,20 @@ var AdConfigManager = /*#__PURE__*/ function() {
5281
5345
  }).call(this);
5282
5346
  }
5283
5347
  },
5348
+ {
5349
+ key: "applyMqttConfigFromResponse",
5350
+ value: function applyMqttConfigFromResponse(data) {
5351
+ var _data_response;
5352
+ var mqttConfig2 = (_data_response = data.response) === null || _data_response === void 0 ? void 0 : _data_response.mqtt_config;
5353
+ if (!mqttConfig2 || (typeof mqttConfig2 === "undefined" ? "undefined" : _type_of(mqttConfig2)) !== "object") {
5354
+ return;
5355
+ }
5356
+ applyMQTTConfig(mqttConfig2);
5357
+ if (this.debug) {
5358
+ console.log("[StormcloudVideoPlayer] Applied MQTT config from /ads/web:", mqttConfig2);
5359
+ }
5360
+ }
5361
+ },
5284
5362
  {
5285
5363
  key: "fetchAndParseVmap",
5286
5364
  value: function fetchAndParseVmap(vmapUrl) {
@@ -6976,12 +7054,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6976
7054
  // ───────────────────────────────────────────────────────────────
6977
7055
  // IMA event listeners
6978
7056
  // ───────────────────────────────────────────────────────────────
6979
- /**
6980
- * Produces the next pod ad-request URL for the current break. Every request
6981
- * (initial and continuous-fetch top-up) carries pod macros (pmad/pmnd/pmxd,
6982
- * no ppos); pmxd tracks the remaining break time so top-up pods only ask for
6983
- * what still fits.
6984
- */ key: "nextAdRequestUrl",
7057
+ key: "nextAdRequestUrl",
6985
7058
  value: function nextAdRequestUrl(baseVastUrl) {
6986
7059
  var remaining = Math.min(this.timing.getWallClockRemainingAdMs(), this.timing.getDurationBudgetRemainingMs());
6987
7060
  var breakDurationMs = remaining > 0 ? remaining : void 0;
@@ -8110,7 +8183,9 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8110
8183
  if (Math.abs(this.host.video.volume - restoredVolume) > 0.01) {
8111
8184
  this.host.video.volume = restoredVolume;
8112
8185
  }
8113
- if (this.host.shouldContinueLiveStreamDuringAds()) {
8186
+ this.host.video.style.visibility = "visible";
8187
+ this.host.video.style.opacity = "1";
8188
+ if (this.host.isLiveStream()) {
8114
8189
  var liveSyncPos = this.host.getLiveSyncPosition();
8115
8190
  if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {
8116
8191
  if (this.debug) {
@@ -8118,16 +8193,39 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8118
8193
  }
8119
8194
  this.host.video.currentTime = liveSyncPos;
8120
8195
  }
8121
- if (this.host.video.paused) {
8122
- var _this_host_video_play;
8123
- if (this.debug) console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
8124
- (_this_host_video_play = this.host.video.play()) === null || _this_host_video_play === void 0 ? void 0 : _this_host_video_play.catch(function() {});
8125
- } else {
8126
- if (this.debug) console.log("[StormcloudVideoPlayer] Content video already playing in live mode after ads");
8196
+ }
8197
+ this.resumeContentPlayback();
8198
+ }
8199
+ },
8200
+ {
8201
+ key: "resumeContentPlayback",
8202
+ value: function resumeContentPlayback() {
8203
+ var _this = this;
8204
+ var attempt = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 0;
8205
+ var maxAttempts = 3;
8206
+ if (!this.host.video.paused) {
8207
+ if (this.debug && attempt === 0) {
8208
+ console.log("[StormcloudVideoPlayer] Content video already playing after ads");
8127
8209
  }
8128
- } else if (this.host.video.paused) {
8129
- var _this_host_video_play1;
8130
- (_this_host_video_play1 = this.host.video.play()) === null || _this_host_video_play1 === void 0 ? void 0 : _this_host_video_play1.catch(function() {});
8210
+ return;
8211
+ }
8212
+ if (this.debug) {
8213
+ console.log("[StormcloudVideoPlayer] Resuming content playback after ads (attempt ".concat(attempt + 1, "/").concat(maxAttempts, ")"));
8214
+ }
8215
+ var playResult = this.host.video.play();
8216
+ if (playResult && typeof playResult.catch === "function") {
8217
+ playResult.catch(function(error) {
8218
+ if (attempt + 1 >= maxAttempts) {
8219
+ if (_this.debug) {
8220
+ console.warn("[StormcloudVideoPlayer] Failed to resume content playback after ads:", error);
8221
+ }
8222
+ return;
8223
+ }
8224
+ var backoffMs = 250 * (attempt + 1);
8225
+ window.setTimeout(function() {
8226
+ _this.resumeContentPlayback(attempt + 1);
8227
+ }, backoffMs);
8228
+ });
8131
8229
  }
8132
8230
  }
8133
8231
  },
@@ -8241,6 +8339,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8241
8339
  shouldContinueLiveStreamDuringAds: function shouldContinueLiveStreamDuringAds() {
8242
8340
  return _this.shouldContinueLiveStreamDuringAds();
8243
8341
  },
8342
+ isLiveStream: function isLiveStream() {
8343
+ return _this.hlsEngine.isLiveStream;
8344
+ },
8244
8345
  getLiveSyncPosition: function getLiveSyncPosition() {
8245
8346
  return _this.hlsEngine.getLiveSyncPosition();
8246
8347
  },
@@ -8463,6 +8564,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8463
8564
  {
8464
8565
  key: "shouldContinueLiveStreamDuringAds",
8465
8566
  value: function shouldContinueLiveStreamDuringAds() {
8567
+ if (this.config.pauseContentDuringAds) {
8568
+ return false;
8569
+ }
8466
8570
  if (this.hlsEngine.isLiveStream) {
8467
8571
  return true;
8468
8572
  }