@oddin-gg/havik-player 2.6.4 → 2.7.0

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
@@ -1426,11 +1426,11 @@ var U = class {
1426
1426
  let r = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
1427
1427
  return new n(t === "DRM" ? i(r) : r === 401 ? "UNAUTHORIZED" : r === 403 ? "FORBIDDEN" : "INTERNAL", r, `${t} error: ${a}${o ? ` (${o})` : ""}`);
1428
1428
  }
1429
- }, rt = 15e3, it = 100, W = 1, at = 1e3, G = 3e4, ot = 1e3, st = {
1429
+ }, rt = 15e3, it = 6e5, at = 3e5, ot = 100, W = 1, st = 1e3, G = 3e4, ct = 1e3, lt = {
1430
1430
  inline: "PRESENTATION_INLINE",
1431
1431
  fullscreen: "PRESENTATION_FULLSCREEN",
1432
1432
  pip: "PRESENTATION_PIP"
1433
- }, ct = {
1433
+ }, ut = {
1434
1434
  widevine: "KEY_SYSTEM_WIDEVINE",
1435
1435
  fairplay: "KEY_SYSTEM_FAIRPLAY",
1436
1436
  none: "KEY_SYSTEM_NONE"
@@ -1438,7 +1438,7 @@ var U = class {
1438
1438
  function K(e, t) {
1439
1439
  return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
1440
1440
  }
1441
- var lt = /* @__PURE__ */ new Set([
1441
+ var dt = /* @__PURE__ */ new Set([
1442
1442
  "INVALID_URN",
1443
1443
  "NOT_FOUND",
1444
1444
  "GONE",
@@ -1458,7 +1458,7 @@ var lt = /* @__PURE__ */ new Set([
1458
1458
  ended: "PLAYER_STATE_ENDED",
1459
1459
  error: "PLAYER_STATE_ERROR"
1460
1460
  };
1461
- function ut(e) {
1461
+ function ft(e) {
1462
1462
  try {
1463
1463
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
1464
1464
  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);
@@ -1466,7 +1466,7 @@ function ut(e) {
1466
1466
  return;
1467
1467
  }
1468
1468
  }
1469
- var dt = class {
1469
+ var pt = class {
1470
1470
  opts;
1471
1471
  url;
1472
1472
  now;
@@ -1526,19 +1526,26 @@ var dt = class {
1526
1526
  lastRequestErrors = 0;
1527
1527
  pendingStart = null;
1528
1528
  firstFlushTimer = null;
1529
+ idleTimer = null;
1530
+ lastTickMs = 0;
1529
1531
  bornMs;
1530
1532
  startupAnchorMs;
1531
1533
  startupTimeMs = 0;
1532
1534
  manifestLoadMs = 0;
1533
1535
  firstFragLoadMs = 0;
1534
1536
  onPagehide = () => this.end("END_REASON_PAGEHIDE");
1537
+ onFreeze = () => {
1538
+ this.endedFlag || this.flush(!0);
1539
+ };
1540
+ onResume = () => this.checkSuspension(this.now());
1535
1541
  onVisibility = () => {
1536
1542
  typeof document > "u" || (document.visibilityState === "hidden" ? (this.hiddenSinceMs === null && (this.hiddenSinceMs = this.now()), this.isHidden = !0) : (this.hiddenSinceMs !== null && (this.hiddenTotalMs += this.now() - this.hiddenSinceMs, this.hiddenSinceMs = null), this.isHidden = !1), this.syncActive(), this.syncBuffering());
1537
1543
  };
1538
1544
  constructor(e) {
1539
1545
  this.opts = {
1540
1546
  ...e,
1541
- heartbeatMs: e.heartbeatMs ?? rt
1547
+ heartbeatMs: e.heartbeatMs ?? rt,
1548
+ idleMs: e.idleMs ?? it
1542
1549
  }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`, this.now = e.now ?? (() => Date.now()), this.bornMs = this.now(), this.presentationSinceMs = this.bornMs, this.startupAnchorMs = this.bornMs;
1543
1550
  }
1544
1551
  start() {
@@ -1548,7 +1555,20 @@ var dt = class {
1548
1555
  sdkVersion: this.opts.sdkVersion,
1549
1556
  ...this.opts.environment
1550
1557
  };
1551
- this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), at), this.timer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs), typeof window < "u" && window.addEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.addEventListener("visibilitychange", this.onVisibility), this.onVisibility());
1558
+ this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), st), this.lastTickMs = this.now(), this.timer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs), typeof window < "u" && window.addEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.addEventListener("visibilitychange", this.onVisibility), document.addEventListener("freeze", this.onFreeze), document.addEventListener("resume", this.onResume), this.onVisibility());
1559
+ }
1560
+ checkSuspension(e) {
1561
+ if (this.endedFlag || !this.opts.idleMs) return !1;
1562
+ let t = Math.max(this.opts.idleMs, at);
1563
+ return this.lastTickMs > 0 && e - this.lastTickMs >= t ? (this.endIdle(this.lastTickMs), !0) : (this.lastTickMs = e, !1);
1564
+ }
1565
+ syncIdle(e) {
1566
+ e === "paused" && this.opts.idleMs ? this.idleTimer === null && (this.idleTimer = setTimeout(() => {
1567
+ this.idleTimer = null, this.endIdle();
1568
+ }, this.opts.idleMs)) : this.idleTimer !== null && (clearTimeout(this.idleTimer), this.idleTimer = null);
1569
+ }
1570
+ endIdle(e) {
1571
+ this.endedFlag || (this.end("END_REASON_IDLE", e), this.opts.onIdle?.());
1552
1572
  }
1553
1573
  noteStateChange(e, t, n, r) {
1554
1574
  if (this.endedFlag || e === t) return;
@@ -1558,32 +1578,32 @@ var dt = class {
1558
1578
  let e = !this.isHidden && !this.stallIsSeekInduced;
1559
1579
  e && (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, e && this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(W, Math.round(this.playingTotalMs)));
1560
1580
  } else e === "buffering" && (this.isBuffering = !1, this.stallIsSeekInduced = !1);
1561
- this.syncBuffering(), t === "paused" && (this.pauseCount += 1);
1581
+ this.syncBuffering(), t === "paused" && (this.pauseCount += 1), this.syncIdle(t);
1562
1582
  let a = {
1563
1583
  fromState: q[e] ?? "PLAYER_STATE_UNSPECIFIED",
1564
1584
  toState: q[t] ?? "PLAYER_STATE_UNSPECIFIED",
1565
- presentation: st[this.presentation]
1585
+ presentation: lt[this.presentation]
1566
1586
  }, o = this.causeOf(e, t, i);
1567
1587
  o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
1568
1588
  }
1569
1589
  seekInduced(e) {
1570
- return this.lastSeekMs >= 0 && e - this.lastSeekMs < ot;
1590
+ return this.lastSeekMs >= 0 && e - this.lastSeekMs < ct;
1571
1591
  }
1572
1592
  causeOf(e, t, n) {
1573
1593
  if (t === "error") return "TRANSITION_CAUSE_ERROR";
1574
1594
  if (t === "buffering") return this.seekInduced(n) ? "TRANSITION_CAUSE_SEEK" : "TRANSITION_CAUSE_NETWORK_STALL";
1575
1595
  if (t === "paused" || t === "playing" && e === "paused") return "TRANSITION_CAUSE_USER";
1576
1596
  }
1577
- syncActive() {
1578
- let e = this.isPlaying && !this.isHidden && !this.isMuted;
1579
- e && this.activeSinceMs === null ? this.activeSinceMs = this.now() : !e && this.activeSinceMs !== null && (this.activeTotalMs += this.now() - this.activeSinceMs, this.activeSinceMs = null);
1597
+ syncActive(e = this.now()) {
1598
+ let t = this.isPlaying && !this.isHidden && !this.isMuted;
1599
+ t && this.activeSinceMs === null ? this.activeSinceMs = e : !t && this.activeSinceMs !== null && (this.activeTotalMs += e - this.activeSinceMs, this.activeSinceMs = null);
1580
1600
  }
1581
1601
  syncBuffering() {
1582
1602
  let e = this.isBuffering && !this.isHidden && !this.stallIsSeekInduced;
1583
1603
  e && this.bufferingSinceMs === null ? this.bufferingSinceMs = this.now() : !e && this.bufferingSinceMs !== null && (this.rebufferTotalMs += this.now() - this.bufferingSinceMs, this.bufferingSinceMs = null);
1584
1604
  }
1585
- closePresentation() {
1586
- let e = this.now(), t = e - this.presentationSinceMs;
1605
+ closePresentation(e = this.now()) {
1606
+ let t = e - this.presentationSinceMs;
1587
1607
  this.presentation === "fullscreen" ? this.fullscreenTotalMs += t : this.presentation === "pip" && (this.pipTotalMs += t), this.presentationSinceMs = e;
1588
1608
  }
1589
1609
  noteAutoplay(e) {
@@ -1635,7 +1655,7 @@ var dt = class {
1635
1655
  let r = n ?? "";
1636
1656
  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";
1637
1657
  if (e === "DRM_CLIENT") return "ERROR_STAGE_DRM";
1638
- if (lt.has(e)) return "ERROR_STAGE_RESOLVE";
1658
+ if (dt.has(e)) return "ERROR_STAGE_RESOLVE";
1639
1659
  let i = t ?? "";
1640
1660
  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";
1641
1661
  }
@@ -1650,66 +1670,68 @@ var dt = class {
1650
1670
  }
1651
1671
  heartbeat() {
1652
1672
  if (this.endedFlag) return;
1653
- let e = this.opts.sample(), t = this.now(), n = this.playingTotalMs;
1654
- this.playingSinceMs !== null && (n += t - this.playingSinceMs);
1673
+ let e = this.now();
1674
+ if (this.checkSuspension(e)) return;
1675
+ let t = this.opts.sample(), n = this.playingTotalMs;
1676
+ this.playingSinceMs !== null && (n += e - this.playingSinceMs);
1655
1677
  let r = this.hiddenTotalMs;
1656
- this.hiddenSinceMs !== null && (r += t - this.hiddenSinceMs);
1678
+ this.hiddenSinceMs !== null && (r += e - this.hiddenSinceMs);
1657
1679
  let i = this.rebufferTotalMs;
1658
- this.bufferingSinceMs !== null && (i += t - this.bufferingSinceMs);
1680
+ this.bufferingSinceMs !== null && (i += e - this.bufferingSinceMs);
1659
1681
  let a = this.activeTotalMs;
1660
- this.activeSinceMs !== null && (a += t - this.activeSinceMs);
1661
- let o = {}, s = e.bytesLoaded ?? 0;
1682
+ this.activeSinceMs !== null && (a += e - this.activeSinceMs);
1683
+ let o = {}, s = t.bytesLoaded ?? 0;
1662
1684
  s > this.lastBytes && (o.bytesDownloadedDelta = s - this.lastBytes), this.lastBytes = s;
1663
- let c = e.playlistBytesLoaded ?? 0;
1685
+ let c = t.playlistBytesLoaded ?? 0;
1664
1686
  c > this.lastPlaylistBytes && (o.playlistBytesDelta = c - this.lastPlaylistBytes), this.lastPlaylistBytes = c;
1665
- let l = e.droppedFrames ?? 0;
1687
+ let l = t.droppedFrames ?? 0;
1666
1688
  l > this.lastDropped && (o.droppedFramesDelta = l - this.lastDropped), this.lastDropped = l;
1667
- let u = e.renditionSwitches ?? 0;
1668
- u > this.lastSwitches && (o.renditionSwitchesDelta = u - this.lastSwitches), this.lastSwitches = u, n > this.lastHbPlayingMs && (o.playingMsDelta = Math.round(n - this.lastHbPlayingMs)), this.lastHbPlayingMs = n, r > this.lastHbHiddenMs && (o.hiddenMsDelta = Math.round(r - this.lastHbHiddenMs)), this.lastHbHiddenMs = r, i > this.lastHbRebufferMs && (o.rebufferMsDelta = Math.round(i - this.lastHbRebufferMs)), this.lastHbRebufferMs = i, this.rebufferCount > this.lastHbRebufferCount && (o.rebufferCountDelta = this.rebufferCount - this.lastHbRebufferCount), this.lastHbRebufferCount = this.rebufferCount, a > this.lastHbActiveMs && (o.activeMsDelta = Math.round(a - this.lastHbActiveMs)), this.lastHbActiveMs = a, typeof e.latencySeconds == "number" && (o.latencyMs = Math.round(e.latencySeconds * 1e3)), typeof e.targetLatencySeconds == "number" && (o.targetLatencyMs = Math.round(e.targetLatencySeconds * 1e3)), typeof e.bandwidthKbps == "number" && (o.bandwidthEstKbps = e.bandwidthKbps), typeof e.levelHeight == "number" && (o.renditionHeight = e.levelHeight), typeof e.renditionBitrateKbps == "number" && (o.renditionBitrateKbps = e.renditionBitrateKbps), e.atLiveEdge === !0 && (o.atLiveEdge = !0), typeof e.bufferedMs == "number" && e.bufferedMs > 0 && (o.bufferedMs = Math.round(e.bufferedMs)), o.playerState = q[e.state] ?? "PLAYER_STATE_UNSPECIFIED";
1689
+ let u = t.renditionSwitches ?? 0;
1690
+ u > this.lastSwitches && (o.renditionSwitchesDelta = u - this.lastSwitches), this.lastSwitches = u, n > this.lastHbPlayingMs && (o.playingMsDelta = Math.round(n - this.lastHbPlayingMs)), this.lastHbPlayingMs = n, r > this.lastHbHiddenMs && (o.hiddenMsDelta = Math.round(r - this.lastHbHiddenMs)), this.lastHbHiddenMs = r, i > this.lastHbRebufferMs && (o.rebufferMsDelta = Math.round(i - this.lastHbRebufferMs)), this.lastHbRebufferMs = i, this.rebufferCount > this.lastHbRebufferCount && (o.rebufferCountDelta = this.rebufferCount - this.lastHbRebufferCount), this.lastHbRebufferCount = this.rebufferCount, a > this.lastHbActiveMs && (o.activeMsDelta = Math.round(a - this.lastHbActiveMs)), this.lastHbActiveMs = a, typeof t.latencySeconds == "number" && (o.latencyMs = Math.round(t.latencySeconds * 1e3)), typeof t.targetLatencySeconds == "number" && (o.targetLatencyMs = Math.round(t.targetLatencySeconds * 1e3)), typeof t.bandwidthKbps == "number" && (o.bandwidthEstKbps = t.bandwidthKbps), typeof t.levelHeight == "number" && (o.renditionHeight = t.levelHeight), typeof t.renditionBitrateKbps == "number" && (o.renditionBitrateKbps = t.renditionBitrateKbps), t.atLiveEdge === !0 && (o.atLiveEdge = !0), typeof t.bufferedMs == "number" && t.bufferedMs > 0 && (o.bufferedMs = Math.round(t.bufferedMs)), o.playerState = q[t.state] ?? "PLAYER_STATE_UNSPECIFIED";
1669
1691
  let d = this.mutedTotalMs;
1670
- this.mutedSinceMs !== null && (d += t - this.mutedSinceMs), d > this.lastHbMutedMs && (o.mutedMsDelta = Math.round(d - this.lastHbMutedMs)), this.lastHbMutedMs = d;
1671
- let f = e.upshifts ?? 0;
1692
+ this.mutedSinceMs !== null && (d += e - this.mutedSinceMs), d > this.lastHbMutedMs && (o.mutedMsDelta = Math.round(d - this.lastHbMutedMs)), this.lastHbMutedMs = d;
1693
+ let f = t.upshifts ?? 0;
1672
1694
  f > this.lastUpshifts && (o.upshiftsDelta = f - this.lastUpshifts), this.lastUpshifts = f;
1673
- let p = e.downshifts ?? 0;
1695
+ let p = t.downshifts ?? 0;
1674
1696
  p > this.lastDownshifts && (o.downshiftsDelta = p - this.lastDownshifts), this.lastDownshifts = p;
1675
- let m = e.decodedFrames ?? 0;
1697
+ let m = t.decodedFrames ?? 0;
1676
1698
  m > this.lastDecoded && (o.decodedFramesDelta = m - this.lastDecoded), this.lastDecoded = m;
1677
- let h = e.requestErrors ?? 0;
1699
+ let h = t.requestErrors ?? 0;
1678
1700
  h > this.lastRequestErrors && (o.requestErrorsDelta = h - this.lastRequestErrors), this.lastRequestErrors = h;
1679
- let g = e.requestCount ?? 0, _ = g - this.lastRequestCount;
1701
+ let g = t.requestCount ?? 0, _ = g - this.lastRequestCount;
1680
1702
  if (this.lastRequestCount = g, _ > 0) {
1681
1703
  o.requestCountDelta = _;
1682
- let t = e.requestTimesMs ?? [], n = t.slice(Math.max(0, t.length - _)).sort((e, t) => e - t);
1704
+ let e = t.requestTimesMs ?? [], n = e.slice(Math.max(0, e.length - _)).sort((e, t) => e - t);
1683
1705
  n.length > 0 && (o.requestTimeP50Ms = K(n, 50), o.requestTimeP95Ms = K(n, 95));
1684
1706
  }
1685
- typeof e.latencySeconds == "number" && typeof e.targetLatencySeconds == "number" && (o.driftMs = Math.round((e.latencySeconds - e.targetLatencySeconds) * 1e3)), typeof e.playbackRate == "number" && e.playbackRate > 0 && (o.playbackRate = e.playbackRate), e.qualityPinned === !0 && (o.qualityPinned = !0), e.cdnHost && (o.cdnHost = e.cdnHost), this.maybeEmitMediaInfo(e), this.enqueue({ heartbeat: o }), this.flush(), s > 0 && this.noteFirstBytes();
1707
+ typeof t.latencySeconds == "number" && typeof t.targetLatencySeconds == "number" && (o.driftMs = Math.round((t.latencySeconds - t.targetLatencySeconds) * 1e3)), typeof t.playbackRate == "number" && t.playbackRate > 0 && (o.playbackRate = t.playbackRate), t.qualityPinned === !0 && (o.qualityPinned = !0), t.cdnHost && (o.cdnHost = t.cdnHost), this.maybeEmitMediaInfo(t), this.enqueue({ heartbeat: o }), this.flush(), s > 0 && this.noteFirstBytes();
1686
1708
  }
1687
1709
  maybeEmitMediaInfo(e) {
1688
1710
  if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
1689
1711
  let t = {};
1690
- e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = ct[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);
1712
+ e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = ut[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);
1691
1713
  let n = JSON.stringify(t);
1692
1714
  n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
1693
1715
  }
1694
- end(e) {
1716
+ end(e, t) {
1695
1717
  if (this.endedFlag) return;
1696
1718
  this.endedFlag = !0;
1697
- let t = this.now();
1698
- this.playingSinceMs !== null && (this.playingTotalMs += t - this.playingSinceMs, this.playingSinceMs = null), this.hiddenSinceMs !== null && (this.hiddenTotalMs += t - this.hiddenSinceMs, this.hiddenSinceMs = null), this.bufferingSinceMs !== null && (this.rebufferTotalMs += t - this.bufferingSinceMs, this.bufferingSinceMs = null), this.mutedSinceMs !== null && (this.mutedTotalMs += t - this.mutedSinceMs, this.mutedSinceMs = null), this.waitingSinceMs !== null && (this.waitingTotalMs += t - this.waitingSinceMs, this.waitingSinceMs = null), this.isPlaying = !1, this.syncActive(), this.closePresentation(), this.firstFlushTimer &&= (clearTimeout(this.firstFlushTimer), null), this.timer &&= (clearInterval(this.timer), null), typeof window < "u" && window.removeEventListener("pagehide", this.onPagehide), typeof document < "u" && document.removeEventListener("visibilitychange", this.onVisibility);
1699
- let n = this.safeSample();
1700
- n && this.maybeEmitMediaInfo(n);
1701
- let r = { reason: e }, i = n?.bytesLoaded ?? this.lastBytes;
1702
- i > 0 && (r.bytesDownloadedTotal = i), this.playingTotalMs > 0 && (r.watchTimeMs = Math.round(this.playingTotalMs)), this.rebufferCount > 0 && (r.rebufferCount = this.rebufferCount), this.rebufferTotalMs > 0 && (r.rebufferTimeMs = Math.round(this.rebufferTotalMs)), this.startupTimeMs > 0 && (r.startupTimeMs = Math.round(this.startupTimeMs)), this.manifestLoadMs > 0 && (r.manifestLoadMs = Math.round(this.manifestLoadMs)), this.firstFragLoadMs > 0 && (r.firstFragLoadMs = Math.round(this.firstFragLoadMs));
1703
- let a = n?.licenseTimeMs ?? 0;
1704
- a > 0 && (r.licenseTimeMs = Math.round(a));
1705
- let o = n?.droppedFrames ?? this.lastDropped;
1706
- o > 0 && (r.droppedFramesTotal = o), this.errorCount > 0 && (r.errorCount = this.errorCount), this.hiddenTotalMs > 0 && (r.hiddenTimeMs = Math.round(this.hiddenTotalMs)), this.seekCount > 0 && (r.seekCount = this.seekCount), this.pauseCount > 0 && (r.pauseCount = this.pauseCount), this.firstAudioMs > 0 && (r.firstAudioMs = Math.round(this.firstAudioMs)), this.waitingTotalMs > 0 && (r.waitingForLiveMs = Math.round(this.waitingTotalMs)), this.timeToFirstStallMs > 0 && (r.timeToFirstStallMs = Math.round(this.timeToFirstStallMs));
1707
- let s = n?.positionMs ?? 0;
1708
- s > 0 && (r.exitPositionMs = Math.round(s)), this.activeTotalMs > 0 && (r.activeWatchTimeMs = Math.round(this.activeTotalMs)), this.mutedTotalMs > 0 && (r.mutedTimeMs = Math.round(this.mutedTotalMs));
1709
- let c = e !== "END_REASON_ENDED";
1710
- c && this.lastStallMs > 0 && t - this.lastStallMs <= G && (r.abandonAfterStallMs = Math.max(W, Math.round(t - this.lastStallMs))), c && this.lastErrorMs > 0 && t - this.lastErrorMs <= G && (r.abandonAfterErrorMs = Math.max(W, Math.round(t - this.lastErrorMs))), this.fullscreenTotalMs > 0 && (r.fullscreenTimeMs = Math.round(this.fullscreenTotalMs)), this.pipTotalMs > 0 && (r.pipTimeMs = Math.round(this.pipTotalMs)), this.volumeChanges > 0 && (r.volumeChanges = this.volumeChanges), this.muteChanges > 0 && (r.muteChanges = this.muteChanges);
1711
- let l = n?.decodedFrames ?? this.lastDecoded;
1712
- l > 0 && (r.decodedFramesTotal = l), this.startupTimeMs === 0 && (r.failedStage = this.lastErrorStage ?? (this.manifestLoadMs === 0 ? "ERROR_STAGE_MANIFEST" : this.firstFragLoadMs === 0 ? "ERROR_STAGE_FIRST_SEGMENT" : "ERROR_STAGE_OTHER")), this.pending.push(this.wrap({ sessionEnd: r })), this.flush(e === "END_REASON_PAGEHIDE");
1719
+ let n = t ?? this.now();
1720
+ this.playingSinceMs !== null && (this.playingTotalMs += n - this.playingSinceMs, this.playingSinceMs = null), this.hiddenSinceMs !== null && (this.hiddenTotalMs += n - this.hiddenSinceMs, this.hiddenSinceMs = null), this.bufferingSinceMs !== null && (this.rebufferTotalMs += n - this.bufferingSinceMs, this.bufferingSinceMs = null), this.mutedSinceMs !== null && (this.mutedTotalMs += n - this.mutedSinceMs, this.mutedSinceMs = null), this.waitingSinceMs !== null && (this.waitingTotalMs += n - this.waitingSinceMs, this.waitingSinceMs = null), this.isPlaying = !1, this.syncActive(n), this.closePresentation(n), this.firstFlushTimer &&= (clearTimeout(this.firstFlushTimer), null), this.timer &&= (clearInterval(this.timer), null), this.idleTimer &&= (clearTimeout(this.idleTimer), null), typeof window < "u" && window.removeEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.removeEventListener("visibilitychange", this.onVisibility), document.removeEventListener("freeze", this.onFreeze), document.removeEventListener("resume", this.onResume));
1721
+ let r = this.safeSample();
1722
+ r && this.maybeEmitMediaInfo(r);
1723
+ let i = { reason: e }, a = r?.bytesLoaded ?? this.lastBytes;
1724
+ a > 0 && (i.bytesDownloadedTotal = a), this.playingTotalMs > 0 && (i.watchTimeMs = Math.round(this.playingTotalMs)), this.rebufferCount > 0 && (i.rebufferCount = this.rebufferCount), this.rebufferTotalMs > 0 && (i.rebufferTimeMs = Math.round(this.rebufferTotalMs)), this.startupTimeMs > 0 && (i.startupTimeMs = Math.round(this.startupTimeMs)), this.manifestLoadMs > 0 && (i.manifestLoadMs = Math.round(this.manifestLoadMs)), this.firstFragLoadMs > 0 && (i.firstFragLoadMs = Math.round(this.firstFragLoadMs));
1725
+ let o = r?.licenseTimeMs ?? 0;
1726
+ o > 0 && (i.licenseTimeMs = Math.round(o));
1727
+ let s = r?.droppedFrames ?? this.lastDropped;
1728
+ s > 0 && (i.droppedFramesTotal = s), this.errorCount > 0 && (i.errorCount = this.errorCount), this.hiddenTotalMs > 0 && (i.hiddenTimeMs = Math.round(this.hiddenTotalMs)), this.seekCount > 0 && (i.seekCount = this.seekCount), this.pauseCount > 0 && (i.pauseCount = this.pauseCount), this.firstAudioMs > 0 && (i.firstAudioMs = Math.round(this.firstAudioMs)), this.waitingTotalMs > 0 && (i.waitingForLiveMs = Math.round(this.waitingTotalMs)), this.timeToFirstStallMs > 0 && (i.timeToFirstStallMs = Math.round(this.timeToFirstStallMs));
1729
+ let c = r?.positionMs ?? 0;
1730
+ c > 0 && (i.exitPositionMs = Math.round(c)), this.activeTotalMs > 0 && (i.activeWatchTimeMs = Math.round(this.activeTotalMs)), this.mutedTotalMs > 0 && (i.mutedTimeMs = Math.round(this.mutedTotalMs));
1731
+ let l = e !== "END_REASON_ENDED";
1732
+ l && this.lastStallMs > 0 && n - this.lastStallMs <= G && (i.abandonAfterStallMs = Math.max(W, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= G && (i.abandonAfterErrorMs = Math.max(W, Math.round(n - this.lastErrorMs))), this.fullscreenTotalMs > 0 && (i.fullscreenTimeMs = Math.round(this.fullscreenTotalMs)), this.pipTotalMs > 0 && (i.pipTimeMs = Math.round(this.pipTotalMs)), this.volumeChanges > 0 && (i.volumeChanges = this.volumeChanges), this.muteChanges > 0 && (i.muteChanges = this.muteChanges);
1733
+ let u = r?.decodedFrames ?? this.lastDecoded;
1734
+ u > 0 && (i.decodedFramesTotal = u), this.startupTimeMs === 0 && (i.failedStage = this.lastErrorStage ?? (this.manifestLoadMs === 0 ? "ERROR_STAGE_MANIFEST" : this.firstFragLoadMs === 0 ? "ERROR_STAGE_FIRST_SEGMENT" : "ERROR_STAGE_OTHER")), this.pending.push(this.wrap({ sessionEnd: i })), this.flush(e === "END_REASON_PAGEHIDE");
1713
1735
  }
1714
1736
  safeSample() {
1715
1737
  try {
@@ -1727,7 +1749,7 @@ var dt = class {
1727
1749
  };
1728
1750
  }
1729
1751
  enqueue(e) {
1730
- for (this.pending.push(this.wrap(e)); this.pending.length > it;) {
1752
+ for (this.pending.push(this.wrap(e)); this.pending.length > ot;) {
1731
1753
  let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
1732
1754
  this.pending.splice(e >= 0 ? e : 0, 1);
1733
1755
  }
@@ -1750,7 +1772,7 @@ var dt = class {
1750
1772
  };
1751
1773
  //#endregion
1752
1774
  //#region src/core/environment.ts
1753
- function ft() {
1775
+ function mt() {
1754
1776
  if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
1755
1777
  let e;
1756
1778
  try {
@@ -1775,14 +1797,14 @@ function ft() {
1775
1797
  integration: "INTEGRATION_KIND_IFRAME"
1776
1798
  };
1777
1799
  }
1778
- function pt() {
1800
+ function ht() {
1779
1801
  if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
1780
1802
  let e = navigator.userAgentData, t = e?.platform ?? "";
1781
1803
  if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
1782
1804
  let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
1783
1805
  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";
1784
1806
  }
1785
- function mt() {
1807
+ function gt() {
1786
1808
  let e = typeof navigator < "u" ? navigator.connection : void 0;
1787
1809
  if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
1788
1810
  let t = {
@@ -1801,17 +1823,17 @@ function mt() {
1801
1823
  }
1802
1824
  return typeof e.downlink == "number" && e.downlink > 0 && (i.downlinkKbps = Math.round(e.downlink * 1e3)), typeof e.rtt == "number" && e.rtt > 0 && (i.rttMs = Math.round(e.rtt)), e.saveData && (i.saveData = !0), i;
1803
1825
  }
1804
- function ht(e) {
1805
- let t = ft(), n = {
1806
- deviceClass: pt(),
1826
+ function _t(e) {
1827
+ let t = mt(), n = {
1828
+ deviceClass: ht(),
1807
1829
  integration: t.integration,
1808
- ...mt()
1830
+ ...gt()
1809
1831
  };
1810
1832
  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);
1811
1833
  let r = e.getBoundingClientRect?.();
1812
1834
  return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
1813
1835
  }
1814
- async function gt() {
1836
+ async function vt() {
1815
1837
  let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
1816
1838
  if (!e?.getHighEntropyValues) return {};
1817
1839
  try {
@@ -1823,7 +1845,7 @@ async function gt() {
1823
1845
  }
1824
1846
  //#endregion
1825
1847
  //#region src/core/version.ts
1826
- var _t = "2.6.4", J = {
1848
+ var yt = "2.7.0", J = {
1827
1849
  accent: "#E1B600",
1828
1850
  accentText: "#1B1C23",
1829
1851
  background: "#0e0f13",
@@ -1856,15 +1878,15 @@ function X(e, t) {
1856
1878
  }
1857
1879
  //#endregion
1858
1880
  //#region src/ui/styles.ts
1859
- var Z = "havik-player-styles", vt = "\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";
1860
- function yt(e = document) {
1881
+ var Z = "havik-player-styles", bt = "\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";
1882
+ function xt(e = document) {
1861
1883
  if (e.getElementById(Z)) return;
1862
1884
  let t = e.createElement("style");
1863
- t.id = Z, t.textContent = vt, e.head.appendChild(t);
1885
+ t.id = Z, t.textContent = bt, e.head.appendChild(t);
1864
1886
  }
1865
1887
  //#endregion
1866
1888
  //#region src/ui/icons.ts
1867
- var bt = {
1889
+ var St = {
1868
1890
  play: "<path d=\"M8 5v14l11-7z\"/>",
1869
1891
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
1870
1892
  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\"/>",
@@ -1878,20 +1900,20 @@ var bt = {
1878
1900
  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\"/>"
1879
1901
  };
1880
1902
  function Q(e) {
1881
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${bt[e]}</svg>`;
1903
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${St[e]}</svg>`;
1882
1904
  }
1883
1905
  //#endregion
1884
1906
  //#region src/ui/controls.ts
1885
- var xt = 3e3, St = /* @__PURE__ */ new Set([
1907
+ var Ct = 3e3, wt = /* @__PURE__ */ new Set([
1886
1908
  "GONE",
1887
1909
  "NOT_FOUND",
1888
1910
  "INVALID_URN",
1889
1911
  "UNAUTHORIZED"
1890
1912
  ]);
1891
- function Ct(e) {
1892
- return new wt(e);
1913
+ function Tt(e) {
1914
+ return new Et(e);
1893
1915
  }
1894
- var wt = class {
1916
+ var Et = class {
1895
1917
  root;
1896
1918
  video;
1897
1919
  player;
@@ -1933,7 +1955,7 @@ var wt = class {
1933
1955
  scrubbing = !1;
1934
1956
  offFns = [];
1935
1957
  constructor(e) {
1936
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? xt, 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, yt(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");
1958
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Ct, 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, xt(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");
1937
1959
  let t = (e, t, n) => {
1938
1960
  let r = this.doc.createElement(e);
1939
1961
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -2024,7 +2046,7 @@ var wt = class {
2024
2046
  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);
2025
2047
  } else {
2026
2048
  let r = this.doc.createElement("div");
2027
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !St.has(this.lastError.code)) {
2049
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !wt.has(this.lastError.code)) {
2028
2050
  let e = this.doc.createElement("button");
2029
2051
  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);
2030
2052
  }
@@ -2065,7 +2087,7 @@ var wt = class {
2065
2087
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
2066
2088
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
2067
2089
  }
2068
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Tt(this.video.currentTime)} / ${Tt(this.video.duration)}`);
2090
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Dt(this.video.currentTime)} / ${Dt(this.video.duration)}`);
2069
2091
  }
2070
2092
  syncLive() {
2071
2093
  let e = this.player.getStats(), t = e.isLive !== !1;
@@ -2115,17 +2137,17 @@ var wt = class {
2115
2137
  renderQualityMenu() {
2116
2138
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
2117
2139
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
2118
- for (let n of e) this.menu.append(this.menuItem(Et(n), t === n.index, () => this.player.setQuality(n.index)));
2140
+ for (let n of e) this.menu.append(this.menuItem(Ot(n), t === n.index, () => this.player.setQuality(n.index)));
2119
2141
  }
2120
2142
  renderAudioMenu() {
2121
2143
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
2122
2144
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
2123
- for (let t of e) this.menu.append(this.menuItem(Dt(t), t.default, () => this.player.setAudioTrack(t.id)));
2145
+ for (let t of e) this.menu.append(this.menuItem(kt(t), t.default, () => this.player.setAudioTrack(t.id)));
2124
2146
  }
2125
2147
  renderCcMenu() {
2126
2148
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
2127
2149
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
2128
- for (let t of e) this.menu.append(this.menuItem(Ot(t), !1, () => this.player.setTextTrack(t.id)));
2150
+ for (let t of e) this.menu.append(this.menuItem(At(t), !1, () => this.player.setTextTrack(t.id)));
2129
2151
  }
2130
2152
  onTracksChanged() {
2131
2153
  this.openMenu && this.renderMenu(this.openMenu);
@@ -2225,32 +2247,32 @@ var wt = class {
2225
2247
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
2226
2248
  }
2227
2249
  };
2228
- function Tt(e) {
2250
+ function Dt(e) {
2229
2251
  (!Number.isFinite(e) || e < 0) && (e = 0);
2230
2252
  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");
2231
2253
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
2232
2254
  }
2233
- function Et(e) {
2255
+ function Ot(e) {
2234
2256
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
2235
2257
  }
2236
- function Dt(e) {
2258
+ function kt(e) {
2237
2259
  return e.name || e.lang || `Track ${e.id}`;
2238
2260
  }
2239
- function Ot(e) {
2261
+ function At(e) {
2240
2262
  return e.name || e.lang || `Track ${e.id}`;
2241
2263
  }
2242
2264
  //#endregion
2243
2265
  //#region src/managed/index.ts
2244
- var kt = 6e3, At = 45e3, jt = 2e3, Mt = 3, Nt = 2e3, Pt = 48e4;
2245
- async function Ft(e) {
2266
+ var jt = 6e3, Mt = 45e3, Nt = 2e3, Pt = 3, Ft = 2e3, It = 48e4;
2267
+ async function Lt(e) {
2246
2268
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
2247
2269
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: baseUrl is required");
2248
2270
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
2249
2271
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
2250
- let t = new It(e);
2272
+ let t = new Rt(e);
2251
2273
  return await t.start(), t;
2252
2274
  }
2253
- var It = class {
2275
+ var Rt = class {
2254
2276
  opts;
2255
2277
  abort = new AbortController();
2256
2278
  listeners = {};
@@ -2276,6 +2298,7 @@ var It = class {
2276
2298
  analyticsSession = null;
2277
2299
  detachMediaListeners;
2278
2300
  playRequested = !1;
2301
+ idleClosed = !1;
2279
2302
  pendingStartLoad = !1;
2280
2303
  constructor(e) {
2281
2304
  this.opts = e;
@@ -2337,7 +2360,7 @@ var It = class {
2337
2360
  setTimeout(() => {
2338
2361
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
2339
2362
  phase: i ? "unavailable" : "tooEarly",
2340
- retryInMs: At,
2363
+ retryInMs: Mt,
2341
2364
  attempt: 1,
2342
2365
  elapsedMs: 0,
2343
2366
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -2376,13 +2399,13 @@ var It = class {
2376
2399
  !a && !p() && h();
2377
2400
  }, e);
2378
2401
  };
2379
- _(kt), this.armingSub = E({
2402
+ _(jt), this.armingSub = E({
2380
2403
  eventsBaseUrl: t,
2381
2404
  matchUrn: this.opts.matchUrn,
2382
2405
  credential: this.opts.credential,
2383
- onAlive: () => _(At),
2406
+ onAlive: () => _(Mt),
2384
2407
  onState: (e) => {
2385
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * jt);
2408
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Nt);
2386
2409
  },
2387
2410
  onError: () => h()
2388
2411
  });
@@ -2420,18 +2443,23 @@ var It = class {
2420
2443
  this.destroyed || t !== this.loadGen || this.engine !== i || (this.analyticsSession?.noteReady(), this.emit("ready"));
2421
2444
  }, 0));
2422
2445
  }
2446
+ onAnalyticsIdle(e) {
2447
+ this.destroyed || this.engine !== e || (this.idleClosed = !0, this.pendingStartLoad = !1, e.stop());
2448
+ }
2423
2449
  startAnalytics(e, t) {
2424
2450
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
2425
2451
  let n = e.analytics?.sid;
2426
2452
  if (!n) return;
2427
- let r = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? ut(this.opts.baseUrl);
2453
+ let r = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? ft(this.opts.baseUrl);
2428
2454
  if (!r) return;
2429
- let i = this.opts.video, a = new dt({
2455
+ let i = this.opts.video, a = new pt({
2430
2456
  endpoint: r,
2431
2457
  sid: n,
2432
2458
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
2433
- sdkVersion: _t,
2434
- environment: ht(i),
2459
+ sdkVersion: yt,
2460
+ environment: _t(i),
2461
+ idleMs: typeof this.opts.analytics == "object" ? this.opts.analytics.idleMs : void 0,
2462
+ onIdle: () => this.onAnalyticsIdle(t),
2435
2463
  sample: () => {
2436
2464
  let e = this.engine?.getStats() ?? I, t = i.buffered.length ? Math.max(0, i.buffered.end(i.buffered.length - 1) - i.currentTime) : 0;
2437
2465
  return {
@@ -2447,7 +2475,7 @@ var It = class {
2447
2475
  let o = () => a.noteSeek(), s = () => a.noteVolume(i.muted, i.volume);
2448
2476
  i.addEventListener("seeking", o), i.addEventListener("volumechange", s), this.detachMediaListeners = () => {
2449
2477
  i.removeEventListener("seeking", o), i.removeEventListener("volumechange", s);
2450
- }, a.start(), a.seedVolume(i.muted, i.volume), a.notePresentation(this.currentPresentation()), gt().then((e) => a.noteEnvironment(e)), this.opts.autoplay === !1 && a.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
2478
+ }, a.start(), a.seedVolume(i.muted, i.volume), a.notePresentation(this.currentPresentation()), vt().then((e) => a.noteEnvironment(e)), this.opts.autoplay === !1 && a.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
2451
2479
  }
2452
2480
  currentPresentation() {
2453
2481
  let e = this.opts.video;
@@ -2518,7 +2546,7 @@ var It = class {
2518
2546
  r = 0, i = !1;
2519
2547
  },
2520
2548
  onError: () => {
2521
- this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= Mt && (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.`))));
2549
+ this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= Pt && (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.`))));
2522
2550
  }
2523
2551
  });
2524
2552
  }
@@ -2540,7 +2568,7 @@ var It = class {
2540
2568
  scheduleRejoin(e) {
2541
2569
  this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
2542
2570
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
2543
- }, Math.random() * jt));
2571
+ }, Math.random() * Nt));
2544
2572
  }
2545
2573
  cancelRejoin() {
2546
2574
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -2550,7 +2578,7 @@ var It = class {
2550
2578
  this.teardownPlayback(), this.setState("waiting"), this.beginArming(e, void 0, !0);
2551
2579
  }
2552
2580
  teardownPlayback() {
2553
- this.stopStats(), this.stopRefresh(), this.cancelRejoin(), this.closeArming(), this.closeLiveState(), this.detachMedia?.(), this.engine?.destroy(), this.engine = null, this.pendingStartLoad = !1;
2581
+ this.stopStats(), this.stopRefresh(), this.cancelRejoin(), this.closeArming(), this.closeLiveState(), this.detachMedia?.(), this.engine?.destroy(), this.engine = null, this.pendingStartLoad = !1, this.idleClosed = !1;
2554
2582
  }
2555
2583
  mountControls() {
2556
2584
  let e = this.opts.controls ?? "custom", t = this.opts.video;
@@ -2564,7 +2592,7 @@ var It = class {
2564
2592
  }
2565
2593
  t.controls = !1;
2566
2594
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
2567
- r && r.insertBefore(n, t), n.appendChild(t), X(n, this.opts.theme), this.container = n, this.controlBar = Ct({
2595
+ r && r.insertBefore(n, t), n.appendChild(t), X(n, this.opts.theme), this.container = n, this.controlBar = Tt({
2568
2596
  root: n,
2569
2597
  video: t,
2570
2598
  player: this,
@@ -2594,7 +2622,7 @@ var It = class {
2594
2622
  }
2595
2623
  startStats() {
2596
2624
  this.stopStats();
2597
- let e = Math.max(500, this.opts.statsIntervalMs ?? Nt);
2625
+ let e = Math.max(500, this.opts.statsIntervalMs ?? Ft);
2598
2626
  this.statsTimer = setInterval(() => {
2599
2627
  if (!this.engine) return;
2600
2628
  let e = this.engine.getStats();
@@ -2606,7 +2634,7 @@ var It = class {
2606
2634
  }
2607
2635
  scheduleLicenseRefresh() {
2608
2636
  this.stopRefresh();
2609
- let e = this.opts.licenseRefreshMs ?? Pt;
2637
+ let e = this.opts.licenseRefreshMs ?? It;
2610
2638
  e && this._descriptor?.drmEnabled && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
2611
2639
  }
2612
2640
  stopRefresh() {
@@ -2629,7 +2657,10 @@ var It = class {
2629
2657
  async play() {
2630
2658
  if (this.destroyed) return;
2631
2659
  let e = !this.playRequested;
2632
- this.playRequested = !0;
2660
+ if (this.playRequested = !0, this.idleClosed && this._state !== "ended" && this._state !== "error") {
2661
+ this.idleClosed = !1, await this.reload();
2662
+ return;
2663
+ }
2633
2664
  let t = this.pendingStartLoad && this._state !== "ended" && this._state !== "error";
2634
2665
  this.opts.autoplay === !1 && this.analyticsSession?.notePlayIntent(), t && (this.pendingStartLoad = !1, this.setState("loading"), this.engine?.startLoad());
2635
2666
  try {
@@ -2758,7 +2789,7 @@ function $(e) {
2758
2789
  }
2759
2790
  //#endregion
2760
2791
  //#region src/embed/host.ts
2761
- function Lt(e) {
2792
+ function zt(e) {
2762
2793
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
2763
2794
  if (!e.src) throw Error("mountEmbed: opts.src is required");
2764
2795
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -2817,8 +2848,8 @@ function Lt(e) {
2817
2848
  }
2818
2849
  //#endregion
2819
2850
  //#region src/embed/page.ts
2820
- var Rt = /^[A-Za-z0-9:_.-]{1,128}$/;
2821
- function zt() {
2851
+ var Bt = /^[A-Za-z0-9:_.-]{1,128}$/;
2852
+ function Vt() {
2822
2853
  let e = window.HAVIK_EMBED_CONFIG;
2823
2854
  if (e) return {
2824
2855
  baseUrl: e.baseUrl ?? "",
@@ -2840,9 +2871,9 @@ function zt() {
2840
2871
  waitForLive: !0
2841
2872
  };
2842
2873
  }
2843
- async function Bt() {
2844
- let e = zt();
2845
- Ht();
2874
+ async function Ht() {
2875
+ let e = Vt();
2876
+ Wt();
2846
2877
  let t = document.createElement("video");
2847
2878
  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);
2848
2879
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -2897,12 +2928,12 @@ async function Bt() {
2897
2928
  code: e.code,
2898
2929
  message: e.message
2899
2930
  })), e.on("autoplayblocked", () => {
2900
- i({ type: "oddin:autoplayblocked" }), Vt(t, e);
2931
+ i({ type: "oddin:autoplayblocked" }), Ut(t, e);
2901
2932
  });
2902
2933
  }, s = async (r) => {
2903
2934
  a?.destroy(), a = null;
2904
2935
  try {
2905
- let n = await Ft({
2936
+ let n = await Lt({
2906
2937
  video: t,
2907
2938
  baseUrl: e.baseUrl,
2908
2939
  matchUrn: r,
@@ -2963,7 +2994,7 @@ async function Bt() {
2963
2994
  a?.retry();
2964
2995
  break;
2965
2996
  case "oddin:load":
2966
- typeof e.matchUrn == "string" && Rt.test(e.matchUrn) && s(e.matchUrn);
2997
+ typeof e.matchUrn == "string" && Bt.test(e.matchUrn) && s(e.matchUrn);
2967
2998
  break;
2968
2999
  case "oddin:destroy": c();
2969
3000
  }
@@ -2975,18 +3006,18 @@ async function Bt() {
2975
3006
  protocol: 1
2976
3007
  });
2977
3008
  }
2978
- function Vt(e, t) {
3009
+ function Ut(e, t) {
2979
3010
  if (document.getElementById("havik-tap")) return;
2980
3011
  let n = document.createElement("button");
2981
3012
  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", () => {
2982
3013
  n.remove(), t.play();
2983
3014
  }), document.body.appendChild(n);
2984
3015
  }
2985
- function Ht() {
3016
+ function Wt() {
2986
3017
  let e = document.createElement("style");
2987
3018
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
2988
3019
  }
2989
3020
  //#endregion
2990
- export { J as ODDIN_THEME, n as PlaybackError, X as applyTheme, Bt as bootEmbed, _e as clearDeviceId, Ft as createPlayer, D as deriveEventsBaseUrl, Oe as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ue as fetchCatalog, fe as fetchTournament, me as flattenMatches, A as getDeviceId, Ae as isLowLatencyManifest, $ as isOddinMessage, ye as licenseRequestHeaders, Lt as mountEmbed, oe as pollToLive, y as resolveStream, E as subscribeLiveState, C as videoFullscreenActive, he as watchStatus };
3021
+ export { J as ODDIN_THEME, n as PlaybackError, X as applyTheme, Ht as bootEmbed, _e as clearDeviceId, Lt as createPlayer, D as deriveEventsBaseUrl, Oe as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ue as fetchCatalog, fe as fetchTournament, me as flattenMatches, A as getDeviceId, Ae as isLowLatencyManifest, $ as isOddinMessage, ye as licenseRequestHeaders, zt as mountEmbed, oe as pollToLive, y as resolveStream, E as subscribeLiveState, C as videoFullscreenActive, he as watchStatus };
2991
3022
 
2992
3023
  //# sourceMappingURL=index.js.map