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
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
  }
@@ -6976,12 +7039,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6976
7039
  // ───────────────────────────────────────────────────────────────
6977
7040
  // IMA event listeners
6978
7041
  // ───────────────────────────────────────────────────────────────
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",
7042
+ key: "nextAdRequestUrl",
6985
7043
  value: function nextAdRequestUrl(baseVastUrl) {
6986
7044
  var remaining = Math.min(this.timing.getWallClockRemainingAdMs(), this.timing.getDurationBudgetRemainingMs());
6987
7045
  var breakDurationMs = remaining > 0 ? remaining : void 0;
@@ -8110,7 +8168,9 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8110
8168
  if (Math.abs(this.host.video.volume - restoredVolume) > 0.01) {
8111
8169
  this.host.video.volume = restoredVolume;
8112
8170
  }
8113
- if (this.host.shouldContinueLiveStreamDuringAds()) {
8171
+ this.host.video.style.visibility = "visible";
8172
+ this.host.video.style.opacity = "1";
8173
+ if (this.host.isLiveStream()) {
8114
8174
  var liveSyncPos = this.host.getLiveSyncPosition();
8115
8175
  if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {
8116
8176
  if (this.debug) {
@@ -8118,16 +8178,39 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8118
8178
  }
8119
8179
  this.host.video.currentTime = liveSyncPos;
8120
8180
  }
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");
8181
+ }
8182
+ this.resumeContentPlayback();
8183
+ }
8184
+ },
8185
+ {
8186
+ key: "resumeContentPlayback",
8187
+ value: function resumeContentPlayback() {
8188
+ var _this = this;
8189
+ var attempt = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 0;
8190
+ var maxAttempts = 3;
8191
+ if (!this.host.video.paused) {
8192
+ if (this.debug && attempt === 0) {
8193
+ console.log("[StormcloudVideoPlayer] Content video already playing after ads");
8127
8194
  }
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() {});
8195
+ return;
8196
+ }
8197
+ if (this.debug) {
8198
+ console.log("[StormcloudVideoPlayer] Resuming content playback after ads (attempt ".concat(attempt + 1, "/").concat(maxAttempts, ")"));
8199
+ }
8200
+ var playResult = this.host.video.play();
8201
+ if (playResult && typeof playResult.catch === "function") {
8202
+ playResult.catch(function(error) {
8203
+ if (attempt + 1 >= maxAttempts) {
8204
+ if (_this.debug) {
8205
+ console.warn("[StormcloudVideoPlayer] Failed to resume content playback after ads:", error);
8206
+ }
8207
+ return;
8208
+ }
8209
+ var backoffMs = 250 * (attempt + 1);
8210
+ window.setTimeout(function() {
8211
+ _this.resumeContentPlayback(attempt + 1);
8212
+ }, backoffMs);
8213
+ });
8131
8214
  }
8132
8215
  }
8133
8216
  },
@@ -8241,6 +8324,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8241
8324
  shouldContinueLiveStreamDuringAds: function shouldContinueLiveStreamDuringAds() {
8242
8325
  return _this.shouldContinueLiveStreamDuringAds();
8243
8326
  },
8327
+ isLiveStream: function isLiveStream() {
8328
+ return _this.hlsEngine.isLiveStream;
8329
+ },
8244
8330
  getLiveSyncPosition: function getLiveSyncPosition() {
8245
8331
  return _this.hlsEngine.getLiveSyncPosition();
8246
8332
  },
@@ -8463,6 +8549,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8463
8549
  {
8464
8550
  key: "shouldContinueLiveStreamDuringAds",
8465
8551
  value: function shouldContinueLiveStreamDuringAds() {
8552
+ if (this.config.pauseContentDuringAds) {
8553
+ return false;
8554
+ }
8466
8555
  if (this.hlsEngine.isLiveStream) {
8467
8556
  return true;
8468
8557
  }