stormcloud-video-player 0.8.21 → 0.8.23

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.
package/lib/index.cjs CHANGED
@@ -2539,6 +2539,18 @@ var AIRY_ROKU_APP_ID = "com.freeairytv.roku";
2539
2539
  var AIRY_APP_NAME = "AiryTV Movies & TV";
2540
2540
  var AIRY_DEFAULT_CONTENT_URL = "https://live.airy.tv";
2541
2541
  var CTV_SESSION_STORAGE_KEY = "stormcloud.ctv.sid";
2542
+ var LG_TEST_APP_ID = "com.hulu.plus";
2543
+ var LG_TEST_APP_NAME = "Hulu";
2544
+ var LG_TEST_CONTENT_URL = "https://www.hulu.com/live-tv";
2545
+ var _lgTestDeviceId;
2546
+ function getLgTestDeviceId() {
2547
+ if (!_lgTestDeviceId) _lgTestDeviceId = createUuid();
2548
+ return _lgTestDeviceId;
2549
+ }
2550
+ function isLgWebOS() {
2551
+ if (typeof navigator === "undefined") return false;
2552
+ return /Web0S|webOS|LG Browser|LGSTB|LGE/i.test(navigator.userAgent);
2553
+ }
2542
2554
  function getDefaultAppId() {
2543
2555
  if (typeof navigator === "undefined") return AIRY_ANDROID_APP_ID;
2544
2556
  var ua = navigator.userAgent;
@@ -2564,17 +2576,20 @@ var ANDROID_ID_TYPES = /* @__PURE__ */ new Set([
2564
2576
  "adid"
2565
2577
  ]);
2566
2578
  function resolveVastEnvironmentSignals(isCtv) {
2567
- var _ref, _bridgeSignals_limitAdTracking;
2579
+ var _ref;
2568
2580
  var bridgeSignals = readNativeBridgeSignals();
2569
- var deviceId = bridgeSignals.deviceId || readStoredDeviceId();
2570
- var deviceIdType = bridgeSignals.deviceIdType || readStoredString("deviceIdType") || readStoredString("idtype") || inferDeviceIdType();
2571
- var limitAdTracking = deviceId != null ? (_ref = (_bridgeSignals_limitAdTracking = bridgeSignals.limitAdTracking) !== null && _bridgeSignals_limitAdTracking !== void 0 ? _bridgeSignals_limitAdTracking : readStoredLimitAdTracking()) !== null && _ref !== void 0 ? _ref : false : void 0;
2581
+ var platformDevice = readPlatformNativeDeviceId();
2582
+ var lg = isCtv && isLgWebOS();
2583
+ var deviceId = bridgeSignals.deviceId || readStoredDeviceId() || platformDevice.deviceId || (lg && platformDevice.limitAdTracking !== true ? getLgTestDeviceId() : void 0);
2584
+ var deviceIdType = bridgeSignals.deviceIdType || readStoredString("deviceIdType") || readStoredString("idtype") || platformDevice.deviceIdType || inferDeviceIdType() || (lg ? "lgudid" : void 0);
2585
+ var platformLat = bridgeSignals.deviceId ? bridgeSignals.limitAdTracking : platformDevice.limitAdTracking;
2586
+ var limitAdTracking = deviceId != null ? (_ref = platformLat !== null && platformLat !== void 0 ? platformLat : readStoredLimitAdTracking()) !== null && _ref !== void 0 ? _ref : false : void 0;
2572
2587
  if (isCtv) {
2573
2588
  return _object_spread_props(_object_spread({
2574
2589
  isCtv: true,
2575
- contentUrl: bridgeSignals.contentUrl || readStoredString("contentUrl") || AIRY_DEFAULT_CONTENT_URL,
2576
- appId: bridgeSignals.appId || readStoredString("appId") || readStoredString("msid") || getDefaultAppId(),
2577
- appName: bridgeSignals.appName || readStoredString("appName") || readStoredString("an") || AIRY_APP_NAME,
2590
+ contentUrl: bridgeSignals.contentUrl || readStoredString("contentUrl") || (lg ? LG_TEST_CONTENT_URL : AIRY_DEFAULT_CONTENT_URL),
2591
+ appId: bridgeSignals.appId || readStoredString("appId") || readStoredString("msid") || (lg ? LG_TEST_APP_ID : getDefaultAppId()),
2592
+ appName: bridgeSignals.appName || readStoredString("appName") || readStoredString("an") || (lg ? LG_TEST_APP_NAME : AIRY_APP_NAME),
2578
2593
  sessionId: getOrCreateCtvSessionId()
2579
2594
  }, deviceId ? {
2580
2595
  deviceId: deviceId
@@ -2639,6 +2654,72 @@ function createUuid() {
2639
2654
  return nibble.toString(16);
2640
2655
  });
2641
2656
  }
2657
+ function readPlatformNativeDeviceId() {
2658
+ if (typeof window === "undefined") return {};
2659
+ try {
2660
+ var _window_webapis;
2661
+ var adinfo = (_window_webapis = window.webapis) === null || _window_webapis === void 0 ? void 0 : _window_webapis.adinfo;
2662
+ if (adinfo) {
2663
+ var tifa = typeof adinfo.getTIFA === "function" ? adinfo.getTIFA() : adinfo.tifa;
2664
+ if (typeof tifa === "string" && tifa && !isMacroPlaceholder(tifa)) {
2665
+ var lat = typeof adinfo.isLATEnabled === "function" ? adinfo.isLATEnabled() : void 0;
2666
+ return _object_spread({
2667
+ deviceId: tifa,
2668
+ deviceIdType: "tifa"
2669
+ }, typeof lat === "boolean" ? {
2670
+ limitAdTracking: lat
2671
+ } : {});
2672
+ }
2673
+ }
2674
+ } catch (unused) {}
2675
+ try {
2676
+ var webOSDev = window.webOSDev;
2677
+ if (webOSDev) {
2678
+ var _webOSDev_lgudid;
2679
+ var lgudid = typeof webOSDev.LGUDID === "function" ? webOSDev.LGUDID() : (_webOSDev_lgudid = webOSDev.lgudid) !== null && _webOSDev_lgudid !== void 0 ? _webOSDev_lgudid : webOSDev.LGUDID;
2680
+ if (typeof lgudid === "string" && lgudid) {
2681
+ if (isZeroedAdId(lgudid)) {
2682
+ return {
2683
+ deviceIdType: "lgudid",
2684
+ limitAdTracking: true
2685
+ };
2686
+ }
2687
+ if (!isMacroPlaceholder(lgudid)) {
2688
+ return {
2689
+ deviceId: lgudid,
2690
+ deviceIdType: "lgudid",
2691
+ limitAdTracking: false
2692
+ };
2693
+ }
2694
+ }
2695
+ }
2696
+ } catch (unused) {}
2697
+ try {
2698
+ var roku = window.Roku;
2699
+ if (roku) {
2700
+ var rida = typeof roku.getPublisherUniqueId === "function" ? roku.getPublisherUniqueId() : roku.rida;
2701
+ if (typeof rida === "string" && rida && !isMacroPlaceholder(rida)) {
2702
+ return {
2703
+ deviceId: rida,
2704
+ deviceIdType: "rida",
2705
+ limitAdTracking: false
2706
+ };
2707
+ }
2708
+ }
2709
+ } catch (unused) {}
2710
+ return {};
2711
+ }
2712
+ var ZEROED_AD_ID_PATTERNS = /* @__PURE__ */ new Set([
2713
+ "38400000-8cf0-11bd-b23e-10b96e40000d",
2714
+ "00000000-0000-0000-0000-000000000000"
2715
+ ]);
2716
+ function isMacroPlaceholder(value) {
2717
+ var t = value.trim();
2718
+ return !t || /^(\[[^\]]*\]|\{[^}]*\}|%%[^%]*%%)$/.test(t) || /^(unknown|null|undefined|none|n\/a|\$\{[^}]+\})$/i.test(t) || ZEROED_AD_ID_PATTERNS.has(t.toLowerCase());
2719
+ }
2720
+ function isZeroedAdId(value) {
2721
+ return ZEROED_AD_ID_PATTERNS.has(value.trim().toLowerCase());
2722
+ }
2642
2723
  function readNativeBridgeSignals() {
2643
2724
  if (typeof window === "undefined") {
2644
2725
  return {};
@@ -2979,7 +3060,7 @@ var INLINE_DEVICE_MACRO_REPLACEMENTS = [
2979
3060
  }
2980
3061
  }
2981
3062
  ];
2982
- function isMacroPlaceholder(value) {
3063
+ function isMacroPlaceholder2(value) {
2983
3064
  if (value == null) return true;
2984
3065
  var trimmed = value.trim();
2985
3066
  if (!trimmed) return true;
@@ -3038,11 +3119,11 @@ function applyDeviceIdentityParams(params, ctx) {
3038
3119
  if (runtimeId) {
3039
3120
  params.set("rdid", runtimeId);
3040
3121
  params.set("idtype", runtimeType || tagType || "aaid");
3041
- params.set("is_lat", latValue !== null && latValue !== void 0 ? latValue : isMacroPlaceholder(tagLat) ? "0" : tagLat);
3122
+ params.set("is_lat", latValue !== null && latValue !== void 0 ? latValue : isMacroPlaceholder2(tagLat) ? "0" : tagLat);
3042
3123
  return;
3043
3124
  }
3044
- var tagRdidValid = Boolean(tagRdid && !isMacroPlaceholder(tagRdid));
3045
- var tagTypeValid = Boolean(tagType && !isMacroPlaceholder(tagType));
3125
+ var tagRdidValid = Boolean(tagRdid && !isMacroPlaceholder2(tagRdid));
3126
+ var tagTypeValid = Boolean(tagType && !isMacroPlaceholder2(tagType));
3046
3127
  if (tagRdidValid && tagTypeValid && runtimeType && !isEquivalentDeviceIdType(tagType, runtimeType)) {
3047
3128
  params.delete("rdid");
3048
3129
  params.delete("idtype");
@@ -3050,10 +3131,10 @@ function applyDeviceIdentityParams(params, ctx) {
3050
3131
  return;
3051
3132
  }
3052
3133
  if (tagRdidValid) {
3053
- if (isMacroPlaceholder(tagType) && runtimeType) {
3134
+ if (isMacroPlaceholder2(tagType) && runtimeType) {
3054
3135
  params.set("idtype", runtimeType);
3055
3136
  }
3056
- if (isMacroPlaceholder(tagLat)) {
3137
+ if (isMacroPlaceholder2(tagLat)) {
3057
3138
  params.set("is_lat", latValue !== null && latValue !== void 0 ? latValue : "0");
3058
3139
  }
3059
3140
  return;
@@ -3239,7 +3320,7 @@ function applyVastMacros(baseUrl, ctx) {
3239
3320
  }
3240
3321
  var staleKeys = [];
3241
3322
  params.forEach(function(value, key) {
3242
- if (isMacroPlaceholder(value)) {
3323
+ if (isMacroPlaceholder2(value)) {
3243
3324
  staleKeys.push(key);
3244
3325
  }
3245
3326
  });
@@ -6211,7 +6292,7 @@ var HlsEngine = /*#__PURE__*/ function() {
6211
6292
  key: "setupNativeHls",
6212
6293
  value: function setupNativeHls() {
6213
6294
  return _async_to_generator(function() {
6214
- var _this_config_isLiveStream, _this_config_lowLatencyMode, _this_video_play;
6295
+ var _this_config_isLiveStream, _this_config_lowLatencyMode, adBehavior, _this_video_play;
6215
6296
  return _ts_generator(this, function(_state) {
6216
6297
  switch(_state.label){
6217
6298
  case 0:
@@ -6220,10 +6301,11 @@ var HlsEngine = /*#__PURE__*/ function() {
6220
6301
  this.video.src = this.config.src;
6221
6302
  this.isLiveStream = (_this_config_isLiveStream = this.config.isLiveStream) !== null && _this_config_isLiveStream !== void 0 ? _this_config_isLiveStream : (_this_config_lowLatencyMode = this.config.lowLatencyMode) !== null && _this_config_lowLatencyMode !== void 0 ? _this_config_lowLatencyMode : false;
6222
6303
  if (this.debug) {
6223
- console.log("[StormcloudVideoPlayer] Using native HLS playback - VOD mode:", {
6304
+ adBehavior = this.isLiveStream ? "live (main video continues muted during ads)" : "vod (main video pauses during ads)";
6305
+ console.log("[StormcloudVideoPlayer] Using native HLS playback:", {
6224
6306
  isLive: this.isLiveStream,
6225
6307
  allowNativeHls: this.config.allowNativeHls,
6226
- adBehavior: "vod (main video pauses during ads)"
6308
+ adBehavior: adBehavior
6227
6309
  });
6228
6310
  }
6229
6311
  if (!this.config.autoplay) return [
@@ -6632,6 +6714,14 @@ var HlsEngine = /*#__PURE__*/ function() {
6632
6714
  });
6633
6715
  }
6634
6716
  },
6717
+ {
6718
+ key: "getLiveSyncPosition",
6719
+ value: function getLiveSyncPosition() {
6720
+ var _this_hls;
6721
+ var pos = (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.liveSyncPosition;
6722
+ return typeof pos === "number" && Number.isFinite(pos) ? pos : void 0;
6723
+ }
6724
+ },
6635
6725
  {
6636
6726
  key: "destroy",
6637
6727
  value: function destroy() {
@@ -8206,6 +8296,13 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8206
8296
  this.host.video.volume = restoredVolume;
8207
8297
  }
8208
8298
  if (this.host.shouldContinueLiveStreamDuringAds()) {
8299
+ var liveSyncPos = this.host.getLiveSyncPosition();
8300
+ if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {
8301
+ if (this.debug) {
8302
+ console.log("[StormcloudVideoPlayer] Live break end — snapping to live edge: ".concat(this.host.video.currentTime.toFixed(2), "s → ").concat(liveSyncPos.toFixed(2), "s"));
8303
+ }
8304
+ this.host.video.currentTime = liveSyncPos;
8305
+ }
8209
8306
  if (this.host.video.paused) {
8210
8307
  var _this_host_video_play;
8211
8308
  if (this.debug) console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
@@ -8348,6 +8445,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8348
8445
  shouldContinueLiveStreamDuringAds: function shouldContinueLiveStreamDuringAds() {
8349
8446
  return _this.shouldContinueLiveStreamDuringAds();
8350
8447
  },
8448
+ getLiveSyncPosition: function getLiveSyncPosition() {
8449
+ return _this.hlsEngine.getLiveSyncPosition();
8450
+ },
8351
8451
  generateVastUrls: function generateVastUrls(base, count) {
8352
8452
  return _this.generateVastUrls(base, count);
8353
8453
  }
@@ -8389,7 +8489,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8389
8489
  // ───────────────────────────────────────────────────────────────
8390
8490
  function load() {
8391
8491
  return _async_to_generator(function() {
8392
- var _this, _this_config_lowLatencyMode, error;
8492
+ var _this, _this_config_lowLatencyMode, error, continueLiveStreamDuringAds;
8393
8493
  return _ts_generator(this, function(_state) {
8394
8494
  switch(_state.label){
8395
8495
  case 0:
@@ -8443,15 +8543,22 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8443
8543
  3,
8444
8544
  6
8445
8545
  ];
8446
- this.adPlayer.destroy();
8447
- this.adPlayer = this.createAdPlayer(false);
8448
- this.adPlayer.initialize();
8449
8546
  return [
8450
8547
  4,
8451
8548
  this.hlsEngine.setupNativeHls()
8452
8549
  ];
8453
8550
  case 5:
8454
8551
  _state.sent();
8552
+ continueLiveStreamDuringAds = this.shouldContinueLiveStreamDuringAds();
8553
+ if (this.debug) {
8554
+ console.log("[StormcloudVideoPlayer] Native HLS ad behavior:", {
8555
+ isLive: this.hlsEngine.isLiveStream,
8556
+ continueLiveStreamDuringAds: continueLiveStreamDuringAds
8557
+ });
8558
+ }
8559
+ this.adPlayer.destroy();
8560
+ this.adPlayer = this.createAdPlayer(continueLiveStreamDuringAds);
8561
+ this.adPlayer.initialize();
8455
8562
  return [
8456
8563
  2
8457
8564
  ];
@@ -8559,9 +8666,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8559
8666
  {
8560
8667
  key: "shouldContinueLiveStreamDuringAds",
8561
8668
  value: function shouldContinueLiveStreamDuringAds() {
8562
- if (this.config.allowNativeHls) return false;
8563
- if (!this.hlsEngine.isLiveStream) return false;
8564
- return true;
8669
+ if (this.hlsEngine.isLiveStream) {
8670
+ return true;
8671
+ }
8672
+ var duration = this.video.duration;
8673
+ if (!Number.isFinite(duration) || duration === Infinity) {
8674
+ return true;
8675
+ }
8676
+ return false;
8565
8677
  }
8566
8678
  },
8567
8679
  {
@@ -8859,9 +8971,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8859
8971
  this.adBreak.showAds = true;
8860
8972
  this.adBreak.inAdBreak = true;
8861
8973
  this.timing.currentAdBreakStartWallClockMs = Date.now();
8862
- if (!this.video.paused) {
8863
- this.video.pause();
8864
- }
8865
8974
  _state.label = 1;
8866
8975
  case 1:
8867
8976
  _state.trys.push([
@@ -9235,7 +9344,8 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
9235
9344
  setShowSpeedMenu(false);
9236
9345
  };
9237
9346
  var isHlsStream = (src === null || src === void 0 ? void 0 : src.toLowerCase().includes(".m3u8")) || (src === null || src === void 0 ? void 0 : src.toLowerCase().includes("/hls/"));
9238
- var shouldShowEnhancedControls = showCustomControls && (isHlsStream ? allowNativeHls || isLiveStream === false : true);
9347
+ var isLiveUiMode = isLiveStream === true;
9348
+ var shouldShowEnhancedControls = showCustomControls && (isHlsStream ? !isLiveUiMode : true);
9239
9349
  var criticalPropsKey = (0, import_react.useMemo)(function() {
9240
9350
  return CRITICAL_PROPS.map(function(prop) {
9241
9351
  return "".concat(prop, ":").concat(props[prop]);
@@ -10615,6 +10725,7 @@ var HlsPlayer = /*#__PURE__*/ function(_import_react3_Component) {
10615
10725
  if (_this1.props.muted !== void 0) config.muted = _this1.props.muted;
10616
10726
  if (_this1.props.lowLatencyMode !== void 0) config.lowLatencyMode = _this1.props.lowLatencyMode;
10617
10727
  if (_this1.props.allowNativeHls !== void 0) config.allowNativeHls = _this1.props.allowNativeHls;
10728
+ if (_this1.props.isLiveStream !== void 0) config.isLiveStream = _this1.props.isLiveStream;
10618
10729
  if (_this1.props.driftToleranceMs !== void 0) config.driftToleranceMs = _this1.props.driftToleranceMs;
10619
10730
  if (_this1.props.immediateManifestAds !== void 0) config.immediateManifestAds = _this1.props.immediateManifestAds;
10620
10731
  if (_this1.props.debugAdTiming !== void 0) config.debugAdTiming = _this1.props.debugAdTiming;
@@ -11381,6 +11492,7 @@ var SUPPORTED_PROPS = [
11381
11492
  "wrapperStyle",
11382
11493
  "allowNativeHls",
11383
11494
  "lowLatencyMode",
11495
+ "isLiveStream",
11384
11496
  "driftToleranceMs",
11385
11497
  "immediateManifestAds",
11386
11498
  "debugAdTiming",