stormcloud-video-player 0.8.33 → 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 (44) hide show
  1. package/dist/stormcloud-vp.min.js +1 -1
  2. package/lib/index.cjs +143 -19
  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 +143 -19
  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 +36 -1
  12. package/lib/player/AdConfigManager.cjs.map +1 -1
  13. package/lib/player/AdConfigManager.d.cts +1 -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 +143 -19
  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 +143 -19
  24. package/lib/players/HlsPlayer.cjs.map +1 -1
  25. package/lib/players/HlsPlayer.d.cts +1 -1
  26. package/lib/players/index.cjs +143 -19
  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/sdk/pal.cjs +38 -2
  32. package/lib/sdk/pal.cjs.map +1 -1
  33. package/lib/{types-CSHvCbhZ.d.cts → types-cTqIKw_D.d.cts} +1 -0
  34. package/lib/ui/StormcloudVideoPlayer.cjs +143 -19
  35. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  36. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
  37. package/lib/utils/browserCompat.cjs +1 -0
  38. package/lib/utils/browserCompat.cjs.map +1 -1
  39. package/lib/utils/browserCompat.d.cts +1 -0
  40. package/lib/utils/devUrl.cjs +102 -0
  41. package/lib/utils/devUrl.cjs.map +1 -0
  42. package/lib/utils/devUrl.d.cts +4 -0
  43. package/lib/utils/tracking.d.cts +1 -1
  44. 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){
@@ -1613,6 +1675,41 @@ function createHlsAdPlayer(contentVideo, options) {
1613
1675
  }
1614
1676
  };
1615
1677
  }
1678
+ // src/utils/devUrl.ts
1679
+ var PRIVATE_HOST_PATTERNS = [
1680
+ /^localhost$/i,
1681
+ /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,
1682
+ /^0\.0\.0\.0$/,
1683
+ /^::1$/,
1684
+ /^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,
1685
+ /^192\.168\.\d{1,3}\.\d{1,3}$/,
1686
+ /^172\.(1[6-9]|2\d|3[0-1])\.\d{1,3}\.\d{1,3}$/
1687
+ ];
1688
+ function isLocalDevHost(hostname) {
1689
+ var host = hostname.replace(/^\[/, "").replace(/\]$/, "").toLowerCase();
1690
+ if (!host) return false;
1691
+ if (host.endsWith(".local") || host.endsWith(".localhost")) return true;
1692
+ return PRIVATE_HOST_PATTERNS.some(function(pattern) {
1693
+ return pattern.test(host);
1694
+ });
1695
+ }
1696
+ function toDevSafeUrl(rawUrl) {
1697
+ if (!rawUrl) return rawUrl !== null && rawUrl !== void 0 ? rawUrl : "";
1698
+ var url;
1699
+ try {
1700
+ url = new URL(rawUrl);
1701
+ } catch (unused) {
1702
+ return rawUrl;
1703
+ }
1704
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
1705
+ return rawUrl;
1706
+ }
1707
+ if (!isLocalDevHost(url.hostname)) {
1708
+ return rawUrl;
1709
+ }
1710
+ var cleanPath = url.pathname.replace(/\/{2,}/g, "/") || "/index.html";
1711
+ return "file://".concat(cleanPath);
1712
+ }
1616
1713
  // src/sdk/pal.ts
1617
1714
  var PAL_SDK_URL = "https://imasdk.googleapis.com/pal/sdkloader/pal.js";
1618
1715
  var _nonceLoader;
@@ -1699,12 +1796,12 @@ function createPalNonceManager() {
1699
1796
  request.adWillPlayMuted = (_options_adWillPlayMuted = options.adWillPlayMuted) !== null && _options_adWillPlayMuted !== void 0 ? _options_adWillPlayMuted : false;
1700
1797
  request.playerVolume = (_options_playerVolume = options.playerVolume) !== null && _options_playerVolume !== void 0 ? _options_playerVolume : request.adWillPlayMuted ? 0 : 1;
1701
1798
  request.continuousPlayback = (_options_continuousPlayback = options.continuousPlayback) !== null && _options_continuousPlayback !== void 0 ? _options_continuousPlayback : true;
1702
- request.descriptionUrl = options.descriptionUrl || (typeof window !== "undefined" ? window.location.href : "");
1799
+ request.descriptionUrl = toDevSafeUrl(options.descriptionUrl || (typeof window !== "undefined" ? window.location.href : ""));
1703
1800
  request.iconsSupported = true;
1704
1801
  request.playerType = "StormcloudVideoPlayer";
1705
1802
  request.playerVersion = "1.0";
1706
1803
  request.supportedApiFrameworks = "2,7,9";
1707
- request.url = options.descriptionUrl || (typeof window !== "undefined" ? window.location.href : "");
1804
+ request.url = toDevSafeUrl(options.descriptionUrl || (typeof window !== "undefined" ? window.location.href : ""));
1708
1805
  request.videoWidth = options.videoWidth || 640;
1709
1806
  request.videoHeight = options.videoHeight || 480;
1710
1807
  if (options.ppid) request.ppid = options.ppid;
@@ -3825,6 +3922,7 @@ function getBrowserConfigOverrides() {
3825
3922
  var overrides = {};
3826
3923
  if (browser.isSmartTV) {
3827
3924
  overrides.allowNativeHls = true;
3925
+ overrides.pauseContentDuringAds = true;
3828
3926
  }
3829
3927
  return overrides;
3830
3928
  }
@@ -5323,7 +5421,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
5323
5421
  podMaxDurationMs: podParams.maxDurationMs,
5324
5422
  isCtv: envSignals.isCtv,
5325
5423
  contentUrl: envSignals.contentUrl,
5326
- pageUrl: !envSignals.isCtv && typeof window !== "undefined" ? window.location.href : void 0,
5424
+ pageUrl: !envSignals.isCtv && typeof window !== "undefined" ? toDevSafeUrl(window.location.href) : void 0,
5327
5425
  adWillPlayMuted: adWillPlayMuted,
5328
5426
  adWillAutoPlay: !!this.config.autoplay,
5329
5427
  appId: envSignals.appId,
@@ -6707,12 +6805,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6707
6805
  // ───────────────────────────────────────────────────────────────
6708
6806
  // IMA event listeners
6709
6807
  // ───────────────────────────────────────────────────────────────
6710
- /**
6711
- * Produces the next pod ad-request URL for the current break. Every request
6712
- * (initial and continuous-fetch top-up) carries pod macros (pmad/pmnd/pmxd,
6713
- * no ppos); pmxd tracks the remaining break time so top-up pods only ask for
6714
- * what still fits.
6715
- */ key: "nextAdRequestUrl",
6808
+ key: "nextAdRequestUrl",
6716
6809
  value: function nextAdRequestUrl(baseVastUrl) {
6717
6810
  var remaining = Math.min(this.timing.getWallClockRemainingAdMs(), this.timing.getDurationBudgetRemainingMs());
6718
6811
  var breakDurationMs = remaining > 0 ? remaining : void 0;
@@ -7841,7 +7934,9 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7841
7934
  if (Math.abs(this.host.video.volume - restoredVolume) > 0.01) {
7842
7935
  this.host.video.volume = restoredVolume;
7843
7936
  }
7844
- if (this.host.shouldContinueLiveStreamDuringAds()) {
7937
+ this.host.video.style.visibility = "visible";
7938
+ this.host.video.style.opacity = "1";
7939
+ if (this.host.isLiveStream()) {
7845
7940
  var liveSyncPos = this.host.getLiveSyncPosition();
7846
7941
  if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {
7847
7942
  if (this.debug) {
@@ -7849,16 +7944,39 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7849
7944
  }
7850
7945
  this.host.video.currentTime = liveSyncPos;
7851
7946
  }
7852
- if (this.host.video.paused) {
7853
- var _this_host_video_play;
7854
- if (this.debug) console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
7855
- (_this_host_video_play = this.host.video.play()) === null || _this_host_video_play === void 0 ? void 0 : _this_host_video_play.catch(function() {});
7856
- } else {
7857
- if (this.debug) console.log("[StormcloudVideoPlayer] Content video already playing in live mode after ads");
7947
+ }
7948
+ this.resumeContentPlayback();
7949
+ }
7950
+ },
7951
+ {
7952
+ key: "resumeContentPlayback",
7953
+ value: function resumeContentPlayback() {
7954
+ var _this = this;
7955
+ var attempt = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 0;
7956
+ var maxAttempts = 3;
7957
+ if (!this.host.video.paused) {
7958
+ if (this.debug && attempt === 0) {
7959
+ console.log("[StormcloudVideoPlayer] Content video already playing after ads");
7858
7960
  }
7859
- } else if (this.host.video.paused) {
7860
- var _this_host_video_play1;
7861
- (_this_host_video_play1 = this.host.video.play()) === null || _this_host_video_play1 === void 0 ? void 0 : _this_host_video_play1.catch(function() {});
7961
+ return;
7962
+ }
7963
+ if (this.debug) {
7964
+ console.log("[StormcloudVideoPlayer] Resuming content playback after ads (attempt ".concat(attempt + 1, "/").concat(maxAttempts, ")"));
7965
+ }
7966
+ var playResult = this.host.video.play();
7967
+ if (playResult && typeof playResult.catch === "function") {
7968
+ playResult.catch(function(error) {
7969
+ if (attempt + 1 >= maxAttempts) {
7970
+ if (_this.debug) {
7971
+ console.warn("[StormcloudVideoPlayer] Failed to resume content playback after ads:", error);
7972
+ }
7973
+ return;
7974
+ }
7975
+ var backoffMs = 250 * (attempt + 1);
7976
+ window.setTimeout(function() {
7977
+ _this.resumeContentPlayback(attempt + 1);
7978
+ }, backoffMs);
7979
+ });
7862
7980
  }
7863
7981
  }
7864
7982
  },
@@ -7972,6 +8090,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7972
8090
  shouldContinueLiveStreamDuringAds: function shouldContinueLiveStreamDuringAds() {
7973
8091
  return _this.shouldContinueLiveStreamDuringAds();
7974
8092
  },
8093
+ isLiveStream: function isLiveStream() {
8094
+ return _this.hlsEngine.isLiveStream;
8095
+ },
7975
8096
  getLiveSyncPosition: function getLiveSyncPosition() {
7976
8097
  return _this.hlsEngine.getLiveSyncPosition();
7977
8098
  },
@@ -8194,6 +8315,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8194
8315
  {
8195
8316
  key: "shouldContinueLiveStreamDuringAds",
8196
8317
  value: function shouldContinueLiveStreamDuringAds() {
8318
+ if (this.config.pauseContentDuringAds) {
8319
+ return false;
8320
+ }
8197
8321
  if (this.hlsEngine.isLiveStream) {
8198
8322
  return true;
8199
8323
  }