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.
@@ -2317,6 +2317,18 @@ var AIRY_ROKU_APP_ID = "com.freeairytv.roku";
2317
2317
  var AIRY_APP_NAME = "AiryTV Movies & TV";
2318
2318
  var AIRY_DEFAULT_CONTENT_URL = "https://live.airy.tv";
2319
2319
  var CTV_SESSION_STORAGE_KEY = "stormcloud.ctv.sid";
2320
+ var LG_TEST_APP_ID = "com.hulu.plus";
2321
+ var LG_TEST_APP_NAME = "Hulu";
2322
+ var LG_TEST_CONTENT_URL = "https://www.hulu.com/live-tv";
2323
+ var _lgTestDeviceId;
2324
+ function getLgTestDeviceId() {
2325
+ if (!_lgTestDeviceId) _lgTestDeviceId = createUuid();
2326
+ return _lgTestDeviceId;
2327
+ }
2328
+ function isLgWebOS() {
2329
+ if (typeof navigator === "undefined") return false;
2330
+ return /Web0S|webOS|LG Browser|LGSTB|LGE/i.test(navigator.userAgent);
2331
+ }
2320
2332
  function getDefaultAppId() {
2321
2333
  if (typeof navigator === "undefined") return AIRY_ANDROID_APP_ID;
2322
2334
  var ua = navigator.userAgent;
@@ -2342,17 +2354,20 @@ var ANDROID_ID_TYPES = /* @__PURE__ */ new Set([
2342
2354
  "adid"
2343
2355
  ]);
2344
2356
  function resolveVastEnvironmentSignals(isCtv) {
2345
- var _ref, _bridgeSignals_limitAdTracking;
2357
+ var _ref;
2346
2358
  var bridgeSignals = readNativeBridgeSignals();
2347
- var deviceId = bridgeSignals.deviceId || readStoredDeviceId();
2348
- var deviceIdType = bridgeSignals.deviceIdType || readStoredString("deviceIdType") || readStoredString("idtype") || inferDeviceIdType();
2349
- 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;
2359
+ var platformDevice = readPlatformNativeDeviceId();
2360
+ var lg = isCtv && isLgWebOS();
2361
+ var deviceId = bridgeSignals.deviceId || readStoredDeviceId() || platformDevice.deviceId || (lg && platformDevice.limitAdTracking !== true ? getLgTestDeviceId() : void 0);
2362
+ var deviceIdType = bridgeSignals.deviceIdType || readStoredString("deviceIdType") || readStoredString("idtype") || platformDevice.deviceIdType || inferDeviceIdType() || (lg ? "lgudid" : void 0);
2363
+ var platformLat = bridgeSignals.deviceId ? bridgeSignals.limitAdTracking : platformDevice.limitAdTracking;
2364
+ var limitAdTracking = deviceId != null ? (_ref = platformLat !== null && platformLat !== void 0 ? platformLat : readStoredLimitAdTracking()) !== null && _ref !== void 0 ? _ref : false : void 0;
2350
2365
  if (isCtv) {
2351
2366
  return _object_spread_props(_object_spread({
2352
2367
  isCtv: true,
2353
- contentUrl: bridgeSignals.contentUrl || readStoredString("contentUrl") || AIRY_DEFAULT_CONTENT_URL,
2354
- appId: bridgeSignals.appId || readStoredString("appId") || readStoredString("msid") || getDefaultAppId(),
2355
- appName: bridgeSignals.appName || readStoredString("appName") || readStoredString("an") || AIRY_APP_NAME,
2368
+ contentUrl: bridgeSignals.contentUrl || readStoredString("contentUrl") || (lg ? LG_TEST_CONTENT_URL : AIRY_DEFAULT_CONTENT_URL),
2369
+ appId: bridgeSignals.appId || readStoredString("appId") || readStoredString("msid") || (lg ? LG_TEST_APP_ID : getDefaultAppId()),
2370
+ appName: bridgeSignals.appName || readStoredString("appName") || readStoredString("an") || (lg ? LG_TEST_APP_NAME : AIRY_APP_NAME),
2356
2371
  sessionId: getOrCreateCtvSessionId()
2357
2372
  }, deviceId ? {
2358
2373
  deviceId: deviceId
@@ -2417,6 +2432,72 @@ function createUuid() {
2417
2432
  return nibble.toString(16);
2418
2433
  });
2419
2434
  }
2435
+ function readPlatformNativeDeviceId() {
2436
+ if (typeof window === "undefined") return {};
2437
+ try {
2438
+ var _window_webapis;
2439
+ var adinfo = (_window_webapis = window.webapis) === null || _window_webapis === void 0 ? void 0 : _window_webapis.adinfo;
2440
+ if (adinfo) {
2441
+ var tifa = typeof adinfo.getTIFA === "function" ? adinfo.getTIFA() : adinfo.tifa;
2442
+ if (typeof tifa === "string" && tifa && !isMacroPlaceholder(tifa)) {
2443
+ var lat = typeof adinfo.isLATEnabled === "function" ? adinfo.isLATEnabled() : void 0;
2444
+ return _object_spread({
2445
+ deviceId: tifa,
2446
+ deviceIdType: "tifa"
2447
+ }, typeof lat === "boolean" ? {
2448
+ limitAdTracking: lat
2449
+ } : {});
2450
+ }
2451
+ }
2452
+ } catch (unused) {}
2453
+ try {
2454
+ var webOSDev = window.webOSDev;
2455
+ if (webOSDev) {
2456
+ var _webOSDev_lgudid;
2457
+ var lgudid = typeof webOSDev.LGUDID === "function" ? webOSDev.LGUDID() : (_webOSDev_lgudid = webOSDev.lgudid) !== null && _webOSDev_lgudid !== void 0 ? _webOSDev_lgudid : webOSDev.LGUDID;
2458
+ if (typeof lgudid === "string" && lgudid) {
2459
+ if (isZeroedAdId(lgudid)) {
2460
+ return {
2461
+ deviceIdType: "lgudid",
2462
+ limitAdTracking: true
2463
+ };
2464
+ }
2465
+ if (!isMacroPlaceholder(lgudid)) {
2466
+ return {
2467
+ deviceId: lgudid,
2468
+ deviceIdType: "lgudid",
2469
+ limitAdTracking: false
2470
+ };
2471
+ }
2472
+ }
2473
+ }
2474
+ } catch (unused) {}
2475
+ try {
2476
+ var roku = window.Roku;
2477
+ if (roku) {
2478
+ var rida = typeof roku.getPublisherUniqueId === "function" ? roku.getPublisherUniqueId() : roku.rida;
2479
+ if (typeof rida === "string" && rida && !isMacroPlaceholder(rida)) {
2480
+ return {
2481
+ deviceId: rida,
2482
+ deviceIdType: "rida",
2483
+ limitAdTracking: false
2484
+ };
2485
+ }
2486
+ }
2487
+ } catch (unused) {}
2488
+ return {};
2489
+ }
2490
+ var ZEROED_AD_ID_PATTERNS = /* @__PURE__ */ new Set([
2491
+ "38400000-8cf0-11bd-b23e-10b96e40000d",
2492
+ "00000000-0000-0000-0000-000000000000"
2493
+ ]);
2494
+ function isMacroPlaceholder(value) {
2495
+ var t = value.trim();
2496
+ return !t || /^(\[[^\]]*\]|\{[^}]*\}|%%[^%]*%%)$/.test(t) || /^(unknown|null|undefined|none|n\/a|\$\{[^}]+\})$/i.test(t) || ZEROED_AD_ID_PATTERNS.has(t.toLowerCase());
2497
+ }
2498
+ function isZeroedAdId(value) {
2499
+ return ZEROED_AD_ID_PATTERNS.has(value.trim().toLowerCase());
2500
+ }
2420
2501
  function readNativeBridgeSignals() {
2421
2502
  if (typeof window === "undefined") {
2422
2503
  return {};
@@ -2757,7 +2838,7 @@ var INLINE_DEVICE_MACRO_REPLACEMENTS = [
2757
2838
  }
2758
2839
  }
2759
2840
  ];
2760
- function isMacroPlaceholder(value) {
2841
+ function isMacroPlaceholder2(value) {
2761
2842
  if (value == null) return true;
2762
2843
  var trimmed = value.trim();
2763
2844
  if (!trimmed) return true;
@@ -2816,11 +2897,11 @@ function applyDeviceIdentityParams(params, ctx) {
2816
2897
  if (runtimeId) {
2817
2898
  params.set("rdid", runtimeId);
2818
2899
  params.set("idtype", runtimeType || tagType || "aaid");
2819
- params.set("is_lat", latValue !== null && latValue !== void 0 ? latValue : isMacroPlaceholder(tagLat) ? "0" : tagLat);
2900
+ params.set("is_lat", latValue !== null && latValue !== void 0 ? latValue : isMacroPlaceholder2(tagLat) ? "0" : tagLat);
2820
2901
  return;
2821
2902
  }
2822
- var tagRdidValid = Boolean(tagRdid && !isMacroPlaceholder(tagRdid));
2823
- var tagTypeValid = Boolean(tagType && !isMacroPlaceholder(tagType));
2903
+ var tagRdidValid = Boolean(tagRdid && !isMacroPlaceholder2(tagRdid));
2904
+ var tagTypeValid = Boolean(tagType && !isMacroPlaceholder2(tagType));
2824
2905
  if (tagRdidValid && tagTypeValid && runtimeType && !isEquivalentDeviceIdType(tagType, runtimeType)) {
2825
2906
  params.delete("rdid");
2826
2907
  params.delete("idtype");
@@ -2828,10 +2909,10 @@ function applyDeviceIdentityParams(params, ctx) {
2828
2909
  return;
2829
2910
  }
2830
2911
  if (tagRdidValid) {
2831
- if (isMacroPlaceholder(tagType) && runtimeType) {
2912
+ if (isMacroPlaceholder2(tagType) && runtimeType) {
2832
2913
  params.set("idtype", runtimeType);
2833
2914
  }
2834
- if (isMacroPlaceholder(tagLat)) {
2915
+ if (isMacroPlaceholder2(tagLat)) {
2835
2916
  params.set("is_lat", latValue !== null && latValue !== void 0 ? latValue : "0");
2836
2917
  }
2837
2918
  return;
@@ -3017,7 +3098,7 @@ function applyVastMacros(baseUrl, ctx) {
3017
3098
  }
3018
3099
  var staleKeys = [];
3019
3100
  params.forEach(function(value, key) {
3020
- if (isMacroPlaceholder(value)) {
3101
+ if (isMacroPlaceholder2(value)) {
3021
3102
  staleKeys.push(key);
3022
3103
  }
3023
3104
  });
@@ -5966,7 +6047,7 @@ var HlsEngine = /*#__PURE__*/ function() {
5966
6047
  key: "setupNativeHls",
5967
6048
  value: function setupNativeHls() {
5968
6049
  return _async_to_generator(function() {
5969
- var _this_config_isLiveStream, _this_config_lowLatencyMode, _this_video_play;
6050
+ var _this_config_isLiveStream, _this_config_lowLatencyMode, adBehavior, _this_video_play;
5970
6051
  return _ts_generator(this, function(_state) {
5971
6052
  switch(_state.label){
5972
6053
  case 0:
@@ -5975,10 +6056,11 @@ var HlsEngine = /*#__PURE__*/ function() {
5975
6056
  this.video.src = this.config.src;
5976
6057
  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;
5977
6058
  if (this.debug) {
5978
- console.log("[StormcloudVideoPlayer] Using native HLS playback - VOD mode:", {
6059
+ adBehavior = this.isLiveStream ? "live (main video continues muted during ads)" : "vod (main video pauses during ads)";
6060
+ console.log("[StormcloudVideoPlayer] Using native HLS playback:", {
5979
6061
  isLive: this.isLiveStream,
5980
6062
  allowNativeHls: this.config.allowNativeHls,
5981
- adBehavior: "vod (main video pauses during ads)"
6063
+ adBehavior: adBehavior
5982
6064
  });
5983
6065
  }
5984
6066
  if (!this.config.autoplay) return [
@@ -6387,6 +6469,14 @@ var HlsEngine = /*#__PURE__*/ function() {
6387
6469
  });
6388
6470
  }
6389
6471
  },
6472
+ {
6473
+ key: "getLiveSyncPosition",
6474
+ value: function getLiveSyncPosition() {
6475
+ var _this_hls;
6476
+ var pos = (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.liveSyncPosition;
6477
+ return typeof pos === "number" && Number.isFinite(pos) ? pos : void 0;
6478
+ }
6479
+ },
6390
6480
  {
6391
6481
  key: "destroy",
6392
6482
  value: function destroy() {
@@ -7961,6 +8051,13 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7961
8051
  this.host.video.volume = restoredVolume;
7962
8052
  }
7963
8053
  if (this.host.shouldContinueLiveStreamDuringAds()) {
8054
+ var liveSyncPos = this.host.getLiveSyncPosition();
8055
+ if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {
8056
+ if (this.debug) {
8057
+ console.log("[StormcloudVideoPlayer] Live break end — snapping to live edge: ".concat(this.host.video.currentTime.toFixed(2), "s → ").concat(liveSyncPos.toFixed(2), "s"));
8058
+ }
8059
+ this.host.video.currentTime = liveSyncPos;
8060
+ }
7964
8061
  if (this.host.video.paused) {
7965
8062
  var _this_host_video_play;
7966
8063
  if (this.debug) console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
@@ -8103,6 +8200,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8103
8200
  shouldContinueLiveStreamDuringAds: function shouldContinueLiveStreamDuringAds() {
8104
8201
  return _this.shouldContinueLiveStreamDuringAds();
8105
8202
  },
8203
+ getLiveSyncPosition: function getLiveSyncPosition() {
8204
+ return _this.hlsEngine.getLiveSyncPosition();
8205
+ },
8106
8206
  generateVastUrls: function generateVastUrls(base, count) {
8107
8207
  return _this.generateVastUrls(base, count);
8108
8208
  }
@@ -8144,7 +8244,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8144
8244
  // ───────────────────────────────────────────────────────────────
8145
8245
  function load() {
8146
8246
  return _async_to_generator(function() {
8147
- var _this, _this_config_lowLatencyMode, error;
8247
+ var _this, _this_config_lowLatencyMode, error, continueLiveStreamDuringAds;
8148
8248
  return _ts_generator(this, function(_state) {
8149
8249
  switch(_state.label){
8150
8250
  case 0:
@@ -8198,15 +8298,22 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8198
8298
  3,
8199
8299
  6
8200
8300
  ];
8201
- this.adPlayer.destroy();
8202
- this.adPlayer = this.createAdPlayer(false);
8203
- this.adPlayer.initialize();
8204
8301
  return [
8205
8302
  4,
8206
8303
  this.hlsEngine.setupNativeHls()
8207
8304
  ];
8208
8305
  case 5:
8209
8306
  _state.sent();
8307
+ continueLiveStreamDuringAds = this.shouldContinueLiveStreamDuringAds();
8308
+ if (this.debug) {
8309
+ console.log("[StormcloudVideoPlayer] Native HLS ad behavior:", {
8310
+ isLive: this.hlsEngine.isLiveStream,
8311
+ continueLiveStreamDuringAds: continueLiveStreamDuringAds
8312
+ });
8313
+ }
8314
+ this.adPlayer.destroy();
8315
+ this.adPlayer = this.createAdPlayer(continueLiveStreamDuringAds);
8316
+ this.adPlayer.initialize();
8210
8317
  return [
8211
8318
  2
8212
8319
  ];
@@ -8314,9 +8421,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8314
8421
  {
8315
8422
  key: "shouldContinueLiveStreamDuringAds",
8316
8423
  value: function shouldContinueLiveStreamDuringAds() {
8317
- if (this.config.allowNativeHls) return false;
8318
- if (!this.hlsEngine.isLiveStream) return false;
8319
- return true;
8424
+ if (this.hlsEngine.isLiveStream) {
8425
+ return true;
8426
+ }
8427
+ var duration = this.video.duration;
8428
+ if (!Number.isFinite(duration) || duration === Infinity) {
8429
+ return true;
8430
+ }
8431
+ return false;
8320
8432
  }
8321
8433
  },
8322
8434
  {
@@ -8614,9 +8726,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8614
8726
  this.adBreak.showAds = true;
8615
8727
  this.adBreak.inAdBreak = true;
8616
8728
  this.timing.currentAdBreakStartWallClockMs = Date.now();
8617
- if (!this.video.paused) {
8618
- this.video.pause();
8619
- }
8620
8729
  _state.label = 1;
8621
8730
  case 1:
8622
8731
  _state.trys.push([
@@ -8990,7 +9099,8 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
8990
9099
  setShowSpeedMenu(false);
8991
9100
  };
8992
9101
  var isHlsStream = (src === null || src === void 0 ? void 0 : src.toLowerCase().includes(".m3u8")) || (src === null || src === void 0 ? void 0 : src.toLowerCase().includes("/hls/"));
8993
- var shouldShowEnhancedControls = showCustomControls && (isHlsStream ? allowNativeHls || isLiveStream === false : true);
9102
+ var isLiveUiMode = isLiveStream === true;
9103
+ var shouldShowEnhancedControls = showCustomControls && (isHlsStream ? !isLiveUiMode : true);
8994
9104
  var criticalPropsKey = (0, import_react.useMemo)(function() {
8995
9105
  return CRITICAL_PROPS.map(function(prop) {
8996
9106
  return "".concat(prop, ":").concat(props[prop]);