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.
@@ -2280,6 +2280,18 @@ var AIRY_ROKU_APP_ID = "com.freeairytv.roku";
2280
2280
  var AIRY_APP_NAME = "AiryTV Movies & TV";
2281
2281
  var AIRY_DEFAULT_CONTENT_URL = "https://live.airy.tv";
2282
2282
  var CTV_SESSION_STORAGE_KEY = "stormcloud.ctv.sid";
2283
+ var LG_TEST_APP_ID = "com.hulu.plus";
2284
+ var LG_TEST_APP_NAME = "Hulu";
2285
+ var LG_TEST_CONTENT_URL = "https://www.hulu.com/live-tv";
2286
+ var _lgTestDeviceId;
2287
+ function getLgTestDeviceId() {
2288
+ if (!_lgTestDeviceId) _lgTestDeviceId = createUuid();
2289
+ return _lgTestDeviceId;
2290
+ }
2291
+ function isLgWebOS() {
2292
+ if (typeof navigator === "undefined") return false;
2293
+ return /Web0S|webOS|LG Browser|LGSTB|LGE/i.test(navigator.userAgent);
2294
+ }
2283
2295
  function getDefaultAppId() {
2284
2296
  if (typeof navigator === "undefined") return AIRY_ANDROID_APP_ID;
2285
2297
  var ua = navigator.userAgent;
@@ -2305,17 +2317,20 @@ var ANDROID_ID_TYPES = /* @__PURE__ */ new Set([
2305
2317
  "adid"
2306
2318
  ]);
2307
2319
  function resolveVastEnvironmentSignals(isCtv) {
2308
- var _ref, _bridgeSignals_limitAdTracking;
2320
+ var _ref;
2309
2321
  var bridgeSignals = readNativeBridgeSignals();
2310
- var deviceId = bridgeSignals.deviceId || readStoredDeviceId();
2311
- var deviceIdType = bridgeSignals.deviceIdType || readStoredString("deviceIdType") || readStoredString("idtype") || inferDeviceIdType();
2312
- 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;
2322
+ var platformDevice = readPlatformNativeDeviceId();
2323
+ var lg = isCtv && isLgWebOS();
2324
+ var deviceId = bridgeSignals.deviceId || readStoredDeviceId() || platformDevice.deviceId || (lg && platformDevice.limitAdTracking !== true ? getLgTestDeviceId() : void 0);
2325
+ var deviceIdType = bridgeSignals.deviceIdType || readStoredString("deviceIdType") || readStoredString("idtype") || platformDevice.deviceIdType || inferDeviceIdType() || (lg ? "lgudid" : void 0);
2326
+ var platformLat = bridgeSignals.deviceId ? bridgeSignals.limitAdTracking : platformDevice.limitAdTracking;
2327
+ var limitAdTracking = deviceId != null ? (_ref = platformLat !== null && platformLat !== void 0 ? platformLat : readStoredLimitAdTracking()) !== null && _ref !== void 0 ? _ref : false : void 0;
2313
2328
  if (isCtv) {
2314
2329
  return _object_spread_props(_object_spread({
2315
2330
  isCtv: true,
2316
- contentUrl: bridgeSignals.contentUrl || readStoredString("contentUrl") || AIRY_DEFAULT_CONTENT_URL,
2317
- appId: bridgeSignals.appId || readStoredString("appId") || readStoredString("msid") || getDefaultAppId(),
2318
- appName: bridgeSignals.appName || readStoredString("appName") || readStoredString("an") || AIRY_APP_NAME,
2331
+ contentUrl: bridgeSignals.contentUrl || readStoredString("contentUrl") || (lg ? LG_TEST_CONTENT_URL : AIRY_DEFAULT_CONTENT_URL),
2332
+ appId: bridgeSignals.appId || readStoredString("appId") || readStoredString("msid") || (lg ? LG_TEST_APP_ID : getDefaultAppId()),
2333
+ appName: bridgeSignals.appName || readStoredString("appName") || readStoredString("an") || (lg ? LG_TEST_APP_NAME : AIRY_APP_NAME),
2319
2334
  sessionId: getOrCreateCtvSessionId()
2320
2335
  }, deviceId ? {
2321
2336
  deviceId: deviceId
@@ -2380,6 +2395,72 @@ function createUuid() {
2380
2395
  return nibble.toString(16);
2381
2396
  });
2382
2397
  }
2398
+ function readPlatformNativeDeviceId() {
2399
+ if (typeof window === "undefined") return {};
2400
+ try {
2401
+ var _window_webapis;
2402
+ var adinfo = (_window_webapis = window.webapis) === null || _window_webapis === void 0 ? void 0 : _window_webapis.adinfo;
2403
+ if (adinfo) {
2404
+ var tifa = typeof adinfo.getTIFA === "function" ? adinfo.getTIFA() : adinfo.tifa;
2405
+ if (typeof tifa === "string" && tifa && !isMacroPlaceholder(tifa)) {
2406
+ var lat = typeof adinfo.isLATEnabled === "function" ? adinfo.isLATEnabled() : void 0;
2407
+ return _object_spread({
2408
+ deviceId: tifa,
2409
+ deviceIdType: "tifa"
2410
+ }, typeof lat === "boolean" ? {
2411
+ limitAdTracking: lat
2412
+ } : {});
2413
+ }
2414
+ }
2415
+ } catch (unused) {}
2416
+ try {
2417
+ var webOSDev = window.webOSDev;
2418
+ if (webOSDev) {
2419
+ var _webOSDev_lgudid;
2420
+ var lgudid = typeof webOSDev.LGUDID === "function" ? webOSDev.LGUDID() : (_webOSDev_lgudid = webOSDev.lgudid) !== null && _webOSDev_lgudid !== void 0 ? _webOSDev_lgudid : webOSDev.LGUDID;
2421
+ if (typeof lgudid === "string" && lgudid) {
2422
+ if (isZeroedAdId(lgudid)) {
2423
+ return {
2424
+ deviceIdType: "lgudid",
2425
+ limitAdTracking: true
2426
+ };
2427
+ }
2428
+ if (!isMacroPlaceholder(lgudid)) {
2429
+ return {
2430
+ deviceId: lgudid,
2431
+ deviceIdType: "lgudid",
2432
+ limitAdTracking: false
2433
+ };
2434
+ }
2435
+ }
2436
+ }
2437
+ } catch (unused) {}
2438
+ try {
2439
+ var roku = window.Roku;
2440
+ if (roku) {
2441
+ var rida = typeof roku.getPublisherUniqueId === "function" ? roku.getPublisherUniqueId() : roku.rida;
2442
+ if (typeof rida === "string" && rida && !isMacroPlaceholder(rida)) {
2443
+ return {
2444
+ deviceId: rida,
2445
+ deviceIdType: "rida",
2446
+ limitAdTracking: false
2447
+ };
2448
+ }
2449
+ }
2450
+ } catch (unused) {}
2451
+ return {};
2452
+ }
2453
+ var ZEROED_AD_ID_PATTERNS = /* @__PURE__ */ new Set([
2454
+ "38400000-8cf0-11bd-b23e-10b96e40000d",
2455
+ "00000000-0000-0000-0000-000000000000"
2456
+ ]);
2457
+ function isMacroPlaceholder(value) {
2458
+ var t = value.trim();
2459
+ return !t || /^(\[[^\]]*\]|\{[^}]*\}|%%[^%]*%%)$/.test(t) || /^(unknown|null|undefined|none|n\/a|\$\{[^}]+\})$/i.test(t) || ZEROED_AD_ID_PATTERNS.has(t.toLowerCase());
2460
+ }
2461
+ function isZeroedAdId(value) {
2462
+ return ZEROED_AD_ID_PATTERNS.has(value.trim().toLowerCase());
2463
+ }
2383
2464
  function readNativeBridgeSignals() {
2384
2465
  if (typeof window === "undefined") {
2385
2466
  return {};
@@ -2720,7 +2801,7 @@ var INLINE_DEVICE_MACRO_REPLACEMENTS = [
2720
2801
  }
2721
2802
  }
2722
2803
  ];
2723
- function isMacroPlaceholder(value) {
2804
+ function isMacroPlaceholder2(value) {
2724
2805
  if (value == null) return true;
2725
2806
  var trimmed = value.trim();
2726
2807
  if (!trimmed) return true;
@@ -2779,11 +2860,11 @@ function applyDeviceIdentityParams(params, ctx) {
2779
2860
  if (runtimeId) {
2780
2861
  params.set("rdid", runtimeId);
2781
2862
  params.set("idtype", runtimeType || tagType || "aaid");
2782
- params.set("is_lat", latValue !== null && latValue !== void 0 ? latValue : isMacroPlaceholder(tagLat) ? "0" : tagLat);
2863
+ params.set("is_lat", latValue !== null && latValue !== void 0 ? latValue : isMacroPlaceholder2(tagLat) ? "0" : tagLat);
2783
2864
  return;
2784
2865
  }
2785
- var tagRdidValid = Boolean(tagRdid && !isMacroPlaceholder(tagRdid));
2786
- var tagTypeValid = Boolean(tagType && !isMacroPlaceholder(tagType));
2866
+ var tagRdidValid = Boolean(tagRdid && !isMacroPlaceholder2(tagRdid));
2867
+ var tagTypeValid = Boolean(tagType && !isMacroPlaceholder2(tagType));
2787
2868
  if (tagRdidValid && tagTypeValid && runtimeType && !isEquivalentDeviceIdType(tagType, runtimeType)) {
2788
2869
  params.delete("rdid");
2789
2870
  params.delete("idtype");
@@ -2791,10 +2872,10 @@ function applyDeviceIdentityParams(params, ctx) {
2791
2872
  return;
2792
2873
  }
2793
2874
  if (tagRdidValid) {
2794
- if (isMacroPlaceholder(tagType) && runtimeType) {
2875
+ if (isMacroPlaceholder2(tagType) && runtimeType) {
2795
2876
  params.set("idtype", runtimeType);
2796
2877
  }
2797
- if (isMacroPlaceholder(tagLat)) {
2878
+ if (isMacroPlaceholder2(tagLat)) {
2798
2879
  params.set("is_lat", latValue !== null && latValue !== void 0 ? latValue : "0");
2799
2880
  }
2800
2881
  return;
@@ -2980,7 +3061,7 @@ function applyVastMacros(baseUrl, ctx) {
2980
3061
  }
2981
3062
  var staleKeys = [];
2982
3063
  params.forEach(function(value, key) {
2983
- if (isMacroPlaceholder(value)) {
3064
+ if (isMacroPlaceholder2(value)) {
2984
3065
  staleKeys.push(key);
2985
3066
  }
2986
3067
  });
@@ -5929,7 +6010,7 @@ var HlsEngine = /*#__PURE__*/ function() {
5929
6010
  key: "setupNativeHls",
5930
6011
  value: function setupNativeHls() {
5931
6012
  return _async_to_generator(function() {
5932
- var _this_config_isLiveStream, _this_config_lowLatencyMode, _this_video_play;
6013
+ var _this_config_isLiveStream, _this_config_lowLatencyMode, adBehavior, _this_video_play;
5933
6014
  return _ts_generator(this, function(_state) {
5934
6015
  switch(_state.label){
5935
6016
  case 0:
@@ -5938,10 +6019,11 @@ var HlsEngine = /*#__PURE__*/ function() {
5938
6019
  this.video.src = this.config.src;
5939
6020
  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;
5940
6021
  if (this.debug) {
5941
- console.log("[StormcloudVideoPlayer] Using native HLS playback - VOD mode:", {
6022
+ adBehavior = this.isLiveStream ? "live (main video continues muted during ads)" : "vod (main video pauses during ads)";
6023
+ console.log("[StormcloudVideoPlayer] Using native HLS playback:", {
5942
6024
  isLive: this.isLiveStream,
5943
6025
  allowNativeHls: this.config.allowNativeHls,
5944
- adBehavior: "vod (main video pauses during ads)"
6026
+ adBehavior: adBehavior
5945
6027
  });
5946
6028
  }
5947
6029
  if (!this.config.autoplay) return [
@@ -6350,6 +6432,14 @@ var HlsEngine = /*#__PURE__*/ function() {
6350
6432
  });
6351
6433
  }
6352
6434
  },
6435
+ {
6436
+ key: "getLiveSyncPosition",
6437
+ value: function getLiveSyncPosition() {
6438
+ var _this_hls;
6439
+ var pos = (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.liveSyncPosition;
6440
+ return typeof pos === "number" && Number.isFinite(pos) ? pos : void 0;
6441
+ }
6442
+ },
6353
6443
  {
6354
6444
  key: "destroy",
6355
6445
  value: function destroy() {
@@ -7924,6 +8014,13 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7924
8014
  this.host.video.volume = restoredVolume;
7925
8015
  }
7926
8016
  if (this.host.shouldContinueLiveStreamDuringAds()) {
8017
+ var liveSyncPos = this.host.getLiveSyncPosition();
8018
+ if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {
8019
+ if (this.debug) {
8020
+ console.log("[StormcloudVideoPlayer] Live break end — snapping to live edge: ".concat(this.host.video.currentTime.toFixed(2), "s → ").concat(liveSyncPos.toFixed(2), "s"));
8021
+ }
8022
+ this.host.video.currentTime = liveSyncPos;
8023
+ }
7927
8024
  if (this.host.video.paused) {
7928
8025
  var _this_host_video_play;
7929
8026
  if (this.debug) console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
@@ -8066,6 +8163,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8066
8163
  shouldContinueLiveStreamDuringAds: function shouldContinueLiveStreamDuringAds() {
8067
8164
  return _this.shouldContinueLiveStreamDuringAds();
8068
8165
  },
8166
+ getLiveSyncPosition: function getLiveSyncPosition() {
8167
+ return _this.hlsEngine.getLiveSyncPosition();
8168
+ },
8069
8169
  generateVastUrls: function generateVastUrls(base, count) {
8070
8170
  return _this.generateVastUrls(base, count);
8071
8171
  }
@@ -8107,7 +8207,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8107
8207
  // ───────────────────────────────────────────────────────────────
8108
8208
  function load() {
8109
8209
  return _async_to_generator(function() {
8110
- var _this, _this_config_lowLatencyMode, error;
8210
+ var _this, _this_config_lowLatencyMode, error, continueLiveStreamDuringAds;
8111
8211
  return _ts_generator(this, function(_state) {
8112
8212
  switch(_state.label){
8113
8213
  case 0:
@@ -8161,15 +8261,22 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8161
8261
  3,
8162
8262
  6
8163
8263
  ];
8164
- this.adPlayer.destroy();
8165
- this.adPlayer = this.createAdPlayer(false);
8166
- this.adPlayer.initialize();
8167
8264
  return [
8168
8265
  4,
8169
8266
  this.hlsEngine.setupNativeHls()
8170
8267
  ];
8171
8268
  case 5:
8172
8269
  _state.sent();
8270
+ continueLiveStreamDuringAds = this.shouldContinueLiveStreamDuringAds();
8271
+ if (this.debug) {
8272
+ console.log("[StormcloudVideoPlayer] Native HLS ad behavior:", {
8273
+ isLive: this.hlsEngine.isLiveStream,
8274
+ continueLiveStreamDuringAds: continueLiveStreamDuringAds
8275
+ });
8276
+ }
8277
+ this.adPlayer.destroy();
8278
+ this.adPlayer = this.createAdPlayer(continueLiveStreamDuringAds);
8279
+ this.adPlayer.initialize();
8173
8280
  return [
8174
8281
  2
8175
8282
  ];
@@ -8277,9 +8384,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8277
8384
  {
8278
8385
  key: "shouldContinueLiveStreamDuringAds",
8279
8386
  value: function shouldContinueLiveStreamDuringAds() {
8280
- if (this.config.allowNativeHls) return false;
8281
- if (!this.hlsEngine.isLiveStream) return false;
8282
- return true;
8387
+ if (this.hlsEngine.isLiveStream) {
8388
+ return true;
8389
+ }
8390
+ var duration = this.video.duration;
8391
+ if (!Number.isFinite(duration) || duration === Infinity) {
8392
+ return true;
8393
+ }
8394
+ return false;
8283
8395
  }
8284
8396
  },
8285
8397
  {
@@ -8577,9 +8689,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8577
8689
  this.adBreak.showAds = true;
8578
8690
  this.adBreak.inAdBreak = true;
8579
8691
  this.timing.currentAdBreakStartWallClockMs = Date.now();
8580
- if (!this.video.paused) {
8581
- this.video.pause();
8582
- }
8583
8692
  _state.label = 1;
8584
8693
  case 1:
8585
8694
  _state.trys.push([