@oddin-gg/havik-player 2.3.0 → 2.3.1

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/dist/index.js CHANGED
@@ -1377,11 +1377,11 @@ var U = class {
1377
1377
  let r = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
1378
1378
  return new n(t === "DRM" ? i(r) : r === 401 ? "UNAUTHORIZED" : r === 403 ? "FORBIDDEN" : "INTERNAL", r, `${t} error: ${a}${o ? ` (${o})` : ""}`);
1379
1379
  }
1380
- }, tt = 15e3, nt = 100, W = 1, rt = 1e3, G = 3e4, it = {
1380
+ }, tt = 15e3, nt = 100, W = 1, rt = 1e3, G = 3e4, it = 1e3, at = {
1381
1381
  inline: "PRESENTATION_INLINE",
1382
1382
  fullscreen: "PRESENTATION_FULLSCREEN",
1383
1383
  pip: "PRESENTATION_PIP"
1384
- }, at = {
1384
+ }, ot = {
1385
1385
  widevine: "KEY_SYSTEM_WIDEVINE",
1386
1386
  fairplay: "KEY_SYSTEM_FAIRPLAY",
1387
1387
  none: "KEY_SYSTEM_NONE"
@@ -1389,7 +1389,7 @@ var U = class {
1389
1389
  function K(e, t) {
1390
1390
  return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
1391
1391
  }
1392
- var ot = /* @__PURE__ */ new Set([
1392
+ var st = /* @__PURE__ */ new Set([
1393
1393
  "INVALID_URN",
1394
1394
  "NOT_FOUND",
1395
1395
  "GONE",
@@ -1409,7 +1409,7 @@ var ot = /* @__PURE__ */ new Set([
1409
1409
  ended: "PLAYER_STATE_ENDED",
1410
1410
  error: "PLAYER_STATE_ERROR"
1411
1411
  };
1412
- function st(e) {
1412
+ function ct(e) {
1413
1413
  try {
1414
1414
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
1415
1415
  return !n || r.length === 0 || n !== "feed" && !n.startsWith("feed-") ? void 0 : (t.hostname = [n.replace(/^feed/, "beacons"), ...r].join("."), t.pathname = "", t.search = "", t.origin);
@@ -1417,7 +1417,7 @@ function st(e) {
1417
1417
  return;
1418
1418
  }
1419
1419
  }
1420
- var ct = class {
1420
+ var lt = class {
1421
1421
  opts;
1422
1422
  url;
1423
1423
  now;
@@ -1462,7 +1462,8 @@ var ct = class {
1462
1462
  timeToFirstStallMs = 0;
1463
1463
  lastStallMs = 0;
1464
1464
  lastErrorMs = 0;
1465
- lastSeekMs = 0;
1465
+ lastSeekMs = -1;
1466
+ stallIsSeekInduced = !1;
1466
1467
  lastErrorStage = null;
1467
1468
  sawFirstBytes = !1;
1468
1469
  mediaInfoSignature = "";
@@ -1500,17 +1501,25 @@ var ct = class {
1500
1501
  noteStateChange(e, t, n, r) {
1501
1502
  if (this.endedFlag || e === t) return;
1502
1503
  let i = this.now();
1503
- t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(W, i - this.startupAnchorMs)), this.firstAudioMs === 0 && !this.isMuted && (this.firstAudioMs = Math.max(W, i - this.startupAnchorMs)), this.isMuted && this.mutedSinceMs === null && (this.mutedSinceMs = i)) : this.playingSinceMs !== null && (this.playingTotalMs += i - this.playingSinceMs, this.playingSinceMs = null, this.mutedSinceMs !== null && (this.mutedTotalMs += i - this.mutedSinceMs, this.mutedSinceMs = null)), this.isPlaying = t === "playing", this.syncActive(), t === "waiting" ? this.waitingSinceMs = i : this.waitingSinceMs !== null && (this.waitingTotalMs += i - this.waitingSinceMs, this.waitingSinceMs = null), t === "buffering" && e === "playing" ? (this.isHidden || (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(W, Math.round(this.playingTotalMs)))) : e === "buffering" && (this.isBuffering = !1), this.syncBuffering(), t === "paused" && (this.pauseCount += 1);
1504
+ if (t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(W, i - this.startupAnchorMs)), this.firstAudioMs === 0 && !this.isMuted && (this.firstAudioMs = Math.max(W, i - this.startupAnchorMs)), this.isMuted && this.mutedSinceMs === null && (this.mutedSinceMs = i)) : this.playingSinceMs !== null && (this.playingTotalMs += i - this.playingSinceMs, this.playingSinceMs = null, this.mutedSinceMs !== null && (this.mutedTotalMs += i - this.mutedSinceMs, this.mutedSinceMs = null)), this.isPlaying = t === "playing", this.syncActive(), t === "waiting" ? this.waitingSinceMs = i : this.waitingSinceMs !== null && (this.waitingTotalMs += i - this.waitingSinceMs, this.waitingSinceMs = null), t === "buffering" && e === "playing") {
1505
+ this.stallIsSeekInduced = this.seekInduced(i);
1506
+ let e = !this.isHidden && !this.stallIsSeekInduced;
1507
+ e && (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, e && this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(W, Math.round(this.playingTotalMs)));
1508
+ } else e === "buffering" && (this.isBuffering = !1, this.stallIsSeekInduced = !1);
1509
+ this.syncBuffering(), t === "paused" && (this.pauseCount += 1);
1504
1510
  let a = {
1505
1511
  fromState: q[e] ?? "PLAYER_STATE_UNSPECIFIED",
1506
1512
  toState: q[t] ?? "PLAYER_STATE_UNSPECIFIED",
1507
- presentation: it[this.presentation]
1513
+ presentation: at[this.presentation]
1508
1514
  }, o = this.causeOf(e, t, i);
1509
1515
  o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
1510
1516
  }
1517
+ seekInduced(e) {
1518
+ return this.lastSeekMs >= 0 && e - this.lastSeekMs < it;
1519
+ }
1511
1520
  causeOf(e, t, n) {
1512
1521
  if (t === "error") return "TRANSITION_CAUSE_ERROR";
1513
- if (t === "buffering") return n - this.lastSeekMs < 1e3 ? "TRANSITION_CAUSE_SEEK" : "TRANSITION_CAUSE_NETWORK_STALL";
1522
+ if (t === "buffering") return this.seekInduced(n) ? "TRANSITION_CAUSE_SEEK" : "TRANSITION_CAUSE_NETWORK_STALL";
1514
1523
  if (t === "paused" || t === "playing" && e === "paused") return "TRANSITION_CAUSE_USER";
1515
1524
  }
1516
1525
  syncActive() {
@@ -1518,7 +1527,7 @@ var ct = class {
1518
1527
  e && this.activeSinceMs === null ? this.activeSinceMs = this.now() : !e && this.activeSinceMs !== null && (this.activeTotalMs += this.now() - this.activeSinceMs, this.activeSinceMs = null);
1519
1528
  }
1520
1529
  syncBuffering() {
1521
- let e = this.isBuffering && !this.isHidden;
1530
+ let e = this.isBuffering && !this.isHidden && !this.stallIsSeekInduced;
1522
1531
  e && this.bufferingSinceMs === null ? this.bufferingSinceMs = this.now() : !e && this.bufferingSinceMs !== null && (this.rebufferTotalMs += this.now() - this.bufferingSinceMs, this.bufferingSinceMs = null);
1523
1532
  }
1524
1533
  closePresentation() {
@@ -1574,7 +1583,7 @@ var ct = class {
1574
1583
  let r = n ?? "";
1575
1584
  if (r !== "") return /^fragLoad/i.test(r) ? this.sawFirstBytes ? "ERROR_STAGE_SEGMENT" : "ERROR_STAGE_FIRST_SEGMENT" : /^(manifest|level|audioTrack|subtitleTrack)/i.test(r) ? "ERROR_STAGE_MANIFEST" : /key|licen[cs]e/i.test(r) ? "ERROR_STAGE_DRM" : /parsing|append|buffer|remux|decrypt|media/i.test(r) ? "ERROR_STAGE_DECODE" : "ERROR_STAGE_OTHER";
1576
1585
  if (e === "DRM_CLIENT") return "ERROR_STAGE_DRM";
1577
- if (ot.has(e)) return "ERROR_STAGE_RESOLVE";
1586
+ if (st.has(e)) return "ERROR_STAGE_RESOLVE";
1578
1587
  let i = t ?? "";
1579
1588
  return /frag|segment/i.test(i) ? this.sawFirstBytes ? "ERROR_STAGE_SEGMENT" : "ERROR_STAGE_FIRST_SEGMENT" : /manifest|level|playlist/i.test(i) ? "ERROR_STAGE_MANIFEST" : /key|licen[cs]e/i.test(i) ? "ERROR_STAGE_DRM" : /buffer|decode|media|append/i.test(i) ? "ERROR_STAGE_DECODE" : "ERROR_STAGE_OTHER";
1580
1589
  }
@@ -1622,7 +1631,7 @@ var ct = class {
1622
1631
  maybeEmitMediaInfo(e) {
1623
1632
  if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
1624
1633
  let t = {};
1625
- e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = at[e.keySystem]), e.drmSecurityLevel && (t.drmSecurityLevel = e.drmSecurityLevel), e.ladderHeights?.length && (t.ladderHeights = e.ladderHeights), e.ladderTopBitrateKbps && (t.ladderTopBitrateKbps = e.ladderTopBitrateKbps), e.lowLatency && (t.lowLatency = !0);
1634
+ e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = ot[e.keySystem]), e.drmSecurityLevel && (t.drmSecurityLevel = e.drmSecurityLevel), e.ladderHeights?.length && (t.ladderHeights = e.ladderHeights), e.ladderTopBitrateKbps && (t.ladderTopBitrateKbps = e.ladderTopBitrateKbps), e.lowLatency && (t.lowLatency = !0);
1626
1635
  let n = JSON.stringify(t);
1627
1636
  n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
1628
1637
  }
@@ -1687,7 +1696,7 @@ var ct = class {
1687
1696
  };
1688
1697
  //#endregion
1689
1698
  //#region src/core/environment.ts
1690
- function lt() {
1699
+ function ut() {
1691
1700
  if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
1692
1701
  let e;
1693
1702
  try {
@@ -1712,14 +1721,14 @@ function lt() {
1712
1721
  integration: "INTEGRATION_KIND_IFRAME"
1713
1722
  };
1714
1723
  }
1715
- function ut() {
1724
+ function dt() {
1716
1725
  if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
1717
1726
  let e = navigator.userAgentData, t = e?.platform ?? "";
1718
1727
  if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
1719
1728
  let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
1720
1729
  return e?.mobile ?? (typeof navigator.maxTouchPoints == "number" && navigator.maxTouchPoints > 1 && n > 0 && n < 900) ? n >= 600 ? "DEVICE_CLASS_TABLET" : "DEVICE_CLASS_PHONE" : e || n >= 900 || /mac|win|linux|chrome os/i.test(t) ? "DEVICE_CLASS_DESKTOP" : "DEVICE_CLASS_OTHER";
1721
1730
  }
1722
- function dt() {
1731
+ function ft() {
1723
1732
  let e = typeof navigator < "u" ? navigator.connection : void 0;
1724
1733
  if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
1725
1734
  let t = { connectionType: e.type ? {
@@ -1729,17 +1738,17 @@ function dt() {
1729
1738
  }[e.type] ?? "CONNECTION_TYPE_OTHER" : "CONNECTION_TYPE_UNKNOWN" };
1730
1739
  return typeof e.downlink == "number" && e.downlink > 0 && (t.downlinkKbps = Math.round(e.downlink * 1e3)), e.saveData && (t.saveData = !0), t;
1731
1740
  }
1732
- function ft(e) {
1733
- let t = lt(), n = {
1734
- deviceClass: ut(),
1741
+ function pt(e) {
1742
+ let t = ut(), n = {
1743
+ deviceClass: dt(),
1735
1744
  integration: t.integration,
1736
- ...dt()
1745
+ ...ft()
1737
1746
  };
1738
1747
  t.host && (n.host = t.host), typeof screen < "u" && screen.width > 0 && (n.screenWidth = screen.width, n.screenHeight = screen.height), typeof window < "u" && window.devicePixelRatio > 0 && (n.devicePixelRatio = Math.round(window.devicePixelRatio * 100) / 100);
1739
1748
  let r = e.getBoundingClientRect?.();
1740
1749
  return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
1741
1750
  }
1742
- async function pt() {
1751
+ async function mt() {
1743
1752
  let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
1744
1753
  if (!e?.getHighEntropyValues) return {};
1745
1754
  try {
@@ -1751,7 +1760,7 @@ async function pt() {
1751
1760
  }
1752
1761
  //#endregion
1753
1762
  //#region src/core/version.ts
1754
- var mt = "2.3.0", J = {
1763
+ var ht = "2.3.1", J = {
1755
1764
  accent: "#E1B600",
1756
1765
  accentText: "#1B1C23",
1757
1766
  background: "#0e0f13",
@@ -1784,15 +1793,15 @@ function X(e, t) {
1784
1793
  }
1785
1794
  //#endregion
1786
1795
  //#region src/ui/styles.ts
1787
- var Z = "havik-player-styles", ht = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n.havik-spinner{width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n";
1788
- function gt(e = document) {
1796
+ var Z = "havik-player-styles", gt = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n.havik-spinner{width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n";
1797
+ function _t(e = document) {
1789
1798
  if (e.getElementById(Z)) return;
1790
1799
  let t = e.createElement("style");
1791
- t.id = Z, t.textContent = ht, e.head.appendChild(t);
1800
+ t.id = Z, t.textContent = gt, e.head.appendChild(t);
1792
1801
  }
1793
1802
  //#endregion
1794
1803
  //#region src/ui/icons.ts
1795
- var _t = {
1804
+ var vt = {
1796
1805
  play: "<path d=\"M8 5v14l11-7z\"/>",
1797
1806
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
1798
1807
  volumeHigh: "<path d=\"M3 10v4h4l5 5V5L7 10H3zm13.5 2a4.5 4.5 0 0 0-2.5-4v8a4.5 4.5 0 0 0 2.5-4zm-2.5-9v2.06A7 7 0 0 1 14 19v2a9 9 0 0 0 0-18z\"/>",
@@ -1806,20 +1815,20 @@ var _t = {
1806
1815
  retry: "<path d=\"M17.65 6.35A8 8 0 1 0 19.73 13h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z\"/>"
1807
1816
  };
1808
1817
  function Q(e) {
1809
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${_t[e]}</svg>`;
1818
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${vt[e]}</svg>`;
1810
1819
  }
1811
1820
  //#endregion
1812
1821
  //#region src/ui/controls.ts
1813
- var vt = 3e3, yt = /* @__PURE__ */ new Set([
1822
+ var yt = 3e3, bt = /* @__PURE__ */ new Set([
1814
1823
  "GONE",
1815
1824
  "NOT_FOUND",
1816
1825
  "INVALID_URN",
1817
1826
  "UNAUTHORIZED"
1818
1827
  ]);
1819
- function bt(e) {
1820
- return new xt(e);
1828
+ function xt(e) {
1829
+ return new St(e);
1821
1830
  }
1822
- var xt = class {
1831
+ var St = class {
1823
1832
  root;
1824
1833
  video;
1825
1834
  player;
@@ -1861,7 +1870,7 @@ var xt = class {
1861
1870
  scrubbing = !1;
1862
1871
  offFns = [];
1863
1872
  constructor(e) {
1864
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? vt, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, gt(this.doc), this.root.classList.add("havik-player"), this.root.hasAttribute("tabindex") || (this.root.tabIndex = 0), this.root.setAttribute("role", "region"), this.root.setAttribute("aria-label", "Video player");
1873
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? yt, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, _t(this.doc), this.root.classList.add("havik-player"), this.root.hasAttribute("tabindex") || (this.root.tabIndex = 0), this.root.setAttribute("role", "region"), this.root.setAttribute("aria-label", "Video player");
1865
1874
  let t = (e, t, n) => {
1866
1875
  let r = this.doc.createElement(e);
1867
1876
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -1952,7 +1961,7 @@ var xt = class {
1952
1961
  e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = Q("play"), e.addEventListener("click", () => void this.player.play()), n.append(e);
1953
1962
  } else {
1954
1963
  let r = this.doc.createElement("div");
1955
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !yt.has(this.lastError.code)) {
1964
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !bt.has(this.lastError.code)) {
1956
1965
  let e = this.doc.createElement("button");
1957
1966
  e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${Q("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), n.append(e);
1958
1967
  }
@@ -1993,7 +2002,7 @@ var xt = class {
1993
2002
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
1994
2003
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
1995
2004
  }
1996
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${St(this.video.currentTime)} / ${St(this.video.duration)}`);
2005
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Ct(this.video.currentTime)} / ${Ct(this.video.duration)}`);
1997
2006
  }
1998
2007
  syncLive() {
1999
2008
  let e = this.player.getStats(), t = e.isLive !== !1;
@@ -2043,17 +2052,17 @@ var xt = class {
2043
2052
  renderQualityMenu() {
2044
2053
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
2045
2054
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
2046
- for (let n of e) this.menu.append(this.menuItem(Ct(n), t === n.index, () => this.player.setQuality(n.index)));
2055
+ for (let n of e) this.menu.append(this.menuItem(wt(n), t === n.index, () => this.player.setQuality(n.index)));
2047
2056
  }
2048
2057
  renderAudioMenu() {
2049
2058
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
2050
2059
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
2051
- for (let t of e) this.menu.append(this.menuItem(wt(t), t.default, () => this.player.setAudioTrack(t.id)));
2060
+ for (let t of e) this.menu.append(this.menuItem(Tt(t), t.default, () => this.player.setAudioTrack(t.id)));
2052
2061
  }
2053
2062
  renderCcMenu() {
2054
2063
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
2055
2064
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
2056
- for (let t of e) this.menu.append(this.menuItem(Tt(t), !1, () => this.player.setTextTrack(t.id)));
2065
+ for (let t of e) this.menu.append(this.menuItem(Et(t), !1, () => this.player.setTextTrack(t.id)));
2057
2066
  }
2058
2067
  onTracksChanged() {
2059
2068
  this.openMenu && this.renderMenu(this.openMenu);
@@ -2153,32 +2162,32 @@ var xt = class {
2153
2162
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
2154
2163
  }
2155
2164
  };
2156
- function St(e) {
2165
+ function Ct(e) {
2157
2166
  (!Number.isFinite(e) || e < 0) && (e = 0);
2158
2167
  let t = Math.floor(e % 60), n = Math.floor(e / 60 % 60), r = Math.floor(e / 3600), i = r > 0 ? String(n).padStart(2, "0") : String(n), a = String(t).padStart(2, "0");
2159
2168
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
2160
2169
  }
2161
- function Ct(e) {
2170
+ function wt(e) {
2162
2171
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
2163
2172
  }
2164
- function wt(e) {
2173
+ function Tt(e) {
2165
2174
  return e.name || e.lang || `Track ${e.id}`;
2166
2175
  }
2167
- function Tt(e) {
2176
+ function Et(e) {
2168
2177
  return e.name || e.lang || `Track ${e.id}`;
2169
2178
  }
2170
2179
  //#endregion
2171
2180
  //#region src/managed/index.ts
2172
- var Et = 6e3, Dt = 45e3, Ot = 2e3, kt = 3, At = 2e3, jt = 48e4;
2173
- async function Mt(e) {
2181
+ var Dt = 6e3, Ot = 45e3, kt = 2e3, At = 3, jt = 2e3, Mt = 48e4;
2182
+ async function Nt(e) {
2174
2183
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
2175
2184
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: baseUrl is required");
2176
2185
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
2177
2186
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
2178
- let t = new Nt(e);
2187
+ let t = new Pt(e);
2179
2188
  return await t.start(), t;
2180
2189
  }
2181
- var Nt = class {
2190
+ var Pt = class {
2182
2191
  opts;
2183
2192
  abort = new AbortController();
2184
2193
  listeners = {};
@@ -2265,7 +2274,7 @@ var Nt = class {
2265
2274
  setTimeout(() => {
2266
2275
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
2267
2276
  phase: i ? "unavailable" : "tooEarly",
2268
- retryInMs: Dt,
2277
+ retryInMs: Ot,
2269
2278
  attempt: 1,
2270
2279
  elapsedMs: 0,
2271
2280
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -2304,13 +2313,13 @@ var Nt = class {
2304
2313
  !a && !p() && h();
2305
2314
  }, e);
2306
2315
  };
2307
- _(Et), this.armingSub = T({
2316
+ _(Dt), this.armingSub = T({
2308
2317
  eventsBaseUrl: t,
2309
2318
  matchUrn: this.opts.matchUrn,
2310
2319
  credential: this.opts.credential,
2311
- onAlive: () => _(Dt),
2320
+ onAlive: () => _(Ot),
2312
2321
  onState: (e) => {
2313
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Ot);
2322
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * kt);
2314
2323
  },
2315
2324
  onError: () => h()
2316
2325
  });
@@ -2352,15 +2361,15 @@ var Nt = class {
2352
2361
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
2353
2362
  let r = e.analytics?.sid;
2354
2363
  if (!r) return;
2355
- let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? st(this.opts.baseUrl);
2364
+ let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? ct(this.opts.baseUrl);
2356
2365
  if (!i) return;
2357
- let a = this.opts.video, o = new ct({
2366
+ let a = this.opts.video, o = new lt({
2358
2367
  endpoint: i,
2359
2368
  sid: r,
2360
2369
  apiKey: n,
2361
2370
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
2362
- sdkVersion: mt,
2363
- environment: ft(a),
2371
+ sdkVersion: ht,
2372
+ environment: pt(a),
2364
2373
  sample: () => {
2365
2374
  let e = this.engine?.getStats() ?? I, t = a.buffered.length ? Math.max(0, a.buffered.end(a.buffered.length - 1) - a.currentTime) : 0;
2366
2375
  return {
@@ -2376,7 +2385,7 @@ var Nt = class {
2376
2385
  let s = () => o.noteSeek(), c = () => o.noteVolume(a.muted, a.volume);
2377
2386
  a.addEventListener("seeking", s), a.addEventListener("volumechange", c), this.detachMediaListeners = () => {
2378
2387
  a.removeEventListener("seeking", s), a.removeEventListener("volumechange", c);
2379
- }, o.start(), o.seedVolume(a.muted, a.volume), o.notePresentation(this.currentPresentation()), pt().then((e) => o.noteEnvironment(e)), this.opts.autoplay === !1 && o.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
2388
+ }, o.start(), o.seedVolume(a.muted, a.volume), o.notePresentation(this.currentPresentation()), mt().then((e) => o.noteEnvironment(e)), this.opts.autoplay === !1 && o.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
2380
2389
  }
2381
2390
  currentPresentation() {
2382
2391
  let e = this.opts.video;
@@ -2447,7 +2456,7 @@ var Nt = class {
2447
2456
  r = 0, i = !1;
2448
2457
  },
2449
2458
  onError: () => {
2450
- this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= kt && (i = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} after ${r} attempts — end-of-stream detection degraded to the HLS staleness watchdog. Check the events host, the api-key's allowed origins, and CSP connect-src.`))));
2459
+ this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= At && (i = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} after ${r} attempts — end-of-stream detection degraded to the HLS staleness watchdog. Check the events host, the api-key's allowed origins, and CSP connect-src.`))));
2451
2460
  }
2452
2461
  });
2453
2462
  }
@@ -2469,7 +2478,7 @@ var Nt = class {
2469
2478
  scheduleRejoin(e) {
2470
2479
  !this.rejoinEnabled() || this.rejoinTimer || (this.rejoinTimer = setTimeout(() => {
2471
2480
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
2472
- }, Math.random() * Ot));
2481
+ }, Math.random() * kt));
2473
2482
  }
2474
2483
  cancelRejoin() {
2475
2484
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -2493,7 +2502,7 @@ var Nt = class {
2493
2502
  }
2494
2503
  t.controls = !1;
2495
2504
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
2496
- r && r.insertBefore(n, t), n.appendChild(t), X(n, this.opts.theme), this.container = n, this.controlBar = bt({
2505
+ r && r.insertBefore(n, t), n.appendChild(t), X(n, this.opts.theme), this.container = n, this.controlBar = xt({
2497
2506
  root: n,
2498
2507
  video: t,
2499
2508
  player: this,
@@ -2523,7 +2532,7 @@ var Nt = class {
2523
2532
  }
2524
2533
  startStats() {
2525
2534
  this.stopStats();
2526
- let e = Math.max(500, this.opts.statsIntervalMs ?? At);
2535
+ let e = Math.max(500, this.opts.statsIntervalMs ?? jt);
2527
2536
  this.statsTimer = setInterval(() => {
2528
2537
  if (!this.engine) return;
2529
2538
  let e = this.engine.getStats();
@@ -2535,7 +2544,7 @@ var Nt = class {
2535
2544
  }
2536
2545
  scheduleLicenseRefresh() {
2537
2546
  this.stopRefresh();
2538
- let e = this.opts.licenseRefreshMs ?? jt;
2547
+ let e = this.opts.licenseRefreshMs ?? Mt;
2539
2548
  !e || !this._descriptor?.drmEnabled || (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
2540
2549
  }
2541
2550
  stopRefresh() {
@@ -2687,7 +2696,7 @@ function $(e) {
2687
2696
  }
2688
2697
  //#endregion
2689
2698
  //#region src/embed/host.ts
2690
- function Pt(e) {
2699
+ function Ft(e) {
2691
2700
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
2692
2701
  if (!e.src) throw Error("mountEmbed: opts.src is required");
2693
2702
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -2746,8 +2755,8 @@ function Pt(e) {
2746
2755
  }
2747
2756
  //#endregion
2748
2757
  //#region src/embed/page.ts
2749
- var Ft = /^[A-Za-z0-9:_.-]{1,128}$/;
2750
- function It() {
2758
+ var It = /^[A-Za-z0-9:_.-]{1,128}$/;
2759
+ function Lt() {
2751
2760
  let e = window.HAVIK_EMBED_CONFIG;
2752
2761
  if (e) return {
2753
2762
  baseUrl: e.baseUrl ?? "",
@@ -2769,9 +2778,9 @@ function It() {
2769
2778
  waitForLive: !0
2770
2779
  };
2771
2780
  }
2772
- async function Lt() {
2773
- let e = It();
2774
- zt();
2781
+ async function Rt() {
2782
+ let e = Lt();
2783
+ Bt();
2775
2784
  let t = document.createElement("video");
2776
2785
  t.setAttribute("playsinline", ""), t.muted = e.muted ?? !0, t.style.cssText = "width:100%;height:100%;object-fit:contain;background:#000;aspect-ratio:auto", document.body.appendChild(t);
2777
2786
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -2826,12 +2835,12 @@ async function Lt() {
2826
2835
  code: e.code,
2827
2836
  message: e.message
2828
2837
  })), e.on("autoplayblocked", () => {
2829
- i({ type: "oddin:autoplayblocked" }), Rt(t, e);
2838
+ i({ type: "oddin:autoplayblocked" }), zt(t, e);
2830
2839
  });
2831
2840
  }, s = async (r) => {
2832
2841
  a?.destroy(), a = null;
2833
2842
  try {
2834
- let n = await Mt({
2843
+ let n = await Nt({
2835
2844
  video: t,
2836
2845
  baseUrl: e.baseUrl,
2837
2846
  matchUrn: r,
@@ -2892,7 +2901,7 @@ async function Lt() {
2892
2901
  a?.retry();
2893
2902
  break;
2894
2903
  case "oddin:load":
2895
- typeof e.matchUrn == "string" && Ft.test(e.matchUrn) && s(e.matchUrn);
2904
+ typeof e.matchUrn == "string" && It.test(e.matchUrn) && s(e.matchUrn);
2896
2905
  break;
2897
2906
  case "oddin:destroy": c();
2898
2907
  }
@@ -2904,18 +2913,18 @@ async function Lt() {
2904
2913
  protocol: 1
2905
2914
  });
2906
2915
  }
2907
- function Rt(e, t) {
2916
+ function zt(e, t) {
2908
2917
  if (document.getElementById("havik-tap")) return;
2909
2918
  let n = document.createElement("button");
2910
2919
  n.id = "havik-tap", n.textContent = "▶ Tap to play", n.style.cssText = "position:fixed;inset:0;width:100%;height:100%;border:0;background:rgba(0,0,0,.55);color:#fff;font:600 18px system-ui,sans-serif;cursor:pointer;z-index:9", n.addEventListener("click", () => {
2911
2920
  n.remove(), t.play();
2912
2921
  }), document.body.appendChild(n);
2913
2922
  }
2914
- function zt() {
2923
+ function Bt() {
2915
2924
  let e = document.createElement("style");
2916
2925
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
2917
2926
  }
2918
2927
  //#endregion
2919
- export { J as ODDIN_THEME, n as PlaybackError, X as applyTheme, Lt as bootEmbed, _e as clearDeviceId, Mt as createPlayer, E as deriveEventsBaseUrl, De as detectDrmSupport, C as enterVideoFullscreen, w as exitVideoFullscreen, de as fetchCatalog, pe as fetchTournament, he as flattenMatches, k as getDeviceId, ke as isLowLatencyManifest, $ as isOddinMessage, ye as licenseRequestHeaders, Pt as mountEmbed, v as pollToLive, b as resolveStream, T as subscribeLiveState, S as videoFullscreenActive, ge as watchStatus };
2928
+ export { J as ODDIN_THEME, n as PlaybackError, X as applyTheme, Rt as bootEmbed, _e as clearDeviceId, Nt as createPlayer, E as deriveEventsBaseUrl, De as detectDrmSupport, C as enterVideoFullscreen, w as exitVideoFullscreen, de as fetchCatalog, pe as fetchTournament, he as flattenMatches, k as getDeviceId, ke as isLowLatencyManifest, $ as isOddinMessage, ye as licenseRequestHeaders, Ft as mountEmbed, v as pollToLive, b as resolveStream, T as subscribeLiveState, S as videoFullscreenActive, ge as watchStatus };
2920
2929
 
2921
2930
  //# sourceMappingURL=index.js.map