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.d.cts CHANGED
@@ -229,6 +229,7 @@ interface BaseStormcloudPlayerProps {
229
229
  wrapperStyle?: CSSProperties;
230
230
  allowNativeHls?: boolean;
231
231
  lowLatencyMode?: boolean;
232
+ isLiveStream?: boolean;
232
233
  driftToleranceMs?: number;
233
234
  immediateManifestAds?: boolean;
234
235
  debugAdTiming?: boolean;
package/lib/index.d.ts CHANGED
@@ -229,6 +229,7 @@ interface BaseStormcloudPlayerProps {
229
229
  wrapperStyle?: CSSProperties;
230
230
  allowNativeHls?: boolean;
231
231
  lowLatencyMode?: boolean;
232
+ isLiveStream?: boolean;
232
233
  driftToleranceMs?: number;
233
234
  immediateManifestAds?: boolean;
234
235
  debugAdTiming?: boolean;
package/lib/index.js CHANGED
@@ -2327,6 +2327,18 @@ var AIRY_ROKU_APP_ID = "com.freeairytv.roku";
2327
2327
  var AIRY_APP_NAME = "AiryTV Movies & TV";
2328
2328
  var AIRY_DEFAULT_CONTENT_URL = "https://live.airy.tv";
2329
2329
  var CTV_SESSION_STORAGE_KEY = "stormcloud.ctv.sid";
2330
+ var LG_TEST_APP_ID = "com.hulu.plus";
2331
+ var LG_TEST_APP_NAME = "Hulu";
2332
+ var LG_TEST_CONTENT_URL = "https://www.hulu.com/live-tv";
2333
+ var _lgTestDeviceId;
2334
+ function getLgTestDeviceId() {
2335
+ if (!_lgTestDeviceId) _lgTestDeviceId = createUuid();
2336
+ return _lgTestDeviceId;
2337
+ }
2338
+ function isLgWebOS() {
2339
+ if (typeof navigator === "undefined") return false;
2340
+ return /Web0S|webOS|LG Browser|LGSTB|LGE/i.test(navigator.userAgent);
2341
+ }
2330
2342
  function getDefaultAppId() {
2331
2343
  if (typeof navigator === "undefined") return AIRY_ANDROID_APP_ID;
2332
2344
  var ua = navigator.userAgent;
@@ -2352,17 +2364,20 @@ var ANDROID_ID_TYPES = /* @__PURE__ */ new Set([
2352
2364
  "adid"
2353
2365
  ]);
2354
2366
  function resolveVastEnvironmentSignals(isCtv) {
2355
- var _ref, _bridgeSignals_limitAdTracking;
2367
+ var _ref;
2356
2368
  var bridgeSignals = readNativeBridgeSignals();
2357
- var deviceId = bridgeSignals.deviceId || readStoredDeviceId();
2358
- var deviceIdType = bridgeSignals.deviceIdType || readStoredString("deviceIdType") || readStoredString("idtype") || inferDeviceIdType();
2359
- 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;
2369
+ var platformDevice = readPlatformNativeDeviceId();
2370
+ var lg = isCtv && isLgWebOS();
2371
+ var deviceId = bridgeSignals.deviceId || readStoredDeviceId() || platformDevice.deviceId || (lg && platformDevice.limitAdTracking !== true ? getLgTestDeviceId() : void 0);
2372
+ var deviceIdType = bridgeSignals.deviceIdType || readStoredString("deviceIdType") || readStoredString("idtype") || platformDevice.deviceIdType || inferDeviceIdType() || (lg ? "lgudid" : void 0);
2373
+ var platformLat = bridgeSignals.deviceId ? bridgeSignals.limitAdTracking : platformDevice.limitAdTracking;
2374
+ var limitAdTracking = deviceId != null ? (_ref = platformLat !== null && platformLat !== void 0 ? platformLat : readStoredLimitAdTracking()) !== null && _ref !== void 0 ? _ref : false : void 0;
2360
2375
  if (isCtv) {
2361
2376
  return _object_spread_props(_object_spread({
2362
2377
  isCtv: true,
2363
- contentUrl: bridgeSignals.contentUrl || readStoredString("contentUrl") || AIRY_DEFAULT_CONTENT_URL,
2364
- appId: bridgeSignals.appId || readStoredString("appId") || readStoredString("msid") || getDefaultAppId(),
2365
- appName: bridgeSignals.appName || readStoredString("appName") || readStoredString("an") || AIRY_APP_NAME,
2378
+ contentUrl: bridgeSignals.contentUrl || readStoredString("contentUrl") || (lg ? LG_TEST_CONTENT_URL : AIRY_DEFAULT_CONTENT_URL),
2379
+ appId: bridgeSignals.appId || readStoredString("appId") || readStoredString("msid") || (lg ? LG_TEST_APP_ID : getDefaultAppId()),
2380
+ appName: bridgeSignals.appName || readStoredString("appName") || readStoredString("an") || (lg ? LG_TEST_APP_NAME : AIRY_APP_NAME),
2366
2381
  sessionId: getOrCreateCtvSessionId()
2367
2382
  }, deviceId ? {
2368
2383
  deviceId: deviceId
@@ -2427,6 +2442,72 @@ function createUuid() {
2427
2442
  return nibble.toString(16);
2428
2443
  });
2429
2444
  }
2445
+ function readPlatformNativeDeviceId() {
2446
+ if (typeof window === "undefined") return {};
2447
+ try {
2448
+ var _window_webapis;
2449
+ var adinfo = (_window_webapis = window.webapis) === null || _window_webapis === void 0 ? void 0 : _window_webapis.adinfo;
2450
+ if (adinfo) {
2451
+ var tifa = typeof adinfo.getTIFA === "function" ? adinfo.getTIFA() : adinfo.tifa;
2452
+ if (typeof tifa === "string" && tifa && !isMacroPlaceholder(tifa)) {
2453
+ var lat = typeof adinfo.isLATEnabled === "function" ? adinfo.isLATEnabled() : void 0;
2454
+ return _object_spread({
2455
+ deviceId: tifa,
2456
+ deviceIdType: "tifa"
2457
+ }, typeof lat === "boolean" ? {
2458
+ limitAdTracking: lat
2459
+ } : {});
2460
+ }
2461
+ }
2462
+ } catch (unused) {}
2463
+ try {
2464
+ var webOSDev = window.webOSDev;
2465
+ if (webOSDev) {
2466
+ var _webOSDev_lgudid;
2467
+ var lgudid = typeof webOSDev.LGUDID === "function" ? webOSDev.LGUDID() : (_webOSDev_lgudid = webOSDev.lgudid) !== null && _webOSDev_lgudid !== void 0 ? _webOSDev_lgudid : webOSDev.LGUDID;
2468
+ if (typeof lgudid === "string" && lgudid) {
2469
+ if (isZeroedAdId(lgudid)) {
2470
+ return {
2471
+ deviceIdType: "lgudid",
2472
+ limitAdTracking: true
2473
+ };
2474
+ }
2475
+ if (!isMacroPlaceholder(lgudid)) {
2476
+ return {
2477
+ deviceId: lgudid,
2478
+ deviceIdType: "lgudid",
2479
+ limitAdTracking: false
2480
+ };
2481
+ }
2482
+ }
2483
+ }
2484
+ } catch (unused) {}
2485
+ try {
2486
+ var roku = window.Roku;
2487
+ if (roku) {
2488
+ var rida = typeof roku.getPublisherUniqueId === "function" ? roku.getPublisherUniqueId() : roku.rida;
2489
+ if (typeof rida === "string" && rida && !isMacroPlaceholder(rida)) {
2490
+ return {
2491
+ deviceId: rida,
2492
+ deviceIdType: "rida",
2493
+ limitAdTracking: false
2494
+ };
2495
+ }
2496
+ }
2497
+ } catch (unused) {}
2498
+ return {};
2499
+ }
2500
+ var ZEROED_AD_ID_PATTERNS = /* @__PURE__ */ new Set([
2501
+ "38400000-8cf0-11bd-b23e-10b96e40000d",
2502
+ "00000000-0000-0000-0000-000000000000"
2503
+ ]);
2504
+ function isMacroPlaceholder(value) {
2505
+ var t = value.trim();
2506
+ return !t || /^(\[[^\]]*\]|\{[^}]*\}|%%[^%]*%%)$/.test(t) || /^(unknown|null|undefined|none|n\/a|\$\{[^}]+\})$/i.test(t) || ZEROED_AD_ID_PATTERNS.has(t.toLowerCase());
2507
+ }
2508
+ function isZeroedAdId(value) {
2509
+ return ZEROED_AD_ID_PATTERNS.has(value.trim().toLowerCase());
2510
+ }
2430
2511
  function readNativeBridgeSignals() {
2431
2512
  if (typeof window === "undefined") {
2432
2513
  return {};
@@ -2767,7 +2848,7 @@ var INLINE_DEVICE_MACRO_REPLACEMENTS = [
2767
2848
  }
2768
2849
  }
2769
2850
  ];
2770
- function isMacroPlaceholder(value) {
2851
+ function isMacroPlaceholder2(value) {
2771
2852
  if (value == null) return true;
2772
2853
  var trimmed = value.trim();
2773
2854
  if (!trimmed) return true;
@@ -2826,11 +2907,11 @@ function applyDeviceIdentityParams(params, ctx) {
2826
2907
  if (runtimeId) {
2827
2908
  params.set("rdid", runtimeId);
2828
2909
  params.set("idtype", runtimeType || tagType || "aaid");
2829
- params.set("is_lat", latValue !== null && latValue !== void 0 ? latValue : isMacroPlaceholder(tagLat) ? "0" : tagLat);
2910
+ params.set("is_lat", latValue !== null && latValue !== void 0 ? latValue : isMacroPlaceholder2(tagLat) ? "0" : tagLat);
2830
2911
  return;
2831
2912
  }
2832
- var tagRdidValid = Boolean(tagRdid && !isMacroPlaceholder(tagRdid));
2833
- var tagTypeValid = Boolean(tagType && !isMacroPlaceholder(tagType));
2913
+ var tagRdidValid = Boolean(tagRdid && !isMacroPlaceholder2(tagRdid));
2914
+ var tagTypeValid = Boolean(tagType && !isMacroPlaceholder2(tagType));
2834
2915
  if (tagRdidValid && tagTypeValid && runtimeType && !isEquivalentDeviceIdType(tagType, runtimeType)) {
2835
2916
  params.delete("rdid");
2836
2917
  params.delete("idtype");
@@ -2838,10 +2919,10 @@ function applyDeviceIdentityParams(params, ctx) {
2838
2919
  return;
2839
2920
  }
2840
2921
  if (tagRdidValid) {
2841
- if (isMacroPlaceholder(tagType) && runtimeType) {
2922
+ if (isMacroPlaceholder2(tagType) && runtimeType) {
2842
2923
  params.set("idtype", runtimeType);
2843
2924
  }
2844
- if (isMacroPlaceholder(tagLat)) {
2925
+ if (isMacroPlaceholder2(tagLat)) {
2845
2926
  params.set("is_lat", latValue !== null && latValue !== void 0 ? latValue : "0");
2846
2927
  }
2847
2928
  return;
@@ -3027,7 +3108,7 @@ function applyVastMacros(baseUrl, ctx) {
3027
3108
  }
3028
3109
  var staleKeys = [];
3029
3110
  params.forEach(function(value, key) {
3030
- if (isMacroPlaceholder(value)) {
3111
+ if (isMacroPlaceholder2(value)) {
3031
3112
  staleKeys.push(key);
3032
3113
  }
3033
3114
  });
@@ -6006,7 +6087,7 @@ var HlsEngine = /*#__PURE__*/ function() {
6006
6087
  key: "setupNativeHls",
6007
6088
  value: function setupNativeHls() {
6008
6089
  return _async_to_generator(function() {
6009
- var _this_config_isLiveStream, _this_config_lowLatencyMode, _this_video_play;
6090
+ var _this_config_isLiveStream, _this_config_lowLatencyMode, adBehavior, _this_video_play;
6010
6091
  return _ts_generator(this, function(_state) {
6011
6092
  switch(_state.label){
6012
6093
  case 0:
@@ -6015,10 +6096,11 @@ var HlsEngine = /*#__PURE__*/ function() {
6015
6096
  this.video.src = this.config.src;
6016
6097
  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;
6017
6098
  if (this.debug) {
6018
- console.log("[StormcloudVideoPlayer] Using native HLS playback - VOD mode:", {
6099
+ adBehavior = this.isLiveStream ? "live (main video continues muted during ads)" : "vod (main video pauses during ads)";
6100
+ console.log("[StormcloudVideoPlayer] Using native HLS playback:", {
6019
6101
  isLive: this.isLiveStream,
6020
6102
  allowNativeHls: this.config.allowNativeHls,
6021
- adBehavior: "vod (main video pauses during ads)"
6103
+ adBehavior: adBehavior
6022
6104
  });
6023
6105
  }
6024
6106
  if (!this.config.autoplay) return [
@@ -6427,6 +6509,14 @@ var HlsEngine = /*#__PURE__*/ function() {
6427
6509
  });
6428
6510
  }
6429
6511
  },
6512
+ {
6513
+ key: "getLiveSyncPosition",
6514
+ value: function getLiveSyncPosition() {
6515
+ var _this_hls;
6516
+ var pos = (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.liveSyncPosition;
6517
+ return typeof pos === "number" && Number.isFinite(pos) ? pos : void 0;
6518
+ }
6519
+ },
6430
6520
  {
6431
6521
  key: "destroy",
6432
6522
  value: function destroy() {
@@ -8002,6 +8092,13 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8002
8092
  this.host.video.volume = restoredVolume;
8003
8093
  }
8004
8094
  if (this.host.shouldContinueLiveStreamDuringAds()) {
8095
+ var liveSyncPos = this.host.getLiveSyncPosition();
8096
+ if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {
8097
+ if (this.debug) {
8098
+ console.log("[StormcloudVideoPlayer] Live break end — snapping to live edge: ".concat(this.host.video.currentTime.toFixed(2), "s → ").concat(liveSyncPos.toFixed(2), "s"));
8099
+ }
8100
+ this.host.video.currentTime = liveSyncPos;
8101
+ }
8005
8102
  if (this.host.video.paused) {
8006
8103
  var _this_host_video_play;
8007
8104
  if (this.debug) console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
@@ -8145,6 +8242,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8145
8242
  shouldContinueLiveStreamDuringAds: function shouldContinueLiveStreamDuringAds() {
8146
8243
  return _this.shouldContinueLiveStreamDuringAds();
8147
8244
  },
8245
+ getLiveSyncPosition: function getLiveSyncPosition() {
8246
+ return _this.hlsEngine.getLiveSyncPosition();
8247
+ },
8148
8248
  generateVastUrls: function generateVastUrls(base, count) {
8149
8249
  return _this.generateVastUrls(base, count);
8150
8250
  }
@@ -8186,7 +8286,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8186
8286
  // ───────────────────────────────────────────────────────────────
8187
8287
  function load() {
8188
8288
  return _async_to_generator(function() {
8189
- var _this, _this_config_lowLatencyMode, error;
8289
+ var _this, _this_config_lowLatencyMode, error, continueLiveStreamDuringAds;
8190
8290
  return _ts_generator(this, function(_state) {
8191
8291
  switch(_state.label){
8192
8292
  case 0:
@@ -8240,15 +8340,22 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8240
8340
  3,
8241
8341
  6
8242
8342
  ];
8243
- this.adPlayer.destroy();
8244
- this.adPlayer = this.createAdPlayer(false);
8245
- this.adPlayer.initialize();
8246
8343
  return [
8247
8344
  4,
8248
8345
  this.hlsEngine.setupNativeHls()
8249
8346
  ];
8250
8347
  case 5:
8251
8348
  _state.sent();
8349
+ continueLiveStreamDuringAds = this.shouldContinueLiveStreamDuringAds();
8350
+ if (this.debug) {
8351
+ console.log("[StormcloudVideoPlayer] Native HLS ad behavior:", {
8352
+ isLive: this.hlsEngine.isLiveStream,
8353
+ continueLiveStreamDuringAds: continueLiveStreamDuringAds
8354
+ });
8355
+ }
8356
+ this.adPlayer.destroy();
8357
+ this.adPlayer = this.createAdPlayer(continueLiveStreamDuringAds);
8358
+ this.adPlayer.initialize();
8252
8359
  return [
8253
8360
  2
8254
8361
  ];
@@ -8356,9 +8463,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8356
8463
  {
8357
8464
  key: "shouldContinueLiveStreamDuringAds",
8358
8465
  value: function shouldContinueLiveStreamDuringAds() {
8359
- if (this.config.allowNativeHls) return false;
8360
- if (!this.hlsEngine.isLiveStream) return false;
8361
- return true;
8466
+ if (this.hlsEngine.isLiveStream) {
8467
+ return true;
8468
+ }
8469
+ var duration = this.video.duration;
8470
+ if (!Number.isFinite(duration) || duration === Infinity) {
8471
+ return true;
8472
+ }
8473
+ return false;
8362
8474
  }
8363
8475
  },
8364
8476
  {
@@ -8656,9 +8768,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8656
8768
  this.adBreak.showAds = true;
8657
8769
  this.adBreak.inAdBreak = true;
8658
8770
  this.timing.currentAdBreakStartWallClockMs = Date.now();
8659
- if (!this.video.paused) {
8660
- this.video.pause();
8661
- }
8662
8771
  _state.label = 1;
8663
8772
  case 1:
8664
8773
  _state.trys.push([
@@ -9032,7 +9141,8 @@ var StormcloudVideoPlayerComponent = React.memo(function(props) {
9032
9141
  setShowSpeedMenu(false);
9033
9142
  };
9034
9143
  var isHlsStream = (src === null || src === void 0 ? void 0 : src.toLowerCase().includes(".m3u8")) || (src === null || src === void 0 ? void 0 : src.toLowerCase().includes("/hls/"));
9035
- var shouldShowEnhancedControls = showCustomControls && (isHlsStream ? allowNativeHls || isLiveStream === false : true);
9144
+ var isLiveUiMode = isLiveStream === true;
9145
+ var shouldShowEnhancedControls = showCustomControls && (isHlsStream ? !isLiveUiMode : true);
9036
9146
  var criticalPropsKey = useMemo(function() {
9037
9147
  return CRITICAL_PROPS.map(function(prop) {
9038
9148
  return "".concat(prop, ":").concat(props[prop]);
@@ -10413,6 +10523,7 @@ var HlsPlayer = /*#__PURE__*/ function(Component) {
10413
10523
  if (_this1.props.muted !== void 0) config.muted = _this1.props.muted;
10414
10524
  if (_this1.props.lowLatencyMode !== void 0) config.lowLatencyMode = _this1.props.lowLatencyMode;
10415
10525
  if (_this1.props.allowNativeHls !== void 0) config.allowNativeHls = _this1.props.allowNativeHls;
10526
+ if (_this1.props.isLiveStream !== void 0) config.isLiveStream = _this1.props.isLiveStream;
10416
10527
  if (_this1.props.driftToleranceMs !== void 0) config.driftToleranceMs = _this1.props.driftToleranceMs;
10417
10528
  if (_this1.props.immediateManifestAds !== void 0) config.immediateManifestAds = _this1.props.immediateManifestAds;
10418
10529
  if (_this1.props.debugAdTiming !== void 0) config.debugAdTiming = _this1.props.debugAdTiming;
@@ -11181,6 +11292,7 @@ var SUPPORTED_PROPS = [
11181
11292
  "wrapperStyle",
11182
11293
  "allowNativeHls",
11183
11294
  "lowLatencyMode",
11295
+ "isLiveStream",
11184
11296
  "driftToleranceMs",
11185
11297
  "immediateManifestAds",
11186
11298
  "debugAdTiming",