@oddin-gg/havik-player 2.27.1 → 2.28.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
@@ -8,8 +8,9 @@ var n = class extends Error {
8
8
  serverCode;
9
9
  liveStartsAtMs;
10
10
  country;
11
+ detail;
11
12
  constructor(e, t, n, r) {
12
- super(n), this.name = "PlaybackError", this.code = e, this.httpStatus = t, this.retryAfterMs = r?.retryAfterMs, this.requestId = r?.requestId, this.serverCode = r?.serverCode, this.liveStartsAtMs = r?.liveStartsAtMs, this.country = r?.country, r?.cause !== void 0 && (this.cause = r.cause);
13
+ super(n), this.name = "PlaybackError", this.code = e, this.httpStatus = t, this.retryAfterMs = r?.retryAfterMs, this.requestId = r?.requestId, this.serverCode = r?.serverCode, this.liveStartsAtMs = r?.liveStartsAtMs, this.country = r?.country, this.detail = r?.detail, r?.cause !== void 0 && (this.cause = r.cause);
13
14
  }
14
15
  };
15
16
  function r(e) {
@@ -315,25 +316,25 @@ function x(e) {
315
316
  function _e(e) {
316
317
  return e.fullscreenElement ?? e.webkitFullscreenElement ?? null;
317
318
  }
318
- function ve(e, t) {
319
+ function S(e, t) {
319
320
  let n = _e(x(e));
320
321
  return n != null && (n === e || t != null && n === t) || e.webkitDisplayingFullscreen === !0;
321
322
  }
322
- function S(e, t) {
323
+ function C(e, t) {
323
324
  let n = t ?? e, r = x(e);
324
325
  if (typeof n.requestFullscreen == "function") return r.fullscreenEnabled !== !1;
325
326
  if (typeof n.webkitRequestFullscreen == "function") return r.webkitFullscreenEnabled !== !1;
326
327
  let i = e;
327
328
  return i.webkitSupportsFullscreen === !0 && typeof i.webkitEnterFullscreen == "function" || e.readyState < 1;
328
329
  }
329
- async function C(e, t) {
330
+ async function w(e, t) {
330
331
  let n = t ?? e;
331
332
  if (typeof n.requestFullscreen == "function") return await n.requestFullscreen(), !0;
332
333
  if (typeof n.webkitRequestFullscreen == "function") return n.webkitRequestFullscreen(), !0;
333
334
  let r = e;
334
335
  return r.webkitSupportsFullscreen && typeof r.webkitEnterFullscreen == "function" ? (r.webkitEnterFullscreen(), !0) : !1;
335
336
  }
336
- async function w(e, t) {
337
+ async function T(e, t) {
337
338
  let n = e;
338
339
  if (n.webkitDisplayingFullscreen) {
339
340
  n.webkitExitFullscreen?.();
@@ -344,7 +345,7 @@ async function w(e, t) {
344
345
  }
345
346
  //#endregion
346
347
  //#region src/core/catalog.ts
347
- async function ye(e) {
348
+ async function ve(e) {
348
349
  let t = new URLSearchParams();
349
350
  e.status && t.set("status", Array.isArray(e.status) ? e.status.join(",") : e.status), e.sport && t.set("sport", e.sport);
350
351
  let i = t.toString(), a = f(e.baseUrl, "/v1/catalog" + (i ? `?${i}` : "")), o = new Headers();
@@ -362,12 +363,12 @@ async function ye(e) {
362
363
  };
363
364
  if (!s.ok) throw new n(r(s.status), s.status, `catalog request failed (HTTP ${s.status})`);
364
365
  return {
365
- tournaments: be(await s.json()),
366
+ tournaments: ye(await s.json()),
366
367
  etag: s.headers.get("etag") ?? void 0,
367
368
  notModified: !1
368
369
  };
369
370
  }
370
- function be(e) {
371
+ function ye(e) {
371
372
  return (e.tournaments ?? []).map((e) => {
372
373
  let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
373
374
  return {
@@ -389,7 +390,7 @@ function be(e) {
389
390
  };
390
391
  });
391
392
  }
392
- async function xe(e) {
393
+ async function be(e) {
393
394
  let t = f(e.baseUrl, `/v1/tournaments/${encodeURIComponent(e.urn)}`), i = new Headers();
394
395
  e.etag && i.set("If-None-Match", e.etag);
395
396
  let a = await m(t, {
@@ -409,13 +410,13 @@ async function xe(e) {
409
410
  };
410
411
  if (!a.ok) throw new n(r(a.status), a.status, `tournament request failed (HTTP ${a.status})`);
411
412
  return {
412
- tournament: Se(await a.json()),
413
+ tournament: xe(await a.json()),
413
414
  etag: a.headers.get("etag") ?? void 0,
414
415
  notModified: !1,
415
416
  notFound: !1
416
417
  };
417
418
  }
418
- function Se(e) {
419
+ function xe(e) {
419
420
  let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
420
421
  return {
421
422
  urn: t,
@@ -435,18 +436,18 @@ function Se(e) {
435
436
  }))
436
437
  };
437
438
  }
438
- function Ce(e) {
439
+ function Se(e) {
439
440
  return e.tournaments.flatMap((e) => e.matches);
440
441
  }
441
442
  //#endregion
442
443
  //#region src/core/poll.ts
443
- var we = /* @__PURE__ */ new Set([
444
+ var Ce = /* @__PURE__ */ new Set([
444
445
  "NOT_FOUND",
445
446
  "GEO_DENIED",
446
447
  "GONE",
447
448
  "INVALID_URN"
448
449
  ]);
449
- function Te(e) {
450
+ function we(e) {
450
451
  let t = Math.max(2e3, e.intervalMs ?? 1e4), i = new AbortController(), a, o, s, c = !1, d = () => {
451
452
  c || (a = setTimeout(p, t));
452
453
  }, p = async () => {
@@ -479,7 +480,7 @@ function Te(e) {
479
480
  } catch (t) {
480
481
  if (!i.signal.aborted) {
481
482
  let r = t instanceof n ? t : new n("NETWORK", 0, String(t));
482
- we.has(r.code) && (c = !0), e.onError?.(r);
483
+ Ce.has(r.code) && (c = !0), e.onError?.(r);
483
484
  }
484
485
  } finally {
485
486
  d();
@@ -491,16 +492,16 @@ function Te(e) {
491
492
  }
492
493
  //#endregion
493
494
  //#region src/core/events.ts
494
- var Ee = 45e3, T = class extends Error {
495
+ var Te = 45e3, E = class extends Error {
495
496
  status;
496
497
  terminal;
497
498
  constructor(e) {
498
499
  let t = e === 410;
499
500
  super(t ? `live-state subscribe failed: HTTP ${e} — match is gone, not retrying` : e === 404 ? `live-state subscribe failed: HTTP ${e} — match not in the catalog, retrying slowly` : `live-state subscribe failed: HTTP ${e}`), this.name = "LiveStateHttpError", this.status = e, this.terminal = t;
500
501
  }
501
- }, De = 3e5;
502
- function E(e) {
503
- let t = Math.max(250, e.minBackoffMs ?? 1e3), n = Math.max(t, e.maxBackoffMs ?? 15e3), r = Math.max(t, e.notFoundRetryMs ?? De), i = e.idleTimeoutMs ?? Ee, a = Number.isFinite(i) && i > 0 ? i : 0, o = new AbortController(), s = !1, l = (e) => new Promise((t) => {
502
+ }, Ee = 3e5;
503
+ function D(e) {
504
+ let t = Math.max(250, e.minBackoffMs ?? 1e3), n = Math.max(t, e.maxBackoffMs ?? 15e3), r = Math.max(t, e.notFoundRetryMs ?? Ee), i = e.idleTimeoutMs ?? Te, a = Number.isFinite(i) && i > 0 ? i : 0, o = new AbortController(), s = !1, l = (e) => new Promise((t) => {
504
505
  if (o.signal.aborted) return t();
505
506
  let n = setTimeout(t, e);
506
507
  o.signal.addEventListener("abort", () => {
@@ -567,7 +568,7 @@ function E(e) {
567
568
  });
568
569
  if (clearTimeout(_), s) return;
569
570
  if (!o.ok || !o.body) {
570
- let t = new T(o.status);
571
+ let t = new E(o.status);
571
572
  h = t.terminal, g = o.status === 404, e.onError?.(t);
572
573
  } else await p(o.body, () => {
573
574
  i = t;
@@ -585,7 +586,7 @@ function E(e) {
585
586
  s || (s = !0, o.abort());
586
587
  } };
587
588
  }
588
- function D(e) {
589
+ function O(e) {
589
590
  try {
590
591
  let t = new URL(e);
591
592
  return t.hostname = `events.${t.hostname}`, t.pathname = "", t.search = "", t.origin;
@@ -595,27 +596,27 @@ function D(e) {
595
596
  }
596
597
  //#endregion
597
598
  //#region src/core/drm/deviceId.ts
598
- var O = "havik.player.deviceId", k;
599
- function A() {
599
+ var De = "havik.player.deviceId", k;
600
+ function Oe() {
600
601
  if (k) return k;
601
602
  try {
602
- let e = globalThis.localStorage?.getItem(O);
603
+ let e = globalThis.localStorage?.getItem(De);
603
604
  if (e) return k = e, e;
604
605
  } catch {}
605
- let e = ke();
606
+ let e = Ae();
606
607
  k = e;
607
608
  try {
608
- globalThis.localStorage?.setItem(O, e);
609
+ globalThis.localStorage?.setItem(De, e);
609
610
  } catch {}
610
611
  return e;
611
612
  }
612
- function Oe() {
613
+ function ke() {
613
614
  k = void 0;
614
615
  try {
615
- globalThis.localStorage?.removeItem(O);
616
+ globalThis.localStorage?.removeItem(De);
616
617
  } catch {}
617
618
  }
618
- function ke() {
619
+ function Ae() {
619
620
  let e = globalThis.crypto;
620
621
  if (e && typeof e.randomUUID == "function") return e.randomUUID();
621
622
  let t = /* @__PURE__ */ new Uint8Array(16);
@@ -627,32 +628,32 @@ function ke() {
627
628
  }
628
629
  //#endregion
629
630
  //#region src/core/drm/headers.ts
630
- function Ae(e, t, n = {}) {
631
+ function je(e, t, n = {}) {
631
632
  let r = {
632
633
  "x-api-key": t.apiKey,
633
634
  "X-Match-Urn": e.matchUrn,
634
- "X-Device-Id": n.deviceId ?? A(),
635
+ "X-Device-Id": n.deviceId ?? Oe(),
635
636
  "Content-Type": "application/octet-stream"
636
637
  };
637
638
  return n.userId && (r["X-User-Id"] = n.userId), r;
638
639
  }
639
640
  //#endregion
640
641
  //#region src/core/bandwidthMemory.ts
641
- var j = "havik.player.bandwidth", je = .8, Me = 2e7, Ne = 3e5;
642
- function Pe() {
642
+ var Me = "havik.player.bandwidth", Ne = .8, Pe = 2e7, Fe = 3e5;
643
+ function Ie() {
643
644
  return globalThis.navigator?.connection;
644
645
  }
645
- function Fe() {
646
- let e = Pe()?.type;
646
+ function Le() {
647
+ let e = Ie()?.type;
647
648
  return typeof e == "string" && e !== "" && e !== "unknown" ? e : "any";
648
649
  }
649
- function Ie() {
650
- let e = Pe()?.downlink;
650
+ function Re() {
651
+ let e = Ie()?.downlink;
651
652
  return typeof e == "number" && Number.isFinite(e) && e > 0 ? e * 1e6 : void 0;
652
653
  }
653
- function Le() {
654
+ function ze() {
654
655
  try {
655
- let e = globalThis.localStorage?.getItem(j);
656
+ let e = globalThis.localStorage?.getItem(Me);
656
657
  if (!e) return {};
657
658
  let t = JSON.parse(e);
658
659
  return t && typeof t == "object" && !Array.isArray(t) ? t : {};
@@ -660,36 +661,36 @@ function Le() {
660
661
  return {};
661
662
  }
662
663
  }
663
- function Re(e = Date.now()) {
664
- let t = Le()[Fe()];
664
+ function Be(e = Date.now()) {
665
+ let t = ze()[Le()];
665
666
  if (!t || typeof t.bps != "number" || typeof t.at != "number" || !Number.isFinite(t.bps) || t.bps <= 0) return;
666
667
  let n = e - t.at;
667
668
  if (n < 0 || n > 2592e5) return;
668
- let r = Ie(), i = r === void 0 ? t.bps : Math.min(t.bps, r);
669
- return Math.round(Math.min(Me, Math.max(Ne, i * je)));
669
+ let r = Re(), i = r === void 0 ? t.bps : Math.min(t.bps, r);
670
+ return Math.round(Math.min(Pe, Math.max(Fe, i * Ne)));
670
671
  }
671
- function ze(e, t = Date.now()) {
672
+ function Ve(e, t = Date.now()) {
672
673
  if (!Number.isFinite(e) || e <= 0) return;
673
- let n = Le();
674
- n[Fe()] = {
674
+ let n = ze();
675
+ n[Le()] = {
675
676
  bps: Math.round(e),
676
677
  at: t
677
678
  };
678
679
  try {
679
- globalThis.localStorage?.setItem(j, JSON.stringify(n));
680
+ globalThis.localStorage?.setItem(Me, JSON.stringify(n));
680
681
  } catch {}
681
682
  }
682
- function Be() {
683
+ function He() {
683
684
  try {
684
- globalThis.localStorage?.removeItem(j);
685
+ globalThis.localStorage?.removeItem(Me);
685
686
  } catch {}
686
687
  }
687
688
  //#endregion
688
689
  //#region src/core/drm/cdm.ts
689
- var Ve = /* @__PURE__ */ new WeakMap();
690
- function He(e, t) {
691
- let n = Ve.get(e);
692
- n || (n = /* @__PURE__ */ new Map(), Ve.set(e, n));
690
+ var Ue = /* @__PURE__ */ new WeakMap();
691
+ function We(e, t) {
692
+ let n = Ue.get(e);
693
+ n || (n = /* @__PURE__ */ new Map(), Ue.set(e, n));
693
694
  let r = n.get(t.keySystem);
694
695
  if (r) return r;
695
696
  let i = t.createMediaKeys().catch((e) => {
@@ -697,22 +698,22 @@ function He(e, t) {
697
698
  });
698
699
  return n.set(t.keySystem, i), i;
699
700
  }
700
- function M() {
701
+ function A() {
701
702
  return typeof navigator < "u" && typeof navigator.requestMediaKeySystemAccess == "function";
702
703
  }
703
- var Ue = ["SW_SECURE_DECODE", "SW_SECURE_CRYPTO"];
704
- function We(e) {
704
+ var j = ["SW_SECURE_DECODE", "SW_SECURE_CRYPTO"];
705
+ function Ge(e) {
705
706
  return e.startsWith("com.widevine");
706
707
  }
707
- function Ge(e, t) {
708
- if (!We(e)) return t;
708
+ function M(e, t) {
709
+ if (!Ge(e)) return t;
709
710
  let n = (e, t) => {
710
711
  let n = e.videoCapabilities;
711
712
  return !n?.length || !n.some((e) => e.robustness) ? !1 : n.every((e) => {
712
- let n = Ue.indexOf(e.robustness);
713
- return n === -1 || Ue.indexOf(t) > n;
713
+ let n = j.indexOf(e.robustness);
714
+ return n === -1 || j.indexOf(t) > n;
714
715
  });
715
- }, r = Ue.flatMap((e) => t.filter((t) => n(t, e)).map((t) => ({
716
+ }, r = j.flatMap((e) => t.filter((t) => n(t, e)).map((t) => ({
716
717
  ...t,
717
718
  videoCapabilities: t.videoCapabilities?.map((t) => ({
718
719
  ...t,
@@ -722,10 +723,10 @@ function Ge(e, t) {
722
723
  return [...t, ...r];
723
724
  }
724
725
  function Ke(e) {
725
- return (t, n) => M() ? navigator.requestMediaKeySystemAccess(t, Ge(t, n)).then((t) => ({
726
+ return (t, n) => A() ? navigator.requestMediaKeySystemAccess(t, M(t, n)).then((t) => ({
726
727
  keySystem: t.keySystem,
727
728
  getConfiguration: () => t.getConfiguration(),
728
- createMediaKeys: () => He(e, t)
729
+ createMediaKeys: () => We(e, t)
729
730
  })) : Promise.reject(/* @__PURE__ */ Error("navigator.requestMediaKeySystemAccess is unavailable — EME needs a secure context (https, or localhost)"));
730
731
  }
731
732
  var N = {
@@ -734,7 +735,7 @@ var N = {
734
735
  scheme: "cbcs"
735
736
  };
736
737
  function qe(e) {
737
- if (!M()) return;
738
+ if (!A()) return;
738
739
  let t = (e, t) => ({
739
740
  contentType: e,
740
741
  robustness: t,
@@ -781,7 +782,7 @@ function Xe(e) {
781
782
  }]
782
783
  }, i = (async () => {
783
784
  try {
784
- return await navigator.requestMediaKeySystemAccess("com.widevine.alpha", Ge("com.widevine.alpha", [r])), !0;
785
+ return await navigator.requestMediaKeySystemAccess("com.widevine.alpha", M("com.widevine.alpha", [r])), !0;
785
786
  } catch {
786
787
  return !1;
787
788
  }
@@ -789,7 +790,7 @@ function Xe(e) {
789
790
  return Ye.set(t, i), i;
790
791
  }
791
792
  function Ze() {
792
- if (M()) for (let e of Object.values(Je)) Xe(e);
793
+ if (A()) for (let e of Object.values(Je)) Xe(e);
793
794
  }
794
795
  function Qe(e) {
795
796
  let t = P(e);
@@ -818,7 +819,7 @@ function rt(e) {
818
819
  robustness: "",
819
820
  encryptionScheme: "cbcs"
820
821
  }]
821
- }] : Ge("com.widevine.alpha", [{
822
+ }] : M("com.widevine.alpha", [{
822
823
  initDataTypes: ["cenc"],
823
824
  videoCapabilities: [{
824
825
  contentType: et,
@@ -975,36 +976,43 @@ var ht = class {
975
976
  this.video.removeEventListener("timeupdate", e), this.video.removeEventListener("seeking", e);
976
977
  };
977
978
  }
978
- }, gt = .25;
979
- function _t(e, t) {
979
+ }, gt = .25, _t = 1e3;
980
+ function vt(e, t) {
980
981
  let n = null, r = (r) => {
981
982
  n = r === "buffering" ? e.currentTime : null, t(r);
982
- }, i = [
983
- ["playing", () => r("playing")],
983
+ }, i = () => typeof performance < "u" ? performance.now() : Date.now(), a = i(), o = e.currentTime, s = () => {
984
+ let t = e.currentTime;
985
+ t > o && !e.seeking && !e.paused && (a = i()), o = t;
986
+ }, c = [
987
+ ["playing", () => {
988
+ a = i(), o = e.currentTime, r("playing");
989
+ }],
984
990
  ["waiting", () => r("buffering")],
985
991
  ["stalled", () => {
986
- !e.paused && e.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && r("buffering");
992
+ if (e.paused) return;
993
+ let t = e.readyState < HTMLMediaElement.HAVE_FUTURE_DATA, n = i() - a >= _t;
994
+ (t || n) && r("buffering");
987
995
  }],
988
996
  ["seeked", () => {
989
- n !== null && (n = e.currentTime);
997
+ o = e.currentTime, n !== null && (n = e.currentTime);
990
998
  }],
991
999
  ["timeupdate", () => {
992
- n !== null && !e.paused && !e.seeking && e.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA && e.currentTime - n >= gt && r("playing");
1000
+ s(), n !== null && !e.paused && !e.seeking && e.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA && e.currentTime - n >= gt && r("playing");
993
1001
  }],
994
1002
  ["pause", () => {
995
1003
  e.ended || r("paused");
996
1004
  }],
997
1005
  ["ended", () => r("ended")]
998
1006
  ];
999
- for (let [t, n] of i) e.addEventListener(t, n);
1007
+ for (let [t, n] of c) e.addEventListener(t, n);
1000
1008
  return () => {
1001
- for (let [t, n] of i) e.removeEventListener(t, n);
1009
+ for (let [t, n] of c) e.removeEventListener(t, n);
1002
1010
  };
1003
1011
  }
1004
1012
  //#endregion
1005
1013
  //#region src/core/engine/native.ts
1006
- var vt = 2e3, yt = 3, z = 500, bt = 6e3, xt = .25, St = 250, Ct = .05, wt = 4, Tt = 12e3, Et = .25, Dt = 1e3;
1007
- function Ot(e) {
1014
+ var yt = 2e3, bt = 3, z = 500, xt = 6e3, St = .25, Ct = 250, wt = .05, Tt = 4, Et = 12e3, Dt = .25, Ot = 1e3, kt = 8e3, At = .05, jt = 2, Mt = 3e4;
1015
+ function Nt(e) {
1008
1016
  try {
1009
1017
  let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
1010
1018
  return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
@@ -1020,7 +1028,7 @@ function V() {
1020
1028
  let e = "WebKitMediaKeys" in window || typeof HTMLMediaElement < "u" && "webkitSetMediaKeys" in HTMLMediaElement.prototype, t = typeof navigator < "u" && /Apple/i.test(navigator.vendor ?? "");
1021
1029
  return e || t;
1022
1030
  }
1023
- function kt() {
1031
+ function Pt() {
1024
1032
  if (typeof navigator > "u" || !V()) return !1;
1025
1033
  let e = navigator.userAgent ?? "";
1026
1034
  return /iPhone|iPad|iPod/.test(e) ? !0 : /Macintosh/.test(e) && typeof navigator.maxTouchPoints == "number" && navigator.maxTouchPoints > 1;
@@ -1046,6 +1054,11 @@ var H = class {
1046
1054
  edgeStampedAt = null;
1047
1055
  lastTickAt = null;
1048
1056
  endedEmitted = !1;
1057
+ lastPlayhead = null;
1058
+ playheadStampedAt = null;
1059
+ movingSince = null;
1060
+ stuckReloads = 0;
1061
+ stuckReloadsTotal = 0;
1049
1062
  fairplayKeyIV;
1050
1063
  loadStarted;
1051
1064
  preloadTouched = !1;
@@ -1076,7 +1089,7 @@ var H = class {
1076
1089
  type: "error",
1077
1090
  error: L() ? new n("INTERNAL", 0, "native HLS playback error") : new n("NETWORK", 0, "native HLS playback error while offline"),
1078
1091
  fatal: !0
1079
- }), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = jt(t.manifestUrl, t.analytics?.sid), e.load();
1092
+ }), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = It(t.manifestUrl, t.analytics?.sid), e.load();
1080
1093
  }
1081
1094
  startLoad() {
1082
1095
  this.loadStarted || (this.loadStarted = !0, this.restorePreload());
@@ -1104,7 +1117,7 @@ var H = class {
1104
1117
  };
1105
1118
  this.onEncrypted = (n) => {
1106
1119
  let a = n;
1107
- a.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = Ot(a.initData), (async () => {
1120
+ a.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = Nt(a.initData), (async () => {
1108
1121
  try {
1109
1122
  let n = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
1110
1123
  initDataTypes: [a.initDataType],
@@ -1204,10 +1217,10 @@ var H = class {
1204
1217
  droppedFrames: t.dropped,
1205
1218
  decodedFrames: t.totalFrames
1206
1219
  };
1207
- if (this.cdnHost ||= mt(this.builtAt), this.cdnHost && (n.cdnHost = this.cdnHost), this.deps.descriptor.drmEnabled && (n.keySystem = "fairplay"), e.duration === Infinity) {
1220
+ if (this.cdnHost ||= mt(this.builtAt), this.cdnHost && (n.cdnHost = this.cdnHost), this.deps.descriptor.drmEnabled && (n.keySystem = "fairplay"), n.stuckReloads = this.stuckReloadsTotal, e.duration === Infinity) {
1208
1221
  n.isLive = !0;
1209
1222
  let t = this.liveEdgeEstimate(), r = t == null ? NaN : t - e.currentTime;
1210
- Number.isFinite(r) && (n.atLiveEdge = r <= wt, !e.paused && r >= 0 && (n.latencySeconds = r));
1223
+ Number.isFinite(r) && (n.atLiveEdge = r <= Tt, !e.paused && r >= 0 && (n.latencySeconds = r));
1211
1224
  } else Number.isFinite(e.duration) && e.duration > 0 && (n.isLive = !1);
1212
1225
  return n;
1213
1226
  }
@@ -1225,7 +1238,7 @@ var H = class {
1225
1238
  return;
1226
1239
  }
1227
1240
  let t = this.hiddenSince;
1228
- this.hiddenSince = null, t != null && e - t >= vt && this.armRefocus();
1241
+ this.hiddenSince = null, t != null && e - t >= yt && this.armRefocus();
1229
1242
  }, this.onPageShow = (e) => {
1230
1243
  e.persisted && this.armRefocus();
1231
1244
  }, document.addEventListener("visibilitychange", this.onVisibility), typeof window < "u" && window.addEventListener("pageshow", this.onPageShow));
@@ -1235,7 +1248,7 @@ var H = class {
1235
1248
  let e = this.liveEnd(), t = U, n = t(), r = 0, i = () => {
1236
1249
  if (this.refocusTimer = null, Number.isFinite(this.deps.video.duration)) return;
1237
1250
  r += z;
1238
- let a = this.liveEnd(), o = a != null && (e == null || a >= e + xt), s = Math.max(r, t() - n) >= bt;
1251
+ let a = this.liveEnd(), o = a != null && (e == null || a >= e + St), s = Math.max(r, t() - n) >= xt;
1239
1252
  if (o || s && a != null) {
1240
1253
  this.snapToLive();
1241
1254
  return;
@@ -1251,28 +1264,27 @@ var H = class {
1251
1264
  if (this.edgeTimer != null) return;
1252
1265
  let { video: e } = this.deps;
1253
1266
  this.onEdgeReset = (e) => {
1254
- this.lastBufferedEnd = null, this.bufferedAdvancedAt = null, e.type === "emptied" && this.resetEndStale();
1267
+ this.lastBufferedEnd = null, this.bufferedAdvancedAt = null, this.resetStuck(), e.type === "emptied" && this.resetEndStale();
1255
1268
  }, e.addEventListener("seeking", this.onEdgeReset), e.addEventListener("emptied", this.onEdgeReset), this.edgeTimer = setInterval(() => {
1256
1269
  if (e.duration !== Infinity) return;
1257
- this.checkEndStale();
1258
- let t = At(e, e.currentTime);
1259
- t != null && ((this.lastBufferedEnd == null || Math.abs(t - this.lastBufferedEnd) >= Ct) && (this.bufferedAdvancedAt = U()), this.lastBufferedEnd = t);
1260
- }, St);
1270
+ let t = U(), n = this.lastTickAt == null ? 0 : t - this.lastTickAt;
1271
+ this.lastTickAt = t;
1272
+ let r = Ft(e, e.currentTime);
1273
+ r != null && ((this.lastBufferedEnd == null || Math.abs(r - this.lastBufferedEnd) >= wt) && (this.bufferedAdvancedAt = t), this.lastBufferedEnd = r), this.checkEndStale(t, n), this.checkStuck(t, n);
1274
+ }, Ct);
1261
1275
  }
1262
- checkEndStale() {
1276
+ checkEndStale(e, t) {
1263
1277
  if (this.endedEmitted) return;
1264
- let e = U(), t = this.lastTickAt == null ? 0 : e - this.lastTickAt;
1265
- this.lastTickAt = e;
1266
1278
  let n = this.liveEnd();
1267
1279
  if (n == null) return;
1268
- let r = this.lastSeekableEnd == null || Math.abs(n - this.lastSeekableEnd) >= Et;
1280
+ let r = this.lastSeekableEnd == null || Math.abs(n - this.lastSeekableEnd) >= Dt;
1269
1281
  this.lastSeekableEnd = n;
1270
1282
  let i = this.deps.video, a = typeof document < "u" && document.visibilityState === "hidden";
1271
- if (r || t > Dt || a || i.paused) {
1283
+ if (r || t > Ot || a || i.paused) {
1272
1284
  this.edgeStampedAt = e;
1273
1285
  return;
1274
1286
  }
1275
- e - (this.edgeStampedAt ?? e) > Tt && (this.endedEmitted = !0, this.deps.emit({
1287
+ e - (this.edgeStampedAt ?? e) > Et && (this.endedEmitted = !0, this.deps.emit({
1276
1288
  type: "ended",
1277
1289
  reason: "stalled"
1278
1290
  }));
@@ -1280,13 +1292,44 @@ var H = class {
1280
1292
  resetEndStale() {
1281
1293
  this.lastSeekableEnd = null, this.edgeStampedAt = null, this.lastTickAt = null;
1282
1294
  }
1295
+ checkStuck(e, t) {
1296
+ if (this.deps.stuckReload === !1 || this.endedEmitted) return;
1297
+ let n = this.deps.video, r = n.currentTime, i = this.lastPlayhead == null || !Number.isFinite(r) || Math.abs(r - this.lastPlayhead) >= At;
1298
+ this.lastPlayhead = Number.isFinite(r) ? r : null;
1299
+ let a = typeof document < "u" && document.visibilityState === "hidden", o = t > Ot || a || n.paused || n.seeking || n.readyState < HTMLMediaElement.HAVE_CURRENT_DATA || !L();
1300
+ if (i || o) {
1301
+ this.playheadStampedAt = e, i && !o ? (this.movingSince ??= e, this.stuckReloads > 0 && e - this.movingSince >= Mt && (this.stuckReloads = 0)) : this.movingSince = null;
1302
+ return;
1303
+ }
1304
+ this.movingSince = null;
1305
+ let s = e - (this.playheadStampedAt ?? e), c = this.bufferedAdvancedAt == null ? Infinity : e - this.bufferedAdvancedAt;
1306
+ s <= kt || c <= kt || (this.playheadStampedAt = e, !(this.stuckReloads >= jt) && this.reloadStuck());
1307
+ }
1308
+ resetStuck() {
1309
+ this.lastPlayhead = null, this.playheadStampedAt = null, this.movingSince = null;
1310
+ }
1311
+ reloadStuck() {
1312
+ let { video: e, descriptor: t } = this.deps;
1313
+ this.stuckReloads += 1, this.stuckReloadsTotal += 1;
1314
+ let n = !e.paused;
1315
+ console.warn("havik-player: native playback frozen, buffer not growing: reloading at live");
1316
+ try {
1317
+ e.src = It(t.manifestUrl, t.analytics?.sid), e.load();
1318
+ } catch (e) {
1319
+ console.warn("havik-player: reload failed", e);
1320
+ return;
1321
+ }
1322
+ n && Promise.resolve(e.play()).catch((e) => {
1323
+ console.warn("havik-player: play() after the reload was refused", e);
1324
+ });
1325
+ }
1283
1326
  unwatchBufferedEnd() {
1284
- this.edgeTimer != null && clearInterval(this.edgeTimer), this.edgeTimer = null, this.onEdgeReset &&= (this.deps.video.removeEventListener("seeking", this.onEdgeReset), this.deps.video.removeEventListener("emptied", this.onEdgeReset), null), this.resetEndStale(), this.lastBufferedEnd = null, this.bufferedAdvancedAt = null;
1327
+ this.edgeTimer != null && clearInterval(this.edgeTimer), this.edgeTimer = null, this.onEdgeReset &&= (this.deps.video.removeEventListener("seeking", this.onEdgeReset), this.deps.video.removeEventListener("emptied", this.onEdgeReset), null), this.resetEndStale(), this.resetStuck(), this.lastBufferedEnd = null, this.bufferedAdvancedAt = null;
1285
1328
  }
1286
1329
  liveEdgeEstimate() {
1287
1330
  let e = this.deps.video;
1288
1331
  if (e.duration !== Infinity) return null;
1289
- let t = this.liveEnd(), n = At(e, e.currentTime) ?? this.lastBufferedEnd;
1332
+ let t = this.liveEnd(), n = Ft(e, e.currentTime) ?? this.lastBufferedEnd;
1290
1333
  if (n != null) {
1291
1334
  let e = this.bufferedAdvancedAt, r = n + (e == null ? 0 : Math.max(0, (U() - e) / 1e3));
1292
1335
  (t == null || r > t) && (t = r);
@@ -1302,7 +1345,7 @@ var H = class {
1302
1345
  snapToLive() {
1303
1346
  let e = this.deps.video, t = this.liveEnd();
1304
1347
  if (t == null) return;
1305
- let n = e.seekable.start(e.seekable.length - 1), r = Math.min(e.currentTime, t), i = Math.max(n, t - yt, r);
1348
+ let n = e.seekable.start(e.seekable.length - 1), r = Math.min(e.currentTime, t), i = Math.max(n, t - bt, r);
1306
1349
  try {
1307
1350
  e.currentTime = i;
1308
1351
  } catch {}
@@ -1323,7 +1366,7 @@ var H = class {
1323
1366
  function U() {
1324
1367
  return typeof performance < "u" ? performance.now() : Date.now();
1325
1368
  }
1326
- function At(e, t) {
1369
+ function Ft(e, t) {
1327
1370
  let n = e.buffered;
1328
1371
  if (!n || !Number.isFinite(t)) return null;
1329
1372
  for (let e = 0; e < n.length; e += 1) if (n.start(e) <= t + .1 && n.end(e) > t) {
@@ -1332,7 +1375,7 @@ function At(e, t) {
1332
1375
  }
1333
1376
  return null;
1334
1377
  }
1335
- function jt(e, t) {
1378
+ function It(e, t) {
1336
1379
  return t ? `${e}${e.includes("?") ? "&" : "?"}CMCD=${encodeURIComponent(`sid="${t}"`)}` : e;
1337
1380
  }
1338
1381
  //#endregion
@@ -1349,13 +1392,13 @@ function W(e, t = Date.now()) {
1349
1392
  }
1350
1393
  //#endregion
1351
1394
  //#region src/core/drm/xhrBody.ts
1352
- var Mt = 4096;
1353
- function Nt(e) {
1395
+ var Lt = 4096;
1396
+ function Rt(e) {
1354
1397
  if (!e || typeof e != "object") return;
1355
1398
  let { response: t } = e;
1356
1399
  if (typeof t == "string") return t;
1357
1400
  let n;
1358
- if (Object.prototype.toString.call(t) === "[object ArrayBuffer]" ? n = new Uint8Array(t) : ArrayBuffer.isView(t) && (n = new Uint8Array(t.buffer, t.byteOffset, t.byteLength)), !(!n || n.byteLength === 0 || n.byteLength > Mt)) try {
1401
+ if (Object.prototype.toString.call(t) === "[object ArrayBuffer]" ? n = new Uint8Array(t) : ArrayBuffer.isView(t) && (n = new Uint8Array(t.buffer, t.byteOffset, t.byteLength)), !(!n || n.byteLength === 0 || n.byteLength > Lt)) try {
1359
1402
  return new TextDecoder().decode(n);
1360
1403
  } catch {
1361
1404
  return;
@@ -1363,7 +1406,7 @@ function Nt(e) {
1363
1406
  }
1364
1407
  //#endregion
1365
1408
  //#region src/core/engine/prefetch.ts
1366
- var Pt = 15e3, Ft = 5e3, It = 6, Lt = 4e3, Rt = 2;
1409
+ var zt = 15e3, Bt = 5e3, Vt = 6, Ht = 4e3, Ut = 2;
1367
1410
  function G(e) {
1368
1411
  try {
1369
1412
  let t = new URL(e);
@@ -1372,7 +1415,7 @@ function G(e) {
1372
1415
  return e;
1373
1416
  }
1374
1417
  }
1375
- var zt = class {
1418
+ var Wt = class {
1376
1419
  cmcd;
1377
1420
  entries = /* @__PURE__ */ new Map();
1378
1421
  keyed = /* @__PURE__ */ new Set();
@@ -1404,17 +1447,17 @@ var zt = class {
1404
1447
  let r = G(e);
1405
1448
  if (this.entries.has(r)) return;
1406
1449
  let i = this.perTrack.get(t) ?? 0;
1407
- i >= It || (this.perTrack.set(t, i + 1), this.start(r, this.withCmcd(e, t, n), !1, Pt), this.stats.started += 1);
1450
+ i >= Vt || (this.perTrack.set(t, i + 1), this.start(r, this.withCmcd(e, t, n), !1, zt), this.stats.started += 1);
1408
1451
  }
1409
1452
  prefetchHint(e, t) {
1410
1453
  let n = G(e);
1411
1454
  if (this.entries.has(n)) return;
1412
1455
  let r = this.hints.get(t) ?? [];
1413
- for (; r.length >= Rt;) {
1456
+ for (; r.length >= Ut;) {
1414
1457
  let e = r.shift();
1415
1458
  e !== void 0 && this.drop(e) && (this.stats.hintDropped += 1);
1416
1459
  }
1417
- r.push(n), this.hints.set(t, r), this.start(n, this.withCmcd(e, t, "media"), !0, Lt), this.stats.hintStarted += 1;
1460
+ r.push(n), this.hints.set(t, r), this.start(n, this.withCmcd(e, t, "media"), !0, Ht), this.stats.hintStarted += 1;
1418
1461
  }
1419
1462
  dropHints(e) {
1420
1463
  for (let t of this.hints.get(e) ?? []) this.drop(t) && (this.stats.hintDropped += 1);
@@ -1467,14 +1510,14 @@ var zt = class {
1467
1510
  }
1468
1511
  }
1469
1512
  };
1470
- function Bt(t, n) {
1513
+ function Gt(t, n) {
1471
1514
  let r = typeof n == "function" ? n : e.DefaultConfig?.loader;
1472
1515
  if (typeof r != "function") return;
1473
1516
  class i extends r {
1474
1517
  served = !1;
1475
1518
  cancelled = !1;
1476
1519
  entry;
1477
- stats = Vt();
1520
+ stats = Kt();
1478
1521
  load(e, n, r) {
1479
1522
  let i = e.rangeEnd ? void 0 : t.take(e.url);
1480
1523
  if (!i) {
@@ -1486,7 +1529,7 @@ function Bt(t, n) {
1486
1529
  i.hint ? t.stats.hintFailed += 1 : t.stats.failed += 1;
1487
1530
  }, s = () => {
1488
1531
  !this.cancelled && this.served && (i.abort.abort(), o(), this.served = !1, this.entry = void 0, super.load(e, n, r));
1489
- }, c = n?.loadPolicy, l = c?.maxTimeToFirstByteMs || Ft, u = setTimeout(s, l);
1532
+ }, c = n?.loadPolicy, l = c?.maxTimeToFirstByteMs || Bt, u = setTimeout(s, l);
1490
1533
  i.headers.then(() => {
1491
1534
  clearTimeout(u);
1492
1535
  let e = c?.maxLoadTimeMs;
@@ -1519,7 +1562,7 @@ function Bt(t, n) {
1519
1562
  }
1520
1563
  return i;
1521
1564
  }
1522
- function Vt() {
1565
+ function Kt() {
1523
1566
  return {
1524
1567
  aborted: !1,
1525
1568
  loaded: 0,
@@ -1543,10 +1586,10 @@ function Vt() {
1543
1586
  }
1544
1587
  };
1545
1588
  }
1546
- function Ht(e) {
1589
+ function qt(e) {
1547
1590
  return e === "audio" ? "audio" : "main";
1548
1591
  }
1549
- function Ut(e, t) {
1592
+ function Jt(e, t) {
1550
1593
  let n = [];
1551
1594
  t.initSegment?.url && !t.initSegment.data && n.push({
1552
1595
  url: t.initSegment.url,
@@ -1563,7 +1606,7 @@ function Ut(e, t) {
1563
1606
  });
1564
1607
  return n;
1565
1608
  }
1566
- function Wt(e) {
1609
+ function Yt(e) {
1567
1610
  let t = e, n = t?.preloadHint;
1568
1611
  if (!n || !t?.url || n.TYPE !== "PART" || n["BYTERANGE-START"] !== void 0 || n["BYTERANGE-LENGTH"] !== void 0) return null;
1569
1612
  let r = n.URI;
@@ -1581,15 +1624,15 @@ function K(e, t) {
1581
1624
  }
1582
1625
  //#endregion
1583
1626
  //#region src/core/engine/hls.ts
1584
- function Gt() {
1627
+ function Xt() {
1585
1628
  return e.isSupported();
1586
1629
  }
1587
- var Kt = 3, qt = 1e6, Jt = 20, Yt = 1e4, Xt = 3, Zt = 5, Qt = 3, $t = 1e4, en = 3e4, tn = 12e3, nn = 3e3, rn = .02, an = 6e3, on = .5, sn = 3, cn = .5, ln = 1.1, un = .25, dn = 5e3, fn = 4e3, pn = 2, mn = 5, hn = 2, gn = 2, _n = 4, vn = 4, yn = 2e3, bn = 3e4, xn = 1, Sn = 2, Cn = .5, wn = .6, Tn = 2, En = 1.5, Dn = 1e4, On = 18e4, kn = .5, An = 2;
1588
- function jn(e) {
1630
+ var Zt = 3, Qt = 1e6, $t = 20, en = 1e4, tn = 3, nn = 5, rn = 3, an = 1e4, on = 3e4, sn = 12e3, cn = 3e3, ln = .02, un = 6e3, dn = .5, fn = 3, pn = .5, mn = 1.1, hn = .25, gn = 5e3, _n = 4e3, vn = 2, yn = 5, bn = 2, xn = 2, Sn = 4, Cn = 4, wn = 2e3, Tn = 3e4, En = 1, Dn = 2, On = .5, kn = .6, An = 2, jn = 1.5, Mn = 1e4, Nn = 18e4, Pn = .5, Fn = 2;
1631
+ function In(e) {
1589
1632
  let t = e.config ?? {};
1590
1633
  return t.capLevelToPlayerSize === !0 || t.capLevelOnFPSDrop === !0;
1591
1634
  }
1592
- var q = 8, Mn = 12e4, Nn = 6e4;
1635
+ var q = 8, Ln = 12e4, Rn = 6e4;
1593
1636
  function J(e, t, n) {
1594
1637
  if (n <= 0 || t < 0 || t >= e.length) return t;
1595
1638
  let r = (t, n) => e[t].width === e[n].width && e[t].height === e[n].height, i = (t) => {
@@ -1602,11 +1645,11 @@ function J(e, t, n) {
1602
1645
  function Y() {
1603
1646
  return typeof self < "u" && self.devicePixelRatio > 0 ? self.devicePixelRatio : 1;
1604
1647
  }
1605
- var Pn = .2, Fn = 30, In = 2, Ln = 2, Rn = 2, zn = 9, Bn = .5, Vn = 64, Hn = 1e4, Un = 8, Wn = .6, Gn = .4, Kn = .4, qn = 2, Jn = 6e4, X = .5, Yn = 3.5, Xn = 300;
1606
- function Z(e) {
1648
+ var zn = .2, Bn = 30, Vn = 2, Hn = 2, Un = 2, Wn = 9, Gn = .5, Kn = 64, qn = 1e4, Jn = 8, Yn = .6, Xn = .4, Zn = .4, Qn = 2, $n = 6e4, X = .5, er = 3.5, tr = 300;
1649
+ function nr(e) {
1607
1650
  return (e?.part ?? e?.frag)?.stats;
1608
1651
  }
1609
- function Zn() {
1652
+ function rr() {
1610
1653
  if (typeof navigator > "u") return 0;
1611
1654
  try {
1612
1655
  let e = navigator.connection?.rtt;
@@ -1615,20 +1658,20 @@ function Zn() {
1615
1658
  return 0;
1616
1659
  }
1617
1660
  }
1618
- function Qn(e) {
1661
+ function ir(e) {
1619
1662
  if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
1620
1663
  delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
1621
1664
  return;
1622
1665
  }
1623
- e.liveSyncDuration ??= Rn;
1624
- let t = Math.max(12, e.liveSyncDuration + zn);
1625
- (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), $n(e, e.liveSyncDuration);
1666
+ e.liveSyncDuration ??= Un;
1667
+ let t = Math.max(12, e.liveSyncDuration + Wn);
1668
+ (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), ar(e, e.liveSyncDuration);
1626
1669
  }
1627
- function $n(e, t) {
1670
+ function ar(e, t) {
1628
1671
  let n = (e, t, n) => Math.min(n, Math.max(t, e));
1629
1672
  e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
1630
1673
  }
1631
- var er = class {
1674
+ var or = class {
1632
1675
  name = "hls";
1633
1676
  hls = null;
1634
1677
  deps;
@@ -1781,7 +1824,7 @@ var er = class {
1781
1824
  }
1782
1825
  this.loadSuspended = !0;
1783
1826
  }
1784
- }, dn));
1827
+ }, gn));
1785
1828
  }
1786
1829
  resumeLoading() {
1787
1830
  if (this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.resetChaseWindow(), this.resetFrameDropWindow(), this.loadSuspended && (this.loadSuspended = !1, !this.stopped && this.hls)) {
@@ -1813,7 +1856,7 @@ var er = class {
1813
1856
  let t = e.liveSyncPosition;
1814
1857
  if (typeof t != "number" || !Number.isFinite(t)) return;
1815
1858
  let { video: n } = this.deps;
1816
- n.currentTime <= t + Bn || (n.currentTime = t);
1859
+ n.currentTime <= t + Gn || (n.currentTime = t);
1817
1860
  }
1818
1861
  reanchorPartTimeline(e, t) {
1819
1862
  let { frag: n, part: r } = t;
@@ -1825,7 +1868,7 @@ var er = class {
1825
1868
  let o = n.sn, s = i.fragments.find((e) => e && e.sn === o) ?? (i.fragmentHint?.sn === o ? i.fragmentHint : void 0);
1826
1869
  if (!s) return;
1827
1870
  let c = a.startPTS - s.start;
1828
- if (!(!Number.isFinite(c) || Math.abs(c) < rn)) {
1871
+ if (!(!Number.isFinite(c) || Math.abs(c) < ln)) {
1829
1872
  for (let e of i.fragments) e && e.setStart(e.start + c);
1830
1873
  i.fragmentHint?.setStart(i.fragmentHint.start + c), this.deps.debug === !0 && console.info(`havik-player: LL-HLS timeline re-anchored by ${c >= 0 ? "+" : ""}${c.toFixed(3)}s at sn ${n.sn} (level ${n.level})`);
1831
1874
  }
@@ -1853,7 +1896,7 @@ var er = class {
1853
1896
  videoEncryptionScheme: N.scheme,
1854
1897
  audioEncryptionScheme: N.scheme
1855
1898
  },
1856
- ...M() ? { requestMediaKeySystemAccessFunc: Ke(t) } : {},
1899
+ ...A() ? { requestMediaKeySystemAccessFunc: Ke(t) } : {},
1857
1900
  licenseXhrSetup: (e, t) => {
1858
1901
  let o = performance.now(), s = () => {
1859
1902
  e.open("POST", this.licenseUrl ?? t, !0), e.setRequestHeader("x-api-key", r.apiKey), e.setRequestHeader("X-Match-Urn", n.matchUrn), e.setRequestHeader("X-Device-Id", i), a && e.setRequestHeader("X-User-Id", a), this.licenseRequested = !0, e.addEventListener("loadend", () => {
@@ -1865,7 +1908,7 @@ var er = class {
1865
1908
  return;
1866
1909
  }
1867
1910
  let l = this.hls, u, d = new Promise((e) => {
1868
- u = setTimeout(() => e(void 0), $t);
1911
+ u = setTimeout(() => e(void 0), an);
1869
1912
  });
1870
1913
  return Promise.race([c().catch(() => void 0), d]).finally(() => clearTimeout(u)).then((e) => {
1871
1914
  if (!this.hls || this.hls !== l) throw Error("havik-player: engine torn down during refresh");
@@ -1883,28 +1926,28 @@ var er = class {
1883
1926
  this.prefetch?.destroy(), this.prefetch = null;
1884
1927
  let p = !1, m = !1, h = u && this.deps.mediaDuringKey !== !1, g = this.deps.preloadHints !== !1;
1885
1928
  if ((h || g) && !f.fLoader) {
1886
- let e = new zt({
1929
+ let e = new Wt({
1887
1930
  sessionId: n.analytics?.sid,
1888
1931
  contentId: n.matchUrn
1889
- }), t = Bt(e, f.loader);
1932
+ }), t = Gt(e, f.loader);
1890
1933
  t && (this.prefetch = e, p = h, m = g, f.fLoader = t);
1891
1934
  }
1892
- Qn(f), this.catchUpRate = typeof f.maxLiveSyncPlaybackRate == "number" ? f.maxLiveSyncPlaybackRate : 1, this.catchUpEngaged = !1, this.catchUpRate > 1 && (f.maxLiveSyncPlaybackRate = 1), this.sizeCapManaged = f.capLevelToPlayerSize !== !0, this.fpsCapManaged = !(c && "capLevelOnFPSDrop" in c), this.loadStarted = f.autoStartLoad !== !1;
1935
+ ir(f), this.catchUpRate = typeof f.maxLiveSyncPlaybackRate == "number" ? f.maxLiveSyncPlaybackRate : 1, this.catchUpEngaged = !1, this.catchUpRate > 1 && (f.maxLiveSyncPlaybackRate = 1), this.sizeCapManaged = f.capLevelToPlayerSize !== !0, this.fpsCapManaged = !(c && "capLevelOnFPSDrop" in c), this.loadStarted = f.autoStartLoad !== !1;
1893
1936
  let _ = new e(f);
1894
1937
  if (this.hls = _, this.armHoldBack(_, f), _.on(e.Events.FRAG_PARSED, (e, t) => this.reanchorPartTimeline(_, t)), _.on(e.Events.MANIFEST_PARSED, () => {
1895
1938
  this.mediaRecoveries = 0, this.networkRecoveries = 0, u && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && this.dropLevelsWidevineCannotPlay(_), this.measureLadder(_), this.applySizeCap(), this.loadSuspended = !1, t.paused && this.schedulePauseSuspend(), l({ type: "ready" });
1896
1939
  }), _.on(e.Events.FRAG_BUFFERED, (e, t) => {
1897
1940
  t?.frag?.type === "main" && (this.kickKind !== "dead" && this.clearKick(), (this.mediaRecoveries > 0 || this.networkRecoveries > 0 || this.licenseRecoveries > 0 || this.starvationKicks > 0 || this.starvationRebuilds > 0) && !this.recoveryResetTimer && (this.recoveryResetTimer = setTimeout(() => {
1898
1941
  this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.starvationRebuilds = 0, this.recoveryResetTimer = null;
1899
- }, en)));
1942
+ }, on)));
1900
1943
  }), _.on(e.Events.FRAG_LOADING, (e, t) => {
1901
1944
  t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
1902
1945
  }), _.on(e.Events.FRAG_LOADED, (e, t) => {
1903
1946
  t?.frag?.type === "main" && (this.mainFragsSinceTick += 1, this.bwFragsLoaded += 1, this.maybeRememberBandwidth(!1));
1904
- let n = t?.frag, r = Z(t), i = r?.loaded;
1947
+ let n = t?.frag, r = nr(t), i = r?.loaded;
1905
1948
  typeof i == "number" && (this.bytesLoaded += i), !this.reportedFirstBytes && typeof i == "number" && i > 0 && (this.reportedFirstBytes = !0, l({ type: "firstBytes" }));
1906
1949
  let a = r?.loading;
1907
- a && a.end > 0 && a.start > 0 && (this.requestCount += 1, this.requestTimesMs.push(Math.max(0, Math.round(a.end - a.start))), this.requestTimesMs.length > Vn && this.requestTimesMs.shift()), this.sampleHoldBack(t), this.sampleHeadroom(t);
1950
+ a && a.end > 0 && a.start > 0 && (this.requestCount += 1, this.requestTimesMs.push(Math.max(0, Math.round(a.end - a.start))), this.requestTimesMs.length > Kn && this.requestTimesMs.shift()), this.sampleHoldBack(t), this.sampleHeadroom(t);
1908
1951
  let o = n?.url;
1909
1952
  if (o) {
1910
1953
  let e = ft(o);
@@ -1924,8 +1967,8 @@ var er = class {
1924
1967
  let i = e;
1925
1968
  if (!i?.live || !i.partList?.length || typeof i.edge != "number") return;
1926
1969
  let a = n.buffered, o = a.length ? a.end(a.length - 1) : 0;
1927
- if (i.edge - o > Kt * (i.partTarget || .5)) return;
1928
- let s = Wt(e);
1970
+ if (i.edge - o > Zt * (i.partTarget || .5)) return;
1971
+ let s = Yt(e);
1929
1972
  s && t.prefetchHint(s, r);
1930
1973
  };
1931
1974
  _.on(e.Events.LEVEL_LOADED, (e, t) => {
@@ -1937,15 +1980,15 @@ var er = class {
1937
1980
  if (this.prefetch && p) {
1938
1981
  let t = this.prefetch;
1939
1982
  _.on(e.Events.KEY_LOADING, (e, n) => {
1940
- let r = n?.frag, i = Ht(r?.type);
1941
- if (r && t.isActive(i)) for (let e of Ut(_, r)) t.prefetch(e.url, i, e.kind);
1942
- }), _.on(e.Events.KEY_LOADED, (e, n) => t.keyUsable(Ht(n?.frag?.type)));
1983
+ let r = n?.frag, i = qt(r?.type);
1984
+ if (r && t.isActive(i)) for (let e of Jt(_, r)) t.prefetch(e.url, i, e.kind);
1985
+ }), _.on(e.Events.KEY_LOADED, (e, n) => t.keyUsable(qt(n?.frag?.type)));
1943
1986
  }
1944
1987
  _.on(e.Events.KEY_LOADED, () => {
1945
1988
  u && !this.reportedDrmReady && (this.reportedDrmReady = !0, l({ type: "drmReady" }));
1946
1989
  }), this.endWatchdog && clearInterval(this.endWatchdog), this.endWatchdog = setInterval(() => {
1947
1990
  this.checkEndStale(), this.checkStarvation(), this.updateCatchUpBand(), this.checkFutileChase(), this.adaptHoldBack(), this.reviewSizeCapHeadroom(), this.releaseStallCap(), this.checkFrameDrops();
1948
- }, nn), _.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1991
+ }, cn), _.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1949
1992
  this.renditionSwitches += 1;
1950
1993
  let n = this.lastLevelIndex >= 0 ? _.levels[this.lastLevelIndex] : void 0, r = _.levels[t.level];
1951
1994
  n && r && r.bitrate !== n.bitrate && (r.bitrate > n.bitrate ? this.upshifts += 1 : this.downshifts += 1), this.lastLevelIndex = t.level, this.armFrameDropGrace(), l({
@@ -2006,8 +2049,8 @@ var er = class {
2006
2049
  let e = this.hls, t = this.sizeCapCover();
2007
2050
  if (!e || !t) return null;
2008
2051
  let { cover: n, deviceRatio: r } = t, i = this.deps.sizeCapHeadroom, a = i == null || Number.isNaN(i);
2009
- if (a && r >= Sn) return n;
2010
- if (a) return this.headroomUngated || this.headroomGranted ? J(e.levels, n, xn) : n;
2052
+ if (a && r >= Dn) return n;
2053
+ if (a) return this.headroomUngated || this.headroomGranted ? J(e.levels, n, En) : n;
2011
2054
  let o = i === Infinity ? e.levels.length : Math.max(0, Math.floor(i));
2012
2055
  return J(e.levels, n, o);
2013
2056
  }
@@ -2024,11 +2067,11 @@ var er = class {
2024
2067
  return;
2025
2068
  }
2026
2069
  this.capTimer && clearTimeout(this.capTimer);
2027
- let t = e > this.sizeCapLevel ? yn : bn;
2070
+ let t = e > this.sizeCapLevel ? wn : Tn;
2028
2071
  this.capTimer = setTimeout(() => {
2029
2072
  this.capTimer = null;
2030
2073
  let n = this.computeSizeCapTarget();
2031
- n === e || t === bn && n != null && n < e ? this.applySizeCap() : this.onMediaResize();
2074
+ n === e || t === Tn && n != null && n < e ? this.applySizeCap() : this.onMediaResize();
2032
2075
  }, t);
2033
2076
  }
2034
2077
  measureLadder(e) {
@@ -2055,7 +2098,7 @@ var er = class {
2055
2098
  }
2056
2099
  defaultHeadroomApplies() {
2057
2100
  let e = this.deps.sizeCapHeadroom;
2058
- return e != null && !Number.isNaN(e) || this.headroomUngated ? !1 : Y() < Sn;
2101
+ return e != null && !Number.isNaN(e) || this.headroomUngated ? !1 : Y() < Dn;
2059
2102
  }
2060
2103
  applySizeCapUnlessPending() {
2061
2104
  this.capTimer || this.applySizeCap();
@@ -2064,7 +2107,7 @@ var er = class {
2064
2107
  if (!this.sizeCapManaged || !this.defaultHeadroomApplies() || e.frag?.type !== "main" || typeof e.frag.level != "number" || typeof document < "u" && document.hidden) return;
2065
2108
  let t = e.part ? e.part.duration : this.detectedLowLatency ? 0 : e.frag.duration;
2066
2109
  if (typeof t != "number" || !(t > 0)) return;
2067
- let n = Z(e)?.loading;
2110
+ let n = nr(e)?.loading;
2068
2111
  if (!n || !(n.start > 0) || !(n.end >= n.start)) return;
2069
2112
  let r = this.hls?.levels?.[e.frag.level]?.bitrate;
2070
2113
  if (!(typeof r == "number" && r > 0)) return;
@@ -2077,7 +2120,7 @@ var er = class {
2077
2120
  }
2078
2121
  onPlaybackStall(e) {
2079
2122
  let t = Date.now();
2080
- this.deps.video.seeking || !this.playingSince || t - this.playingSince < Dn || (this.lastStallAt = t, this.stepStallCapDown(t, e), this.headroomGranted && (this.headroomGranted = !1, this.applySizeCapUnlessPending()));
2123
+ this.deps.video.seeking || !this.playingSince || t - this.playingSince < Mn || (this.lastStallAt = t, this.stepStallCapDown(t, e), this.headroomGranted && (this.headroomGranted = !1, this.applySizeCapUnlessPending()));
2081
2124
  }
2082
2125
  stallCapLevel(e) {
2083
2126
  if (this.stallCapBitrate == null) return -1;
@@ -2088,7 +2131,7 @@ var er = class {
2088
2131
  }
2089
2132
  stepStallCapDown(e, t) {
2090
2133
  let n = this.hls;
2091
- if (!n || !this.stallCapEnabled || jn(n) || !n.autoLevelEnabled || typeof t == "number" && t >= kn || this.isLiveEdgeWait(n, t ?? 0)) return;
2134
+ if (!n || !this.stallCapEnabled || In(n) || !n.autoLevelEnabled || typeof t == "number" && t >= Pn || this.isLiveEdgeWait(n, t ?? 0)) return;
2092
2135
  this.stallCapBitrate != null && (this.stallCapSince = e);
2093
2136
  let r = Math.max(n.levels[n.currentLevel]?.bitrate ?? 0, n.levels[n.loadLevel]?.bitrate ?? 0);
2094
2137
  if (!(r > 0)) return;
@@ -2099,7 +2142,7 @@ var er = class {
2099
2142
  let n = e.latestLevelDetails;
2100
2143
  if (!n?.live) return !1;
2101
2144
  let r = n.partTarget || n.targetduration;
2102
- return !(r > 0) || !(n.edge > 0) ? !1 : n.edge - (this.deps.video.currentTime + t) < An * r;
2145
+ return !(r > 0) || !(n.edge > 0) ? !1 : n.edge - (this.deps.video.currentTime + t) < Fn * r;
2103
2146
  }
2104
2147
  releaseStallCap() {
2105
2148
  let e = this.hls, t = Date.now(), n = this.stallCapTickAt;
@@ -2108,7 +2151,7 @@ var er = class {
2108
2151
  n && (this.stallCapSince += t - Math.max(n, this.stallCapSince));
2109
2152
  return;
2110
2153
  }
2111
- if (t - this.stallCapSince < On) return;
2154
+ if (t - this.stallCapSince < Nn) return;
2112
2155
  let r = this.stallCapBitrate, i = e.levels.map((e) => e.bitrate).filter((e) => e > r).sort((e, t) => e - t)[0];
2113
2156
  this.stallCapBitrate = i ?? null, this.stallCapSince = t, this.pushCap();
2114
2157
  }
@@ -2121,26 +2164,26 @@ var er = class {
2121
2164
  this.headroomGranted = !1, this.headroomSamples = [];
2122
2165
  return;
2123
2166
  }
2124
- this.headroomSamples = this.headroomSamples.filter((t) => t.t >= e - Hn);
2167
+ this.headroomSamples = this.headroomSamples.filter((t) => t.t >= e - qn);
2125
2168
  let r = this.headroomEarned(n, e);
2126
- r !== this.headroomGranted && (r || (this.headroomRegrantAt = e + Nn), this.headroomGranted = r, this.applySizeCapUnlessPending());
2169
+ r !== this.headroomGranted && (r || (this.headroomRegrantAt = e + Rn), this.headroomGranted = r, this.applySizeCapUnlessPending());
2127
2170
  }
2128
2171
  headroomEarned(e, t) {
2129
- if (t - this.lastStallAt < Mn || !this.headroomGranted && t < this.headroomRegrantAt || !this.headroomGranted && this.headroomSamples.length < q) return !1;
2172
+ if (t - this.lastStallAt < Ln || !this.headroomGranted && t < this.headroomRegrantAt || !this.headroomGranted && this.headroomSamples.length < q) return !1;
2130
2173
  let n = e.levels, r = this.sizeCapCoverLevel();
2131
2174
  if (r == null) return this.headroomGranted;
2132
- let i = J(n, r, xn);
2175
+ let i = J(n, r, En);
2133
2176
  if (i === r) return this.headroomGranted;
2134
- let a = n[i]?.bitrate, o = e.bandwidthEstimate, s = this.headroomGranted ? En : Tn;
2177
+ let a = n[i]?.bitrate, o = e.bandwidthEstimate, s = this.headroomGranted ? jn : An;
2135
2178
  if (!(a > 0) || !(o >= s * a)) return !1;
2136
2179
  let c = (e) => K([...e].sort((e, t) => e - t), 90);
2137
2180
  if (this.headroomGranted) {
2138
2181
  let e = this.headroomSamples.filter((e) => e.bitrate >= a).map((e) => e.ratio);
2139
- return e.length < q || c(e) <= wn;
2182
+ return e.length < q || c(e) <= kn;
2140
2183
  }
2141
2184
  let l = [];
2142
2185
  for (let e of this.headroomSamples) e.bitrate < a && l.push(e.ratio * (a / e.bitrate));
2143
- return l.length >= q && c(l) <= Cn;
2186
+ return l.length >= q && c(l) <= On;
2144
2187
  }
2145
2188
  applySizeCap() {
2146
2189
  if (!this.sizeCapManaged) return;
@@ -2215,7 +2258,7 @@ var er = class {
2215
2258
  let t = e.currentLevel >= 0 ? e.levels[e.currentLevel] : void 0;
2216
2259
  t && (n.levelHeight = t.height);
2217
2260
  let r = e.autoLevelCapping, i = r >= 0 ? e.levels[r] : void 0;
2218
- i && i.height > 0 && (n.maxLevelHeight = i.height, !jn(e) && r === this.pushedCapLevel && this.pushedCapReason && (n.capReason = this.pushedCapReason)), t?.videoCodec && (n.videoCodec = t.videoCodec), t?.audioCodec && (n.audioCodec = t.audioCodec), t && typeof t.bitrate == "number" && (n.renditionBitrateKbps = Math.round(t.bitrate / 1e3)), typeof t?.details?.live == "boolean" && (n.isLive = t.details.live, typeof e.liveSyncPosition == "number" && (n.atLiveEdge = this.deps.video.currentTime >= e.liveSyncPosition - 1.5));
2261
+ i && i.height > 0 && (n.maxLevelHeight = i.height, !In(e) && r === this.pushedCapLevel && this.pushedCapReason && (n.capReason = this.pushedCapReason)), t?.videoCodec && (n.videoCodec = t.videoCodec), t?.audioCodec && (n.audioCodec = t.audioCodec), t && typeof t.bitrate == "number" && (n.renditionBitrateKbps = Math.round(t.bitrate / 1e3)), typeof t?.details?.live == "boolean" && (n.isLive = t.details.live, typeof e.liveSyncPosition == "number" && (n.atLiveEdge = this.deps.video.currentTime >= e.liveSyncPosition - 1.5));
2219
2262
  }
2220
2263
  return n;
2221
2264
  }
@@ -2243,7 +2286,7 @@ var er = class {
2243
2286
  return;
2244
2287
  }
2245
2288
  let e = Date.now() - this.lastAdvanceMs;
2246
- !this.chaseSuspended && e > an && this.suspendChasing(), e > tn && this.emitEnded("stalled");
2289
+ !this.chaseSuspended && e > un && this.suspendChasing(), e > sn && this.emitEnded("stalled");
2247
2290
  }
2248
2291
  resetStarvationWindow() {
2249
2292
  this.starvedTicks = 0, this.deadTicks = 0, this.mainFragsSinceTick = 0, this.recentMainFrags = [];
@@ -2258,7 +2301,7 @@ var er = class {
2258
2301
  this.resetStarvationWindow();
2259
2302
  return;
2260
2303
  }
2261
- let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < an, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > fn, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = R(t), c = o - s, l = c !== this.starvedFrames;
2304
+ let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < un, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > _n, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = R(t), c = o - s, l = c !== this.starvedFrames;
2262
2305
  if (this.starvedFrames = c, !a || l) {
2263
2306
  (t.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA || l) && this.clearKick(), this.resetStarvationWindow();
2264
2307
  return;
@@ -2267,12 +2310,12 @@ var er = class {
2267
2310
  this.resetStarvationWindow();
2268
2311
  return;
2269
2312
  }
2270
- if (this.kickPending && (this.kickKind === "dead" || i) && (this.kickTicks += 1), this.starvedTicks = i ? this.starvedTicks + 1 : 0, this.deadTicks += 1, this.recentMainFrags.push(this.mainFragsSinceTick), this.mainFragsSinceTick = 0, this.recentMainFrags.length > _n && this.recentMainFrags.shift(), this.clearRecoveryReset(), this.kickPending && this.kickTicks >= hn && (this.clearKick(), this.starvationRebuilds < gn)) {
2313
+ if (this.kickPending && (this.kickKind === "dead" || i) && (this.kickTicks += 1), this.starvedTicks = i ? this.starvedTicks + 1 : 0, this.deadTicks += 1, this.recentMainFrags.push(this.mainFragsSinceTick), this.mainFragsSinceTick = 0, this.recentMainFrags.length > Sn && this.recentMainFrags.shift(), this.clearRecoveryReset(), this.kickPending && this.kickTicks >= bn && (this.clearKick(), this.starvationRebuilds < xn)) {
2271
2314
  this.starvationRebuilds += 1, this.resetStarvationWindow(), this.clearRecoveryReset(), console.warn("havik-player: restarting the load did not resume video (the wedge is in hls.js state that only a rebuild clears) — rebuilding the player"), this.rebuildInstance("starvation rebuild");
2272
2315
  return;
2273
2316
  }
2274
- let u = i && this.starvedTicks >= pn, d = !i && this.deadTicks >= _n && this.recentMainFrags.reduce((e, t) => e + t, 0) >= vn;
2275
- if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >= mn))) {
2317
+ let u = i && this.starvedTicks >= vn, d = !i && this.deadTicks >= Sn && this.recentMainFrags.reduce((e, t) => e + t, 0) >= Cn;
2318
+ if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >= yn))) {
2276
2319
  this.starvationKicks += 1, this.kickPending = !0, this.kickTicks = 0, this.kickKind = d ? "dead" : "quiet", this.clearRecoveryReset(), console.warn(d ? "havik-player: video starved while hls.js keeps loading media it never plays (dead pipeline at the live edge) — restarting the load" : "havik-player: video starved while the live playlist advances (wedged hls.js fragment pipeline) — restarting the load");
2277
2320
  try {
2278
2321
  e.stopLoad(), e.startLoad(-1);
@@ -2287,7 +2330,7 @@ var er = class {
2287
2330
  let t = e.latency, n = e.targetLatency;
2288
2331
  if (typeof t != "number" || typeof n != "number" || !Number.isFinite(t)) return;
2289
2332
  let r = t - n;
2290
- !this.catchUpEngaged && r > on ? this.catchUpEngaged = !0 : this.catchUpEngaged && r <= on && this.deps.video.playbackRate === 1 && (this.catchUpEngaged = !1);
2333
+ !this.catchUpEngaged && r > dn ? this.catchUpEngaged = !0 : this.catchUpEngaged && r <= dn && this.deps.video.playbackRate === 1 && (this.catchUpEngaged = !1);
2291
2334
  let i = this.catchUpEngaged ? this.catchUpRate : 1;
2292
2335
  e.config.maxLiveSyncPlaybackRate !== i && (e.config.maxLiveSyncPlaybackRate = i);
2293
2336
  }
@@ -2300,7 +2343,7 @@ var er = class {
2300
2343
  }), n < 0 ? 0 : n;
2301
2344
  }
2302
2345
  armFrameDropGrace() {
2303
- this.fpsGraceTicks > 0 || (this.fpsGraceTicks = Ln, this.fpsBadTicks = 0);
2346
+ this.fpsGraceTicks > 0 || (this.fpsGraceTicks = Hn, this.fpsBadTicks = 0);
2304
2347
  }
2305
2348
  resetFrameDropWindow() {
2306
2349
  this.fpsLastFrames = -1, this.fpsLastDropped = -1, this.fpsBadTicks = 0;
@@ -2312,7 +2355,7 @@ var er = class {
2312
2355
  return;
2313
2356
  }
2314
2357
  let { totalFrames: n, dropped: r } = R(t), i = this.fpsLastFrames < 0, a = n - this.fpsLastFrames, o = r - this.fpsLastDropped;
2315
- if (this.fpsLastFrames = n, this.fpsLastDropped = r, i || a < Fn || o < 0) {
2358
+ if (this.fpsLastFrames = n, this.fpsLastDropped = r, i || a < Bn || o < 0) {
2316
2359
  this.fpsBadTicks = 0;
2317
2360
  return;
2318
2361
  }
@@ -2320,11 +2363,11 @@ var er = class {
2320
2363
  --this.fpsGraceTicks, this.fpsBadTicks = 0;
2321
2364
  return;
2322
2365
  }
2323
- if (o <= Pn * a) {
2366
+ if (o <= zn * a) {
2324
2367
  this.fpsBadTicks = 0;
2325
2368
  return;
2326
2369
  }
2327
- if (this.fpsBadTicks += 1, this.fpsBadTicks < In) return;
2370
+ if (this.fpsBadTicks += 1, this.fpsBadTicks < Vn) return;
2328
2371
  this.fpsBadTicks = 0;
2329
2372
  let s = e.levels, c = s[e.currentLevel];
2330
2373
  if (!c) return;
@@ -2341,22 +2384,22 @@ var er = class {
2341
2384
  return;
2342
2385
  }
2343
2386
  let n = e.latency, r = t.playbackRate, i = e.config.maxLiveSyncPlaybackRate;
2344
- if (!(!t.paused && typeof i == "number" && i > 1 && r >= Math.min(ln, i) && r <= i && typeof n == "number" && Number.isFinite(n))) {
2387
+ if (!(!t.paused && typeof i == "number" && i > 1 && r >= Math.min(mn, i) && r <= i && typeof n == "number" && Number.isFinite(n))) {
2345
2388
  this.resetChaseWindow();
2346
2389
  return;
2347
2390
  }
2348
- if (this.chaseLatency < 0 || this.chaseLatency - n >= cn) {
2391
+ if (this.chaseLatency < 0 || this.chaseLatency - n >= pn) {
2349
2392
  this.chaseLatency = n, this.chaseTicks = 0;
2350
2393
  return;
2351
2394
  }
2352
- if (this.chaseTicks += 1, this.chaseTicks < sn) return;
2395
+ if (this.chaseTicks += 1, this.chaseTicks < fn) return;
2353
2396
  this.resetChaseWindow();
2354
2397
  let a = typeof e.config.liveMaxLatencyDuration == "number" ? e.config.liveMaxLatencyDuration : e.maxLatency;
2355
2398
  if (!(a > 1)) return;
2356
2399
  let o = e.targetLatency;
2357
2400
  if (typeof o != "number" || !(o > 0)) return;
2358
2401
  let s = Math.min(n, a - 1);
2359
- s < o + un || (console.warn(`havik-player: catch-up at ${r}x not gaining on the live edge (latency ${n.toFixed(1)}s); holding at ${s.toFixed(1)}s`), this.liveRetargets += 1, e.targetLatency = s, this.deps.emit({
2402
+ s < o + hn || (console.warn(`havik-player: catch-up at ${r}x not gaining on the live edge (latency ${n.toFixed(1)}s); holding at ${s.toFixed(1)}s`), this.liveRetargets += 1, e.targetLatency = s, this.deps.emit({
2360
2403
  type: "livetargetchange",
2361
2404
  change: {
2362
2405
  targetSeconds: s,
@@ -2373,13 +2416,13 @@ var er = class {
2373
2416
  if (!this.holdBackEnabled || this.holdBackPriorChecked || !e || (this.holdBackPriorChecked = !0, this.holdBackApplied > 0 || this.holdBackStepCount > 0)) return;
2374
2417
  let t = e.targetLatency;
2375
2418
  if (this.holdBackBase < 0 && typeof t == "number" && t > 0 && (this.holdBackBase = t), this.holdBackBase < 0) return;
2376
- let n = Zn();
2377
- if (!(n >= Xn)) return;
2419
+ let n = rr();
2420
+ if (!(n >= tr)) return;
2378
2421
  let r = typeof t == "number" && t > 0 ? t : this.holdBackBase, i = Math.min(this.holdBackCeiling(e), this.holdBackBase + X);
2379
2422
  i > r && (this.holdBackStepCount = 1, this.moveHoldBack(e, r, i, "connection-rtt", `connection rtt ${n} ms`));
2380
2423
  }
2381
2424
  holdBackCeiling(e) {
2382
- let t = this.deps.adaptiveHoldBackCeiling, n = typeof t == "number" && Number.isFinite(t) ? t : Yn, r = typeof e.config.liveMaxLatencyDuration == "number" ? e.config.liveMaxLatencyDuration : e.maxLatency;
2425
+ let t = this.deps.adaptiveHoldBackCeiling, n = typeof t == "number" && Number.isFinite(t) ? t : er, r = typeof e.config.liveMaxLatencyDuration == "number" ? e.config.liveMaxLatencyDuration : e.maxLatency;
2383
2426
  return r > 1 ? Math.min(n, r - 1) : n;
2384
2427
  }
2385
2428
  sampleHoldBack(e) {
@@ -2388,7 +2431,7 @@ var er = class {
2388
2431
  if (t !== "main" && t !== "audio") return;
2389
2432
  let n = e.part ? e.part.duration : this.detectedLowLatency ? 0 : e.frag?.duration;
2390
2433
  if (typeof n != "number" || !(n > 0)) return;
2391
- let r = Z(e)?.loading;
2434
+ let r = nr(e)?.loading;
2392
2435
  if (!r || !(r.start > 0) || !(r.end > 0)) return;
2393
2436
  let i = r.first > r.start ? r.first : r.end, a = Math.max(0, i - r.start) / (n * 1e3);
2394
2437
  this.holdBackSamples.push({
@@ -2403,20 +2446,20 @@ var er = class {
2403
2446
  this.holdBackSlowTicks = 0, this.holdBackFastSince = 0, this.holdBackSamples = [];
2404
2447
  return;
2405
2448
  }
2406
- let t = Date.now(), n = t - Hn, r = this.holdBackSamples, i = 0;
2449
+ let t = Date.now(), n = t - qn, r = this.holdBackSamples, i = 0;
2407
2450
  for (; i < r.length && r[i].t < n;) i += 1;
2408
2451
  i > 0 && r.splice(0, i);
2409
2452
  let a = e.targetLatency;
2410
2453
  if (typeof a != "number" || !(a > 0)) return;
2411
2454
  this.holdBackBase < 0 && (this.holdBackBase = a);
2412
2455
  let o = this.holdBackBase, s = this.holdBackCeiling(e);
2413
- if (r.length < Un) {
2456
+ if (r.length < Jn) {
2414
2457
  this.holdBackSlowTicks = 0;
2415
2458
  return;
2416
2459
  }
2417
2460
  let c = r.map((e) => e.ratio).sort((e, t) => e - t), l = K(c, 95), u = K(c, 50), d = Math.round(l * 1e3);
2418
- if (l >= Wn && u >= Gn) {
2419
- if (this.holdBackFastSince = 0, this.holdBackSlowTicks += 1, this.holdBackSlowTicks < qn) return;
2461
+ if (l >= Yn && u >= Xn) {
2462
+ if (this.holdBackFastSince = 0, this.holdBackSlowTicks += 1, this.holdBackSlowTicks < Qn) return;
2420
2463
  this.holdBackSlowTicks = 0;
2421
2464
  let t = Math.ceil((s - o) / X), n = Math.ceil((a - o) / X - 1e-9);
2422
2465
  if (n > this.holdBackStepCount && (this.holdBackStepCount = n), this.holdBackStepCount >= t) return;
@@ -2426,7 +2469,7 @@ var er = class {
2426
2469
  this.moveHoldBack(e, a, r, "slow-fetches", `part fetches slow (first byte p50 ${Math.round(u * 1e3)} / p95 ${d} ms per 1 s of media)`);
2427
2470
  return;
2428
2471
  }
2429
- if (this.holdBackSlowTicks = 0, l >= Kn) {
2472
+ if (this.holdBackSlowTicks = 0, l >= Zn) {
2430
2473
  this.holdBackFastSince = 0;
2431
2474
  return;
2432
2475
  }
@@ -2434,7 +2477,7 @@ var er = class {
2434
2477
  this.holdBackFastSince = t;
2435
2478
  return;
2436
2479
  }
2437
- if (t - this.holdBackFastSince < Jn || (this.holdBackFastSince = t, this.holdBackStepCount === 0 || a !== this.holdBackApplied)) return;
2480
+ if (t - this.holdBackFastSince < $n || (this.holdBackFastSince = t, this.holdBackStepCount === 0 || a !== this.holdBackApplied)) return;
2438
2481
  --this.holdBackStepCount;
2439
2482
  let f = Math.max(o, o + this.holdBackStepCount * X);
2440
2483
  f >= a || this.moveHoldBack(e, a, f, "fast-fetches", `part fetches fast again (p95 first byte ${d} ms per 1 s of media)`);
@@ -2477,14 +2520,14 @@ var er = class {
2477
2520
  } catch {}
2478
2521
  }
2479
2522
  startEstimate() {
2480
- return this.deps.rememberBandwidth === !1 ? qt : Re() ?? qt;
2523
+ return this.deps.rememberBandwidth === !1 ? Qt : Be() ?? Qt;
2481
2524
  }
2482
2525
  maybeRememberBandwidth(e) {
2483
- if (this.deps.rememberBandwidth === !1 || this.bwFragsLoaded < Jt) return;
2526
+ if (this.deps.rememberBandwidth === !1 || this.bwFragsLoaded < $t) return;
2484
2527
  let t = Date.now();
2485
- if (!e && t - this.bwSavedAt < Yt) return;
2528
+ if (!e && t - this.bwSavedAt < en) return;
2486
2529
  let n = this.hls?.bandwidthEstimate;
2487
- typeof n != "number" || !Number.isFinite(n) || n <= 0 || (this.bwSavedAt = t, ze(n));
2530
+ typeof n != "number" || !Number.isFinite(n) || n <= 0 || (this.bwSavedAt = t, Ve(n));
2488
2531
  }
2489
2532
  destroy() {
2490
2533
  if (this.maybeRememberBandwidth(!0), this.detachFrames?.(), this.detachFrames = null, this.prefetch?.destroy(), this.prefetch = null, this.clearRecoveryReset(), this.capTimer &&= (clearTimeout(this.capTimer), null), this.capObserver &&= (this.capObserver.disconnect(), null), this.unwatchDprChanges(), this.endWatchdog &&= (clearInterval(this.endWatchdog), null), this.onVisibility && typeof document < "u" && (document.removeEventListener("visibilitychange", this.onVisibility), this.onVisibility = null), this.onOffline && typeof window < "u" && (window.removeEventListener("offline", this.onOffline), this.onOffline = null), this.onOnline && typeof window < "u" && (window.removeEventListener("online", this.onOnline), this.onOnline = null), this.onSeeking &&= (this.deps.video.removeEventListener("seeking", this.onSeeking), null), this.onTimeUpdate &&= (this.deps.video.removeEventListener("timeupdate", this.onTimeUpdate), null), this.onPause &&= (this.deps.video.removeEventListener("pause", this.onPause), null), this.onPlay &&= (this.deps.video.removeEventListener("play", this.onPlay), null), this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.networkRetryTimer &&= (clearTimeout(this.networkRetryTimer), null), this.licenseRetryTimer &&= (clearTimeout(this.licenseRetryTimer), null), this.hls) {
@@ -2502,7 +2545,7 @@ var er = class {
2502
2545
  scheduleLicenseRetry(t) {
2503
2546
  if (t.details !== e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED && t.details !== e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED) return !1;
2504
2547
  let n = t.response?.code ?? 0;
2505
- if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Qt) return !1;
2548
+ if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= rn) return !1;
2506
2549
  this.licenseRecoveries += 1;
2507
2550
  let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
2508
2551
  return this.licenseRetryTimer = setTimeout(() => {
@@ -2538,7 +2581,7 @@ var er = class {
2538
2581
  this.offlineHold = !0, this.networkRetryTimer &&= (clearTimeout(this.networkRetryTimer), null);
2539
2582
  return;
2540
2583
  }
2541
- if (this.networkRecoveries < Zt) {
2584
+ if (this.networkRecoveries < nn) {
2542
2585
  this.networkRecoveries += 1;
2543
2586
  let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
2544
2587
  this.networkRetryTimer = setTimeout(() => {
@@ -2555,7 +2598,7 @@ var er = class {
2555
2598
  return;
2556
2599
  }
2557
2600
  if (t.type === e.ErrorTypes.MEDIA_ERROR) {
2558
- if (this.mediaRecoveries < Xt) {
2601
+ if (this.mediaRecoveries < tn) {
2559
2602
  this.mediaRecoveries += 1, n.recoverMediaError();
2560
2603
  return;
2561
2604
  }
@@ -2576,18 +2619,21 @@ var er = class {
2576
2619
  }
2577
2620
  }
2578
2621
  toError(t, r = "PLAYBACK") {
2579
- let o = t.response?.code ?? 0, s = t.details ?? t.type, c = t.reason ?? t.error?.message ?? "", l = r === "DRM" && o === 403 && t.details === e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED ? i(Nt(t.networkDetails)) : void 0;
2580
- return new n(r === "DRM" ? a(o, l) : r === "NETWORK" && o === 0 ? "NETWORK" : o === 401 ? "UNAUTHORIZED" : o === 403 ? "FORBIDDEN" : "INTERNAL", o, `${r} error: ${s}${c ? ` (${c})` : ""}`, l ? { serverCode: l } : void 0);
2622
+ let o = t.response?.code ?? 0, s = t.details ?? t.type, c = t.reason ?? t.error?.message ?? "", l = r === "DRM" && o === 403 && t.details === e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED ? i(Rt(t.networkDetails)) : void 0;
2623
+ return new n(r === "DRM" ? a(o, l) : r === "NETWORK" && o === 0 ? "NETWORK" : o === 401 ? "UNAUTHORIZED" : o === 403 ? "FORBIDDEN" : "INTERNAL", o, `${r} error: ${s}${c ? ` (${c})` : ""}`, {
2624
+ detail: s,
2625
+ ...l ? { serverCode: l } : {}
2626
+ });
2581
2627
  }
2582
- }, tr = 15e3, nr = 6e5, rr = 3e5, ir = 100, Q = 1, ar = 1e3, or = 3e4, sr = 1e3, cr = {
2628
+ }, sr = 15e3, cr = 6e5, lr = 3e5, ur = 100, Z = 1, dr = 1e3, fr = 3e4, pr = 1e3, mr = {
2583
2629
  inline: "PRESENTATION_INLINE",
2584
2630
  fullscreen: "PRESENTATION_FULLSCREEN",
2585
2631
  pip: "PRESENTATION_PIP"
2586
- }, lr = {
2632
+ }, hr = {
2587
2633
  widevine: "KEY_SYSTEM_WIDEVINE",
2588
2634
  fairplay: "KEY_SYSTEM_FAIRPLAY",
2589
2635
  none: "KEY_SYSTEM_NONE"
2590
- }, ur = /* @__PURE__ */ new Set([
2636
+ }, gr = /* @__PURE__ */ new Set([
2591
2637
  "INVALID_URN",
2592
2638
  "NOT_FOUND",
2593
2639
  "GEO_DENIED",
@@ -2598,7 +2644,7 @@ var er = class {
2598
2644
  "FORBIDDEN",
2599
2645
  "RATE_LIMITED",
2600
2646
  "TIMEOUT"
2601
- ]), dr = {
2647
+ ]), _r = {
2602
2648
  idle: "PLAYER_STATE_IDLE",
2603
2649
  waiting: "PLAYER_STATE_WAITING",
2604
2650
  loading: "PLAYER_STATE_LOADING",
@@ -2608,7 +2654,7 @@ var er = class {
2608
2654
  ended: "PLAYER_STATE_ENDED",
2609
2655
  error: "PLAYER_STATE_ERROR"
2610
2656
  };
2611
- function fr(e) {
2657
+ function vr(e) {
2612
2658
  try {
2613
2659
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
2614
2660
  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);
@@ -2616,8 +2662,8 @@ function fr(e) {
2616
2662
  return;
2617
2663
  }
2618
2664
  }
2619
- var pr = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), mr = /* @__PURE__ */ new Set(), hr = /* @__PURE__ */ new Set(), gr = /* @__PURE__ */ new Set(), _r = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
2620
- function vr(e) {
2665
+ var yr = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), br = /* @__PURE__ */ new Set(), xr = /* @__PURE__ */ new Set(), Sr = /* @__PURE__ */ new Set(), Cr = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
2666
+ function wr(e) {
2621
2667
  if (e.disposition !== "enforce" || e.effectiveDirective !== "connect-src") return;
2622
2668
  let t;
2623
2669
  try {
@@ -2626,19 +2672,19 @@ function vr(e) {
2626
2672
  } catch {
2627
2673
  return;
2628
2674
  }
2629
- if (mr.has(t) && !hr.has(t)) {
2630
- hr.add(t), _(_r(new URL(t).origin));
2631
- for (let e of gr) e.noteCspBlocked();
2675
+ if (br.has(t) && !xr.has(t)) {
2676
+ xr.add(t), _(Cr(new URL(t).origin));
2677
+ for (let e of Sr) e.noteCspBlocked();
2632
2678
  }
2633
2679
  }
2634
- function yr(e, t) {
2680
+ function Tr(e, t) {
2635
2681
  return _(t, e), { disabledReason: t };
2636
2682
  }
2637
- function br(e, t, n) {
2638
- if (n) return t && !pr(t, n) && _(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
2683
+ function Er(e, t, n) {
2684
+ if (n) return t && !yr(t, n) && _(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
2639
2685
  if (t) return { endpoint: t };
2640
- if (t !== void 0) return yr("empty-endpoint", "analytics is disabled: no QoE beacons will be sent. analytics.endpoint was supplied but empty. Pass a real beacons origin, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, or omit the option to derive it from baseUrl (or analytics: false to opt out).");
2641
- let r = fr(e);
2686
+ if (t !== void 0) return Tr("empty-endpoint", "analytics is disabled: no QoE beacons will be sent. analytics.endpoint was supplied but empty. Pass a real beacons origin, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, or omit the option to derive it from baseUrl (or analytics: false to opt out).");
2687
+ let r = vr(e);
2642
2688
  if (r) return { endpoint: r };
2643
2689
  let i;
2644
2690
  try {
@@ -2646,9 +2692,9 @@ function br(e, t, n) {
2646
2692
  } catch {
2647
2693
  i = e;
2648
2694
  }
2649
- return yr(i, `analytics is disabled: no QoE beacons will be sent. The beacons endpoint cannot be derived from the baseUrl host "${i}" — the convention is feed[-dev].<domain> → beacons[-dev].<domain>. Pass an explicit endpoint, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, to createPlayer (or analytics: false to opt out deliberately).`);
2695
+ return Tr(i, `analytics is disabled: no QoE beacons will be sent. The beacons endpoint cannot be derived from the baseUrl host "${i}" — the convention is feed[-dev].<domain> → beacons[-dev].<domain>. Pass an explicit endpoint, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, to createPlayer (or analytics: false to opt out deliberately).`);
2650
2696
  }
2651
- var xr = class {
2697
+ var Dr = class {
2652
2698
  opts;
2653
2699
  url;
2654
2700
  cspKey;
@@ -2730,8 +2776,8 @@ var xr = class {
2730
2776
  constructor(e) {
2731
2777
  this.opts = {
2732
2778
  ...e,
2733
- heartbeatMs: e.heartbeatMs ?? tr,
2734
- idleMs: e.idleMs ?? nr
2779
+ heartbeatMs: e.heartbeatMs ?? sr,
2780
+ idleMs: e.idleMs ?? cr
2735
2781
  }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`;
2736
2782
  try {
2737
2783
  let e = new URL(this.url);
@@ -2748,11 +2794,11 @@ var xr = class {
2748
2794
  sdkVersion: this.opts.sdkVersion,
2749
2795
  ...this.opts.environment
2750
2796
  };
2751
- this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), ar), 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.cspKey && mr.add(this.cspKey), document.addEventListener("securitypolicyviolation", vr), gr.add(this), this.noteCspBlocked(), this.onVisibility());
2797
+ this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), dr), 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.cspKey && br.add(this.cspKey), document.addEventListener("securitypolicyviolation", wr), Sr.add(this), this.noteCspBlocked(), this.onVisibility());
2752
2798
  }
2753
2799
  checkSuspension(e) {
2754
2800
  if (this.endedFlag || !this.opts.idleMs) return !1;
2755
- let t = Math.max(this.opts.idleMs, rr);
2801
+ let t = Math.max(this.opts.idleMs, lr);
2756
2802
  return this.lastTickMs > 0 && e - this.lastTickMs >= t ? (this.endIdle(this.lastTickMs), !0) : (this.lastTickMs = e, !1);
2757
2803
  }
2758
2804
  syncIdle(e) {
@@ -2766,21 +2812,21 @@ var xr = class {
2766
2812
  noteStateChange(e, t, n, r) {
2767
2813
  if (this.endedFlag || e === t) return;
2768
2814
  let i = this.now();
2769
- if (t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(Q, i - this.startupAnchorMs)), this.firstAudioMs === 0 && !this.isMuted && (this.firstAudioMs = Math.max(Q, 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") {
2815
+ if (t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(Z, i - this.startupAnchorMs)), this.firstAudioMs === 0 && !this.isMuted && (this.firstAudioMs = Math.max(Z, 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") {
2770
2816
  this.stallIsSeekInduced = this.seekInduced(i);
2771
2817
  let e = !this.isHidden && !this.stallIsSeekInduced;
2772
- e && (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, e && this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(Q, Math.round(this.playingTotalMs)));
2818
+ e && (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, e && this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(Z, Math.round(this.playingTotalMs)));
2773
2819
  } else e === "buffering" && (this.isBuffering = !1, this.stallIsSeekInduced = !1);
2774
2820
  this.syncBuffering(), t === "paused" && (this.pauseCount += 1), this.syncIdle(t);
2775
2821
  let a = {
2776
- fromState: dr[e] ?? "PLAYER_STATE_UNSPECIFIED",
2777
- toState: dr[t] ?? "PLAYER_STATE_UNSPECIFIED",
2778
- presentation: cr[this.presentation]
2822
+ fromState: _r[e] ?? "PLAYER_STATE_UNSPECIFIED",
2823
+ toState: _r[t] ?? "PLAYER_STATE_UNSPECIFIED",
2824
+ presentation: mr[this.presentation]
2779
2825
  }, o = this.causeOf(e, t, i);
2780
2826
  o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
2781
2827
  }
2782
2828
  seekInduced(e) {
2783
- return this.lastSeekMs >= 0 && e - this.lastSeekMs < sr;
2829
+ return this.lastSeekMs >= 0 && e - this.lastSeekMs < pr;
2784
2830
  }
2785
2831
  causeOf(e, t, n) {
2786
2832
  if (t === "error") return "TRANSITION_CAUSE_ERROR";
@@ -2817,25 +2863,25 @@ var xr = class {
2817
2863
  }
2818
2864
  this.muteChanges += 1;
2819
2865
  let r = this.now();
2820
- n ? this.isPlaying && this.mutedSinceMs === null && (this.mutedSinceMs = r) : (this.mutedSinceMs !== null && (this.mutedTotalMs += r - this.mutedSinceMs, this.mutedSinceMs = null), this.firstAudioMs === 0 && this.isPlaying && (this.firstAudioMs = Math.max(Q, r - this.startupAnchorMs))), this.isMuted = n, this.syncActive();
2866
+ n ? this.isPlaying && this.mutedSinceMs === null && (this.mutedSinceMs = r) : (this.mutedSinceMs !== null && (this.mutedTotalMs += r - this.mutedSinceMs, this.mutedSinceMs = null), this.firstAudioMs === 0 && this.isPlaying && (this.firstAudioMs = Math.max(Z, r - this.startupAnchorMs))), this.isMuted = n, this.syncActive();
2821
2867
  }
2822
2868
  notePresentation(e) {
2823
2869
  this.endedFlag || e === this.presentation || (this.closePresentation(), this.presentation = e);
2824
2870
  }
2825
2871
  notePlayIntent() {
2826
- this.endedFlag || this.startupTimeMs > 0 || (this.startupAnchorMs = this.now(), this.manifestLoadMs > 0 && (this.manifestLoadMs = Q), this.firstPlaylistMs > 0 && (this.firstPlaylistMs = Q), this.firstFragLoadMs > 0 && (this.firstFragLoadMs = Q), this.drmSetupMs > 0 && (this.drmSetupMs = Q));
2872
+ this.endedFlag || this.startupTimeMs > 0 || (this.startupAnchorMs = this.now(), this.manifestLoadMs > 0 && (this.manifestLoadMs = Z), this.firstPlaylistMs > 0 && (this.firstPlaylistMs = Z), this.firstFragLoadMs > 0 && (this.firstFragLoadMs = Z), this.drmSetupMs > 0 && (this.drmSetupMs = Z));
2827
2873
  }
2828
2874
  noteReady() {
2829
- this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(Q, this.now() - this.startupAnchorMs));
2875
+ this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(Z, this.now() - this.startupAnchorMs));
2830
2876
  }
2831
2877
  noteFirstPlaylist() {
2832
- this.firstPlaylistMs === 0 && (this.firstPlaylistMs = Math.max(Q, this.now() - this.startupAnchorMs));
2878
+ this.firstPlaylistMs === 0 && (this.firstPlaylistMs = Math.max(Z, this.now() - this.startupAnchorMs));
2833
2879
  }
2834
2880
  noteDrmReady() {
2835
- this.drmSetupMs === 0 && (this.drmSetupMs = Math.max(Q, this.now() - this.startupAnchorMs));
2881
+ this.drmSetupMs === 0 && (this.drmSetupMs = Math.max(Z, this.now() - this.startupAnchorMs));
2836
2882
  }
2837
2883
  noteFirstBytes() {
2838
- this.sawFirstBytes = !0, this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(Q, this.now() - this.startupAnchorMs));
2884
+ this.sawFirstBytes = !0, this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(Z, this.now() - this.startupAnchorMs));
2839
2885
  }
2840
2886
  noteSeek() {
2841
2887
  this.endedFlag || (this.seekCount += 1, this.lastSeekMs = this.now());
@@ -2854,7 +2900,7 @@ var xr = class {
2854
2900
  let r = n ?? "";
2855
2901
  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";
2856
2902
  if (e === "DRM_CLIENT" || e === "DRM_DEVICE_REVOKED") return "ERROR_STAGE_DRM";
2857
- if (ur.has(e)) return "ERROR_STAGE_RESOLVE";
2903
+ if (gr.has(e)) return "ERROR_STAGE_RESOLVE";
2858
2904
  let i = t ?? "";
2859
2905
  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";
2860
2906
  }
@@ -2886,7 +2932,7 @@ var xr = class {
2886
2932
  let l = t.droppedFrames ?? 0;
2887
2933
  l > this.lastDropped && (o.droppedFramesDelta = l - this.lastDropped), this.lastDropped = l;
2888
2934
  let u = t.renditionSwitches ?? 0;
2889
- 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 = dr[t.state] ?? "PLAYER_STATE_UNSPECIFIED";
2935
+ 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 = _r[t.state] ?? "PLAYER_STATE_UNSPECIFIED";
2890
2936
  let d = this.mutedTotalMs;
2891
2937
  this.mutedSinceMs !== null && (d += e - this.mutedSinceMs), d > this.lastHbMutedMs && (o.mutedMsDelta = Math.round(d - this.lastHbMutedMs)), this.lastHbMutedMs = d;
2892
2938
  let f = t.upshifts ?? 0;
@@ -2908,13 +2954,13 @@ var xr = class {
2908
2954
  maybeEmitMediaInfo(e) {
2909
2955
  if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
2910
2956
  let t = {};
2911
- e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = lr[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);
2957
+ e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = hr[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);
2912
2958
  let n = JSON.stringify(t);
2913
2959
  n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
2914
2960
  }
2915
2961
  end(e, t) {
2916
2962
  if (this.endedFlag) return;
2917
- this.endedFlag = !0, gr.delete(this);
2963
+ this.endedFlag = !0, Sr.delete(this);
2918
2964
  let n = t ?? this.now();
2919
2965
  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));
2920
2966
  let r = this.safeSample();
@@ -2928,7 +2974,7 @@ var xr = class {
2928
2974
  let c = r?.positionMs ?? 0;
2929
2975
  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));
2930
2976
  let l = e !== "END_REASON_ENDED";
2931
- l && this.lastStallMs > 0 && n - this.lastStallMs <= or && (i.abandonAfterStallMs = Math.max(Q, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= or && (i.abandonAfterErrorMs = Math.max(Q, 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);
2977
+ l && this.lastStallMs > 0 && n - this.lastStallMs <= fr && (i.abandonAfterStallMs = Math.max(Z, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= fr && (i.abandonAfterErrorMs = Math.max(Z, 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);
2932
2978
  let u = r?.decodedFrames ?? this.lastDecoded;
2933
2979
  u > 0 && (i.decodedFramesTotal = u), e === "END_REASON_STOPPED" && this.startupTimeMs === 0 && this.lastErrorStage === null && (e = "END_REASON_ABORTED", i.reason = e), this.startupTimeMs === 0 && e !== "END_REASON_ABORTED" && (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");
2934
2980
  }
@@ -2948,13 +2994,13 @@ var xr = class {
2948
2994
  };
2949
2995
  }
2950
2996
  noteCspBlocked() {
2951
- this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(_r(new URL(this.url).origin)));
2997
+ this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(Cr(new URL(this.url).origin)));
2952
2998
  }
2953
2999
  cspBlocked() {
2954
- return !!this.cspKey && hr.has(this.cspKey);
3000
+ return !!this.cspKey && xr.has(this.cspKey);
2955
3001
  }
2956
3002
  enqueue(e) {
2957
- if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > ir;) {
3003
+ if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > ur;) {
2958
3004
  let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
2959
3005
  this.pending.splice(e >= 0 ? e : 0, 1);
2960
3006
  }
@@ -2981,7 +3027,7 @@ var xr = class {
2981
3027
  };
2982
3028
  //#endregion
2983
3029
  //#region src/core/environment.ts
2984
- function Sr() {
3030
+ function Or() {
2985
3031
  if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
2986
3032
  let e;
2987
3033
  try {
@@ -3006,14 +3052,14 @@ function Sr() {
3006
3052
  integration: "INTEGRATION_KIND_IFRAME"
3007
3053
  };
3008
3054
  }
3009
- function Cr() {
3055
+ function kr() {
3010
3056
  if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
3011
3057
  let e = navigator.userAgentData, t = e?.platform ?? "";
3012
3058
  if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
3013
3059
  let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
3014
3060
  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";
3015
3061
  }
3016
- function wr() {
3062
+ function Ar() {
3017
3063
  let e = typeof navigator < "u" ? navigator.connection : void 0;
3018
3064
  if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
3019
3065
  let t = {
@@ -3032,17 +3078,17 @@ function wr() {
3032
3078
  }
3033
3079
  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;
3034
3080
  }
3035
- function Tr(e) {
3036
- let t = Sr(), n = {
3037
- deviceClass: Cr(),
3081
+ function jr(e) {
3082
+ let t = Or(), n = {
3083
+ deviceClass: kr(),
3038
3084
  integration: t.integration,
3039
- ...wr()
3085
+ ...Ar()
3040
3086
  };
3041
3087
  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);
3042
3088
  let r = e.getBoundingClientRect?.();
3043
3089
  return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
3044
3090
  }
3045
- async function Er() {
3091
+ async function Mr() {
3046
3092
  let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
3047
3093
  if (!e?.getHighEntropyValues) return {};
3048
3094
  try {
@@ -3054,7 +3100,7 @@ async function Er() {
3054
3100
  }
3055
3101
  //#endregion
3056
3102
  //#region src/core/version.ts
3057
- var Dr = "2.27.1", Or = {
3103
+ var Nr = "2.28.0", Pr = {
3058
3104
  accent: "#E1B600",
3059
3105
  accentText: "#1B1C23",
3060
3106
  background: "#0e0f13",
@@ -3065,7 +3111,7 @@ var Dr = "2.27.1", Or = {
3065
3111
  border: "rgba(255, 255, 255, 0.1)",
3066
3112
  radius: "8px",
3067
3113
  fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
3068
- }, kr = {
3114
+ }, Fr = {
3069
3115
  accent: "--havik-accent",
3070
3116
  accentText: "--havik-accent-text",
3071
3117
  background: "--havik-bg",
@@ -3077,26 +3123,26 @@ var Dr = "2.27.1", Or = {
3077
3123
  radius: "--havik-radius",
3078
3124
  fontFamily: "--havik-font"
3079
3125
  };
3080
- function Ar(e, t) {
3126
+ function Ir(e, t) {
3081
3127
  let n = {
3082
- ...Or,
3128
+ ...Pr,
3083
3129
  ...t
3084
3130
  };
3085
- for (let t of Object.keys(kr)) e.style.setProperty(kr[t], n[t]);
3131
+ for (let t of Object.keys(Fr)) e.style.setProperty(Fr[t], n[t]);
3086
3132
  n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
3087
3133
  }
3088
3134
  //#endregion
3089
3135
  //#region src/ui/styles.ts
3090
- var jr = "havik-player-styles", Mr = "\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 touch-action:manipulation}\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/* Absolutely centred, NOT a flex sibling of .havik-center: the hidden center\n button (.havik-gone) still occupies its 74px in the row, which pushed an\n in-flow spinner 37px right of centre. */\n.havik-spinner{position:absolute;inset:0;margin:auto;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", Nr = /* @__PURE__ */ new WeakMap();
3091
- function Pr(e = document, t) {
3092
- let n = e.querySelector(`style#${jr}`);
3093
- if (n && (!t || Nr.get(n) === t)) return;
3136
+ var Lr = "havik-player-styles", Rr = "\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 touch-action:manipulation}\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/* Absolutely centred, NOT a flex sibling of .havik-center: the hidden center\n button (.havik-gone) still occupies its 74px in the row, which pushed an\n in-flow spinner 37px right of centre. */\n.havik-spinner{position:absolute;inset:0;margin:auto;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", zr = /* @__PURE__ */ new WeakMap();
3137
+ function Br(e = document, t) {
3138
+ let n = e.querySelector(`style#${Lr}`);
3139
+ if (n && (!t || zr.get(n) === t)) return;
3094
3140
  let r = e.createElement("style");
3095
- r.id = jr, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = Mr, Nr.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
3141
+ r.id = Lr, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = Rr, zr.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
3096
3142
  }
3097
3143
  //#endregion
3098
3144
  //#region src/ui/icons.ts
3099
- var Fr = {
3145
+ var Vr = {
3100
3146
  play: "<path d=\"M8 5v14l11-7z\"/>",
3101
3147
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
3102
3148
  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\"/>",
@@ -3109,12 +3155,12 @@ var Fr = {
3109
3155
  liveDot: "<circle cx=\"12\" cy=\"12\" r=\"6\"/>",
3110
3156
  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\"/>"
3111
3157
  };
3112
- function $(e) {
3113
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${Fr[e]}</svg>`;
3158
+ function Q(e) {
3159
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${Vr[e]}</svg>`;
3114
3160
  }
3115
3161
  //#endregion
3116
3162
  //#region src/ui/controls.ts
3117
- var Ir = 3e3, Lr = 250, Rr = /* @__PURE__ */ new Set([
3163
+ var Hr = 3e3, Ur = 250, Wr = /* @__PURE__ */ new Set([
3118
3164
  "GONE",
3119
3165
  "NOT_FOUND",
3120
3166
  "GEO_DENIED",
@@ -3122,15 +3168,19 @@ var Ir = 3e3, Lr = 250, Rr = /* @__PURE__ */ new Set([
3122
3168
  "UNAUTHORIZED",
3123
3169
  "DRM_DEVICE_REVOKED"
3124
3170
  ]);
3125
- function zr(e) {
3171
+ function Gr(e) {
3126
3172
  let t = "This match isn't available in your country";
3127
3173
  return e ? `${t} (${e})` : t;
3128
3174
  }
3129
- var Br = "This device can't play protected video";
3130
- function Vr(e) {
3131
- return new Hr(e);
3175
+ var Kr = "This device can't play protected video";
3176
+ function qr(e) {
3177
+ return `This browser can't play protected video. Allow protected content in its site settings, or use ${e ? "Chrome, Edge or Safari" : "Chrome or Edge"} (Firefox asks when you retry)`;
3178
+ }
3179
+ var Jr = "Protected video can't be shown on this display. Use the built-in screen, or an HDCP-capable monitor and cable";
3180
+ function Yr(e) {
3181
+ return new Xr(e);
3132
3182
  }
3133
- var Hr = class {
3183
+ var Xr = class {
3134
3184
  root;
3135
3185
  video;
3136
3186
  player;
@@ -3179,7 +3229,7 @@ var Hr = class {
3179
3229
  scrubbing = !1;
3180
3230
  offFns = [];
3181
3231
  constructor(e) {
3182
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Ir, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.streamEnded = e.streamEnded ?? (() => !1), this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, this.offlineMessage = e.offlineMessage ?? "No internet connection — waiting to reconnect", Pr(this.doc, e.cspNonce), 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");
3232
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Hr, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.streamEnded = e.streamEnded ?? (() => !1), this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, this.offlineMessage = e.offlineMessage ?? "No internet connection — waiting to reconnect", Br(this.doc, e.cspNonce), 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");
3183
3233
  let t = (e, t, n) => {
3184
3234
  let r = this.doc.createElement(e);
3185
3235
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -3190,12 +3240,12 @@ var Hr = class {
3190
3240
  "aria-label": r,
3191
3241
  title: r
3192
3242
  });
3193
- return i.innerHTML = $(n), i;
3243
+ return i.innerHTML = Q(n), i;
3194
3244
  };
3195
3245
  this.overlay = t("div", "havik-overlay"), this.center = t("button", "havik-center", {
3196
3246
  type: "button",
3197
3247
  "aria-label": "Play"
3198
- }), this.center.innerHTML = $("play"), this.spinner = t("div", "havik-spinner"), this.spinner.style.display = "none", this.overlay.append(this.center, this.spinner), this.gradient = t("div", "havik-gradient"), this.bar = t("div", "havik-bar"), this.seek = t("div", "havik-seek", {
3248
+ }), this.center.innerHTML = Q("play"), this.spinner = t("div", "havik-spinner"), this.spinner.style.display = "none", this.overlay.append(this.center, this.spinner), this.gradient = t("div", "havik-gradient"), this.bar = t("div", "havik-bar"), this.seek = t("div", "havik-seek", {
3199
3249
  role: "slider",
3200
3250
  "aria-label": "Seek",
3201
3251
  "aria-valuemin": "0",
@@ -3213,7 +3263,7 @@ var Hr = class {
3213
3263
  "aria-label": "Volume"
3214
3264
  });
3215
3265
  let a = t("div", "havik-vol");
3216
- a.append(this.muteBtn, this.volSlider), this.live = t("div", "havik-live"), this.live.innerHTML = $("liveDot");
3266
+ a.append(this.muteBtn, this.volSlider), this.live = t("div", "havik-live"), this.live.innerHTML = Q("liveDot");
3217
3267
  let o = this.doc.createElement("span");
3218
3268
  o.textContent = "LIVE", this.liveGo = t("button", "havik-live__go", { type: "button" }), this.liveGo.textContent = "Go live", this.live.append(o, this.liveGo), this.time = t("div", "havik-time"), this.time.textContent = "0:00", this.time.style.display = "none";
3219
3269
  let s = t("div", "havik-spacer");
@@ -3265,7 +3315,7 @@ var Hr = class {
3265
3315
  let t = this.surfaceWasHidden;
3266
3316
  this.tapTimer = setTimeout(() => {
3267
3317
  this.tapTimer = void 0, this.toggleBar(t);
3268
- }, Lr);
3318
+ }, Ur);
3269
3319
  }
3270
3320
  toggleBar(e) {
3271
3321
  e ? this.poke() : this.hideIfIdle();
@@ -3292,7 +3342,20 @@ var Hr = class {
3292
3342
  this.renderStatus(t);
3293
3343
  }
3294
3344
  statusMessageFor(e) {
3295
- return e === "ended" ? this.isGone() ? this.endedMessage : this.player.endedReason === "interrupted" ? this.interruptedMessage : this.endedMessage : e === "offline" ? this.offlineMessage : e === "waiting" ? this.hasPlayed ? "Reconnecting — please stand by" : "Starting soon…" : e === "error" ? this.errorMessage === void 0 ? this.lastError?.code === "GEO_DENIED" ? zr(this.lastError.country) : this.lastError?.code === "DRM_DEVICE_REVOKED" ? Br : this.lastError?.message ?? "Playback unavailable" : this.errorMessage : null;
3345
+ if (e === "ended") return this.isGone() ? this.endedMessage : this.player.endedReason === "interrupted" ? this.interruptedMessage : this.endedMessage;
3346
+ if (e === "offline") return this.offlineMessage;
3347
+ if (e === "waiting") return this.hasPlayed ? "Reconnecting — please stand by" : "Starting soon…";
3348
+ if (e === "error") {
3349
+ if (this.errorMessage !== void 0) return this.errorMessage;
3350
+ if (this.lastError?.code === "GEO_DENIED") return Gr(this.lastError.country);
3351
+ if (this.lastError?.code === "DRM_DEVICE_REVOKED") return Kr;
3352
+ if (this.lastError?.code === "DRM_CLIENT") {
3353
+ if (this.lastError.detail === "keySystemNoAccess") return qr(!!this.player.descriptor?.drm?.fairplay?.licenseUrl);
3354
+ if (this.lastError.detail === "keySystemStatusOutputRestricted") return Jr;
3355
+ }
3356
+ return this.lastError?.message ?? "Playback unavailable";
3357
+ }
3358
+ return null;
3296
3359
  }
3297
3360
  renderStatus(e) {
3298
3361
  let t = this.statusMessageFor(e);
@@ -3304,12 +3367,12 @@ var Hr = class {
3304
3367
  }
3305
3368
  if (e === "preplay") {
3306
3369
  let e = this.doc.createElement("button");
3307
- e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = $("play"), e.addEventListener("click", () => void this.player.play()), n.append(e);
3370
+ 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);
3308
3371
  } else {
3309
3372
  let r = this.doc.createElement("div");
3310
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !Rr.has(this.lastError.code)) {
3373
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !Wr.has(this.lastError.code)) {
3311
3374
  let e = this.doc.createElement("button");
3312
- e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${$("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), n.append(e);
3375
+ 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);
3313
3376
  }
3314
3377
  }
3315
3378
  this.root.append(n), this.statusBox = n, this.show();
@@ -3348,7 +3411,7 @@ var Hr = class {
3348
3411
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
3349
3412
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
3350
3413
  }
3351
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Ur(this.video.currentTime)} / ${Ur(this.video.duration)}`);
3414
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Zr(this.video.currentTime)} / ${Zr(this.video.duration)}`);
3352
3415
  }
3353
3416
  syncLive() {
3354
3417
  let e = this.player.getStats(), t = e.isLive !== !1, n = this.isGone(), r = this.player.state === "ended" || n, i = !t && !this.streamEnded() && !n;
@@ -3398,17 +3461,17 @@ var Hr = class {
3398
3461
  renderQualityMenu() {
3399
3462
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
3400
3463
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
3401
- for (let n of e) this.menu.append(this.menuItem(Wr(n), t === n.index, () => this.player.setQuality(n.index)));
3464
+ for (let n of e) this.menu.append(this.menuItem(Qr(n), t === n.index, () => this.player.setQuality(n.index)));
3402
3465
  }
3403
3466
  renderAudioMenu() {
3404
3467
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
3405
3468
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
3406
- for (let t of e) this.menu.append(this.menuItem(Gr(t), t.default, () => this.player.setAudioTrack(t.id)));
3469
+ for (let t of e) this.menu.append(this.menuItem($r(t), t.default, () => this.player.setAudioTrack(t.id)));
3407
3470
  }
3408
3471
  renderCcMenu() {
3409
3472
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
3410
3473
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
3411
- for (let t of e) this.menu.append(this.menuItem(Kr(t), !1, () => this.player.setTextTrack(t.id)));
3474
+ for (let t of e) this.menu.append(this.menuItem(ei(t), !1, () => this.player.setTextTrack(t.id)));
3412
3475
  }
3413
3476
  onTracksChanged() {
3414
3477
  this.openMenu && this.renderMenu(this.openMenu);
@@ -3428,14 +3491,14 @@ var Hr = class {
3428
3491
  }
3429
3492
  async toggleFullscreen() {
3430
3493
  try {
3431
- ve(this.video, this.root) ? await w(this.video, this.root) : await C(this.video, this.root);
3494
+ S(this.video, this.root) ? await T(this.video, this.root) : await w(this.video, this.root);
3432
3495
  } catch {}
3433
3496
  }
3434
3497
  syncFullscreenSupport() {
3435
- this.fsSupported = S(this.video, this.root), this.fsBtn.style.display = this.fsSupported ? "" : "none";
3498
+ this.fsSupported = C(this.video, this.root), this.fsBtn.style.display = this.fsSupported ? "" : "none";
3436
3499
  }
3437
3500
  syncFullscreen() {
3438
- let e = ve(this.video, this.root);
3501
+ let e = S(this.video, this.root);
3439
3502
  this.setIcon(this.fsBtn, e ? "fullscreenExit" : "fullscreen"), this.fsBtn.setAttribute("aria-label", e ? "Exit fullscreen" : "Fullscreen");
3440
3503
  }
3441
3504
  poke() {
@@ -3496,7 +3559,7 @@ var Hr = class {
3496
3559
  }
3497
3560
  }
3498
3561
  setIcon(e, t) {
3499
- e.innerHTML = $(t);
3562
+ e.innerHTML = Q(t);
3500
3563
  }
3501
3564
  destroy() {
3502
3565
  this.hideTimer && clearTimeout(this.hideTimer), this.tapTimer && clearTimeout(this.tapTimer);
@@ -3513,23 +3576,23 @@ var Hr = class {
3513
3576
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
3514
3577
  }
3515
3578
  };
3516
- function Ur(e) {
3579
+ function Zr(e) {
3517
3580
  (!Number.isFinite(e) || e < 0) && (e = 0);
3518
3581
  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");
3519
3582
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
3520
3583
  }
3521
- function Wr(e) {
3584
+ function Qr(e) {
3522
3585
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
3523
3586
  }
3524
- function Gr(e) {
3587
+ function $r(e) {
3525
3588
  return e.name || e.lang || `Track ${e.id}`;
3526
3589
  }
3527
- function Kr(e) {
3590
+ function ei(e) {
3528
3591
  return e.name || e.lang || `Track ${e.id}`;
3529
3592
  }
3530
3593
  //#endregion
3531
3594
  //#region src/managed/index.ts
3532
- var qr = 6e3, Jr = 45e3, Yr = 2e3, Xr = 3, Zr = 5e3, Qr = 2e3, $r = 48e4, ei = "keySystemLicenseRequestFailed", ti = /* @__PURE__ */ new Set([
3595
+ var ti = 6e3, ni = 45e3, ri = 2e3, ii = 3, ai = 6e4, oi = 2, si = 6e5, ci = 5e3, li = 2e3, ui = 48e4, di = "keySystemLicenseRequestFailed", fi = /* @__PURE__ */ new Set([
3533
3596
  "GONE",
3534
3597
  "NOT_FOUND",
3535
3598
  "INVALID_URN",
@@ -3537,27 +3600,27 @@ var qr = 6e3, Jr = 45e3, Yr = 2e3, Xr = 3, Zr = 5e3, Qr = 2e3, $r = 48e4, ei = "
3537
3600
  "GEO_DENIED",
3538
3601
  "DRM_DEVICE_REVOKED"
3539
3602
  ]);
3540
- function ni(e) {
3541
- return !ti.has(e);
3603
+ function pi(e) {
3604
+ return !fi.has(e);
3542
3605
  }
3543
- function ri(e) {
3606
+ function mi(e) {
3544
3607
  if (e.env !== void 0 && e.baseUrl !== void 0) throw new n("INTERNAL", 0, `createPlayer: pass env OR baseUrl, not both (got env=${JSON.stringify(e.env)} and baseUrl=${JSON.stringify(e.baseUrl)})`);
3545
3608
  if (e.env !== void 0) return lt(e.env);
3546
3609
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
3547
3610
  return e.baseUrl;
3548
3611
  }
3549
- async function ii(e) {
3612
+ async function hi(e) {
3550
3613
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
3551
- let t = ri(e);
3614
+ let t = mi(e);
3552
3615
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
3553
3616
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
3554
- let r = new ai({
3617
+ let r = new gi({
3555
3618
  ...e,
3556
3619
  baseUrl: t
3557
3620
  });
3558
3621
  return await r.start(), r;
3559
3622
  }
3560
- var ai = class {
3623
+ var gi = class {
3561
3624
  opts;
3562
3625
  abort = new AbortController();
3563
3626
  listeners = {};
@@ -3580,6 +3643,10 @@ var ai = class {
3580
3643
  destroyed = !1;
3581
3644
  loadGen = 0;
3582
3645
  liveStateSub = null;
3646
+ liveStateLast = null;
3647
+ liveStateAliveAt = 0;
3648
+ staleEndReloads = 0;
3649
+ staleEndWindowAt = 0;
3583
3650
  endedHandled = !1;
3584
3651
  _endedReason = null;
3585
3652
  rejoinTimer = null;
@@ -3636,7 +3703,7 @@ var ai = class {
3636
3703
  };
3637
3704
  }
3638
3705
  beginArming(e, t, n = !1) {
3639
- let r = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? D(this.opts.baseUrl);
3706
+ let r = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? O(this.opts.baseUrl);
3640
3707
  r ? this.armWithSse(e, r, t, n) : this.beginArmingByPolling(e);
3641
3708
  }
3642
3709
  beginArmingByPolling(e) {
@@ -3657,7 +3724,7 @@ var ai = class {
3657
3724
  setTimeout(() => {
3658
3725
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
3659
3726
  phase: i ? "unavailable" : "tooEarly",
3660
- retryInMs: Jr,
3727
+ retryInMs: ni,
3661
3728
  attempt: 1,
3662
3729
  elapsedMs: 0,
3663
3730
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -3696,13 +3763,13 @@ var ai = class {
3696
3763
  !a && !p() && h();
3697
3764
  }, e);
3698
3765
  };
3699
- _(qr), this.armingSub = E({
3766
+ _(ti), this.armingSub = D({
3700
3767
  eventsBaseUrl: t,
3701
3768
  matchUrn: this.opts.matchUrn,
3702
3769
  credential: this.opts.credential,
3703
- onAlive: () => _(Jr),
3770
+ onAlive: () => _(ni),
3704
3771
  onState: (e) => {
3705
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Yr);
3772
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * ri);
3706
3773
  },
3707
3774
  onError: () => h()
3708
3775
  });
@@ -3725,7 +3792,7 @@ var ai = class {
3725
3792
  let n = await c(this.opts.credential);
3726
3793
  if (this.destroyed || t !== this.loadGen) return;
3727
3794
  this.setState("loading");
3728
- let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = _t(this.opts.video, (e) => {
3795
+ let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = vt(this.opts.video, (e) => {
3729
3796
  this._state !== "ended" && this._state !== "error" && this.setState(e);
3730
3797
  });
3731
3798
  if (await i.load(), this.destroyed || t !== this.loadGen) {
@@ -3747,19 +3814,19 @@ var ai = class {
3747
3814
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
3748
3815
  let i = e.analytics?.sid;
3749
3816
  if (!i) return;
3750
- let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = br(this.opts.baseUrl, a, e.analytics?.endpoint);
3817
+ let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = Er(this.opts.baseUrl, a, e.analytics?.endpoint);
3751
3818
  if (!o) {
3752
3819
  s && setTimeout(() => {
3753
3820
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, s));
3754
3821
  }, 0);
3755
3822
  return;
3756
3823
  }
3757
- let c = this.opts.video, l = new xr({
3824
+ let c = this.opts.video, l = new Dr({
3758
3825
  endpoint: o,
3759
3826
  sid: i,
3760
3827
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
3761
- sdkVersion: Dr,
3762
- environment: Tr(c),
3828
+ sdkVersion: Nr,
3829
+ environment: jr(c),
3763
3830
  idleMs: typeof this.opts.analytics == "object" ? this.opts.analytics.idleMs : void 0,
3764
3831
  onIdle: () => this.onAnalyticsIdle(t),
3765
3832
  onBlocked: (e) => setTimeout(() => {
@@ -3780,7 +3847,7 @@ var ai = class {
3780
3847
  let u = () => l.noteSeek(), d = () => l.noteVolume(c.muted, c.volume);
3781
3848
  c.addEventListener("seeking", u), c.addEventListener("volumechange", d), this.detachMediaListeners = () => {
3782
3849
  c.removeEventListener("seeking", u), c.removeEventListener("volumechange", d);
3783
- }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), Er().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
3850
+ }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), Mr().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
3784
3851
  }
3785
3852
  currentPresentation() {
3786
3853
  let e = this.opts.video;
@@ -3795,7 +3862,7 @@ var ai = class {
3795
3862
  video: this.opts.video,
3796
3863
  descriptor: e,
3797
3864
  credential: t,
3798
- deviceId: A(),
3865
+ deviceId: Oe(),
3799
3866
  userId: this.opts.userId,
3800
3867
  lowLatency: this.opts.lowLatency === void 0 || this.opts.lowLatency === "auto" || this.opts.lowLatency,
3801
3868
  snapToLiveOnRefocus: this.opts.snapToLiveOnRefocus,
@@ -3804,6 +3871,7 @@ var ai = class {
3804
3871
  adaptiveHoldBack: this.opts.adaptiveHoldBack,
3805
3872
  adaptiveHoldBackCeiling: this.opts.adaptiveHoldBackCeiling,
3806
3873
  stallCap: this.opts.stallCap,
3874
+ stuckReload: this.opts.stuckReload,
3807
3875
  mediaDuringKey: this.opts.mediaDuringKey,
3808
3876
  preloadHints: this.opts.preloadHints,
3809
3877
  debug: this.opts.debug,
@@ -3813,9 +3881,9 @@ var ai = class {
3813
3881
  freshLicenseUrl: () => this.refreshLicense()
3814
3882
  }, o = this.opts.engine ?? "auto";
3815
3883
  if (o === "native") return new H(a);
3816
- if (o === "hls") return new er(a);
3817
- if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && B(this.opts.video) && V() || !e.drmEnabled && kt() && B(this.opts.video)) return new H(a);
3818
- if (Gt()) return new er(a);
3884
+ if (o === "hls") return new or(a);
3885
+ if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && B(this.opts.video) && V() || !e.drmEnabled && Pt() && B(this.opts.video)) return new H(a);
3886
+ if (Xt()) return new or(a);
3819
3887
  if (B(this.opts.video)) return new H(a);
3820
3888
  throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
3821
3889
  }
@@ -3849,6 +3917,7 @@ var ai = class {
3849
3917
  this.emit("texttrackchange", e.id);
3850
3918
  break;
3851
3919
  case "ended":
3920
+ if (e.reason === "stalled" && this.reloadStaleEnd()) break;
3852
3921
  this.onStreamEnded();
3853
3922
  break;
3854
3923
  case "error": {
@@ -3866,27 +3935,27 @@ var ai = class {
3866
3935
  }
3867
3936
  startLiveState(e) {
3868
3937
  if (this.opts.liveStateEvents === !1) return;
3869
- let t = this.opts.eventsBaseUrl ?? D(this.opts.baseUrl);
3938
+ let t = this.opts.eventsBaseUrl ?? O(this.opts.baseUrl);
3870
3939
  if (!t) return;
3871
- this.closeLiveState();
3940
+ this.closeLiveState(), this.liveStateLast = null, this.liveStateAliveAt = 0;
3872
3941
  let i = 0, a = !1;
3873
- this.liveStateSub = E({
3942
+ this.liveStateSub = D({
3874
3943
  eventsBaseUrl: t,
3875
3944
  matchUrn: this.opts.matchUrn,
3876
3945
  credential: this.opts.credential,
3877
3946
  onState: (t) => {
3878
- this.destroyed || e !== this.loadGen || (t.state === "gone" ? this.onStreamEnded(!0, t.reason ?? "match_ended") : t.state === "ended" ? this.onStreamEnded(!1, t.reason) : t.state === "live" && this.endedHandled && this.scheduleRejoin(e));
3947
+ this.destroyed || e !== this.loadGen || (this.liveStateLast = t.state, this.liveStateAliveAt = Date.now(), t.state === "gone" ? this.onStreamEnded(!0, t.reason ?? "match_ended") : t.state === "ended" ? this.onStreamEnded(!1, t.reason) : t.state === "live" && this.endedHandled && this.scheduleRejoin(e));
3879
3948
  },
3880
3949
  onAlive: () => {
3881
- i = 0, a = !1;
3950
+ i = 0, a = !1, !this.destroyed && e === this.loadGen && (this.liveStateAliveAt = Date.now());
3882
3951
  },
3883
3952
  onError: (o) => {
3884
3953
  if (!(this.destroyed || e !== this.loadGen || a) && !this.endedHandled) {
3885
- if (o instanceof T && (o.terminal || o.status === 404)) {
3954
+ if (o instanceof E && (o.terminal || o.status === 404)) {
3886
3955
  a = !0, this.emit("warning", new n(r(o.status), o.status, `live-state events answered HTTP ${o.status} for ${this.opts.matchUrn}: the match is ` + (o.terminal ? "gone, so the subscription stopped" : "not in the catalog (ended and dropped, or not listed yet), so it retries every 5 min") + " — end-of-stream detection falls back to the HLS staleness watchdog."));
3887
3956
  return;
3888
3957
  }
3889
- i += 1, i >= Xr && (a = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} after ${i} 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.`)));
3958
+ i += 1, i >= ii && (a = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} after ${i} 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.`)));
3890
3959
  }
3891
3960
  }
3892
3961
  });
@@ -3894,6 +3963,18 @@ var ai = class {
3894
3963
  closeLiveState() {
3895
3964
  this.liveStateSub?.close(), this.liveStateSub = null;
3896
3965
  }
3966
+ reloadStaleEnd() {
3967
+ if (this.destroyed || this.endedHandled || this._state === "error" || this._state === "ended") return !1;
3968
+ let e = Date.now();
3969
+ if (this.liveStateLast !== "live" || e - this.liveStateAliveAt > ai || (e - this.staleEndWindowAt > si && (this.staleEndReloads = 0, this.staleEndWindowAt = e), this.staleEndReloads >= oi)) return !1;
3970
+ this.staleEndReloads += 1;
3971
+ let t = new n("UNAVAILABLE", 0, `live playlist stopped advancing for 12 s while the match is live (live-state push heard ${Math.round((e - this.liveStateAliveAt) / 1e3)} s ago): reloading instead of ending (${this.staleEndReloads} of ${oi} this window)`);
3972
+ this.analyticsSession?.noteError(t.code, t.message, !1, 0, Math.round(this.opts.video.currentTime * 1e3), "staleEndReload");
3973
+ let r = this.loadGen;
3974
+ return this.emit("warning", t), Promise.resolve().then(() => {
3975
+ if (!(this.destroyed || r !== this.loadGen) && this._state !== "error" && this._state !== "ended") return this.reload().catch(() => void 0);
3976
+ }), !0;
3977
+ }
3897
3978
  watchConnectivity() {
3898
3979
  this.connectivity || (this.connectivity = ut(), this.connectivity.onChange((e) => {
3899
3980
  if (this.destroyed) return;
@@ -3903,7 +3984,7 @@ var ai = class {
3903
3984
  }
3904
3985
  if (this.emit("online"), this._state !== "error" || !this._lastError) return;
3905
3986
  let t = this._lastError.code;
3906
- ni(t) && (t === "NETWORK" || this.failedWhileOffline) && this.retry().catch(() => void 0);
3987
+ pi(t) && (t === "NETWORK" || this.failedWhileOffline) && this.retry().catch(() => void 0);
3907
3988
  }));
3908
3989
  }
3909
3990
  watchReturn() {
@@ -3913,7 +3994,7 @@ var ai = class {
3913
3994
  return;
3914
3995
  }
3915
3996
  let e = this.hiddenAtMs;
3916
- this.hiddenAtMs = null, e != null && Date.now() - e >= Zr && this.resyncLiveState();
3997
+ this.hiddenAtMs = null, e != null && Date.now() - e >= ci && this.resyncLiveState();
3917
3998
  }, this.onReturnPageShow = (e) => {
3918
3999
  e.persisted && this.resyncLiveState();
3919
4000
  }, document.addEventListener("visibilitychange", this.onReturnVisible), typeof window < "u" && window.addEventListener("pageshow", this.onReturnPageShow));
@@ -3945,7 +4026,7 @@ var ai = class {
3945
4026
  scheduleRejoin(e) {
3946
4027
  this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
3947
4028
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
3948
- }, Math.random() * Yr));
4029
+ }, Math.random() * ri));
3949
4030
  }
3950
4031
  cancelRejoin() {
3951
4032
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -3969,7 +4050,7 @@ var ai = class {
3969
4050
  }
3970
4051
  t.controls = !1;
3971
4052
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
3972
- r && r.insertBefore(n, t), n.appendChild(t), Ar(n, this.opts.theme), this.container = n, this.controlBar = Vr({
4053
+ r && r.insertBefore(n, t), n.appendChild(t), Ir(n, this.opts.theme), this.container = n, this.controlBar = Yr({
3973
4054
  root: n,
3974
4055
  video: t,
3975
4056
  player: this,
@@ -4008,7 +4089,7 @@ var ai = class {
4008
4089
  }
4009
4090
  startStats() {
4010
4091
  this.stopStats();
4011
- let e = Math.max(500, this.opts.statsIntervalMs ?? Qr);
4092
+ let e = Math.max(500, this.opts.statsIntervalMs ?? li);
4012
4093
  this.statsTimer = setInterval(() => {
4013
4094
  if (!this.engine) return;
4014
4095
  let e = this.engine.getStats();
@@ -4019,14 +4100,14 @@ var ai = class {
4019
4100
  this.statsTimer && clearInterval(this.statsTimer), this.statsTimer = void 0;
4020
4101
  }
4021
4102
  isStaleLicenseDenial(e) {
4022
- return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== ei || e.error.httpStatus !== 403 || e.error.code !== "FORBIDDEN" || e.error.serverCode === "license_refused" ? !1 : W(this._descriptor?.drm?.widevine?.licenseUrl);
4103
+ return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== di || e.error.httpStatus !== 403 || e.error.code !== "FORBIDDEN" || e.error.serverCode === "license_refused" ? !1 : W(this._descriptor?.drm?.widevine?.licenseUrl);
4023
4104
  }
4024
4105
  scheduleLicenseRefresh() {
4025
4106
  if (this.stopRefresh(), !this._descriptor?.drmEnabled) return;
4026
4107
  typeof document < "u" && this.engine?.name === "hls" && (this.onRefocus = () => {
4027
4108
  document.visibilityState !== "visible" || this.destroyed || W(this._descriptor?.drm?.widevine?.licenseUrl) && this.refreshLicense();
4028
4109
  }, document.addEventListener("visibilitychange", this.onRefocus));
4029
- let e = this.opts.licenseRefreshMs ?? $r;
4110
+ let e = this.opts.licenseRefreshMs ?? ui;
4030
4111
  e && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
4031
4112
  }
4032
4113
  stopRefresh() {
@@ -4122,13 +4203,13 @@ var ai = class {
4122
4203
  e.pictureInPictureElement === this.opts.video && typeof e.exitPictureInPicture == "function" && await e.exitPictureInPicture();
4123
4204
  }
4124
4205
  async enterFullscreen() {
4125
- return !this.destroyed && C(this.opts.video, this.container ?? void 0);
4206
+ return !this.destroyed && w(this.opts.video, this.container ?? void 0);
4126
4207
  }
4127
4208
  async exitFullscreen() {
4128
- await w(this.opts.video, this.container ?? void 0);
4209
+ await T(this.opts.video, this.container ?? void 0);
4129
4210
  }
4130
4211
  fullscreenSupported() {
4131
- return !this.destroyed && S(this.opts.video, this.container ?? void 0);
4212
+ return !this.destroyed && C(this.opts.video, this.container ?? void 0);
4132
4213
  }
4133
4214
  retry() {
4134
4215
  return this.reload();
@@ -4191,12 +4272,12 @@ var ai = class {
4191
4272
  };
4192
4273
  //#endregion
4193
4274
  //#region src/embed/protocol.ts
4194
- function oi(e) {
4275
+ function $(e) {
4195
4276
  return !!e && typeof e == "object" && typeof e.type == "string" && e.type.startsWith("oddin:");
4196
4277
  }
4197
4278
  //#endregion
4198
4279
  //#region src/embed/host.ts
4199
- function si(e) {
4280
+ function _i(e) {
4200
4281
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
4201
4282
  if (!e.src) throw Error("mountEmbed: opts.src is required");
4202
4283
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -4205,7 +4286,7 @@ function si(e) {
4205
4286
  let r = document.createElement("iframe");
4206
4287
  r.src = t.toString(), r.allow = "autoplay; encrypted-media; fullscreen", r.setAttribute("allowfullscreen", ""), r.referrerPolicy = "no-referrer", r.setAttribute("sandbox", e.sandbox ?? "allow-scripts allow-same-origin allow-presentation"), r.style.cssText = "border:0;width:100%;height:100%;display:block", e.container.appendChild(r);
4207
4288
  let i = /* @__PURE__ */ new Set(), a = (t) => {
4208
- if (t.source !== r.contentWindow || t.origin !== n || !oi(t.data)) return;
4289
+ if (t.source !== r.contentWindow || t.origin !== n || !$(t.data)) return;
4209
4290
  let a = t.data;
4210
4291
  a.type === "oddin:ready" && a.protocol !== 1 && console.warn(`havik-player: embed protocol v${a.protocol} != host v1. Pin the embed page version to the SDK version to avoid skew.`), e.onEvent?.(a), i.forEach((e) => e(a));
4211
4292
  };
@@ -4255,15 +4336,15 @@ function si(e) {
4255
4336
  }
4256
4337
  //#endregion
4257
4338
  //#region src/embed/page.ts
4258
- var ci = /^[A-Za-z0-9:_.-]{1,128}$/;
4259
- function li(e) {
4339
+ var vi = /^[A-Za-z0-9:_.-]{1,128}$/;
4340
+ function yi(e) {
4260
4341
  if (typeof e == "boolean") return e;
4261
4342
  if (typeof e == "object" && e) {
4262
4343
  let t = e.endpoint;
4263
4344
  return typeof t == "string" ? { endpoint: t } : {};
4264
4345
  }
4265
4346
  }
4266
- function ui() {
4347
+ function bi() {
4267
4348
  let e = window.HAVIK_EMBED_CONFIG;
4268
4349
  if (e) return {
4269
4350
  baseUrl: e.baseUrl ?? "",
@@ -4273,7 +4354,7 @@ function ui() {
4273
4354
  autoplay: e.autoplay ?? !0,
4274
4355
  muted: e.muted ?? !0,
4275
4356
  waitForLive: e.waitForLive ?? !0,
4276
- analytics: li(e.analytics)
4357
+ analytics: yi(e.analytics)
4277
4358
  };
4278
4359
  let t = new URLSearchParams(location.search);
4279
4360
  return {
@@ -4286,9 +4367,9 @@ function ui() {
4286
4367
  waitForLive: !0
4287
4368
  };
4288
4369
  }
4289
- async function di() {
4290
- let e = ui();
4291
- pi();
4370
+ async function xi() {
4371
+ let e = bi();
4372
+ Ci();
4292
4373
  let t = document.createElement("video");
4293
4374
  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);
4294
4375
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -4344,12 +4425,12 @@ async function di() {
4344
4425
  message: e.message,
4345
4426
  ...e.country === void 0 ? {} : { country: e.country }
4346
4427
  })), e.on("autoplayblocked", () => {
4347
- i({ type: "oddin:autoplayblocked" }), fi(t, e);
4428
+ i({ type: "oddin:autoplayblocked" }), Si(t, e);
4348
4429
  });
4349
4430
  }, s = async (r) => {
4350
4431
  a?.destroy(), a = null;
4351
4432
  try {
4352
- let n = await ii({
4433
+ let n = await hi({
4353
4434
  video: t,
4354
4435
  baseUrl: e.baseUrl,
4355
4436
  matchUrn: r,
@@ -4412,30 +4493,30 @@ async function di() {
4412
4493
  a?.retry();
4413
4494
  break;
4414
4495
  case "oddin:load":
4415
- typeof e.matchUrn == "string" && ci.test(e.matchUrn) && s(e.matchUrn);
4496
+ typeof e.matchUrn == "string" && vi.test(e.matchUrn) && s(e.matchUrn);
4416
4497
  break;
4417
4498
  case "oddin:destroy": c();
4418
4499
  }
4419
4500
  }, u = (e) => {
4420
- r && e.origin === r && oi(e.data) && l(e.data);
4501
+ r && e.origin === r && $(e.data) && l(e.data);
4421
4502
  };
4422
4503
  r && window.addEventListener("message", u), window.addEventListener("pagehide", c, { once: !0 }), await s(e.matchUrn), i({
4423
4504
  type: "oddin:ready",
4424
4505
  protocol: 1
4425
4506
  });
4426
4507
  }
4427
- function fi(e, t) {
4508
+ function Si(e, t) {
4428
4509
  if (document.getElementById("havik-tap")) return;
4429
4510
  let n = document.createElement("button");
4430
4511
  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", () => {
4431
4512
  n.remove(), t.play();
4432
4513
  }), document.body.appendChild(n);
4433
4514
  }
4434
- function pi() {
4515
+ function Ci() {
4435
4516
  let e = document.createElement("style");
4436
4517
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
4437
4518
  }
4438
4519
  //#endregion
4439
- export { T as LiveStateHttpError, Or as ODDIN_THEME, n as PlaybackError, Ar as applyTheme, lt as baseUrlForEnv, di as bootEmbed, Oe as clearDeviceId, Be as clearRememberedBandwidth, ii as createPlayer, D as deriveEventsBaseUrl, at as detectDrmSupport, C as enterVideoFullscreen, w as exitVideoFullscreen, ye as fetchCatalog, xe as fetchTournament, Ce as flattenMatches, S as fullscreenSupported, A as getDeviceId, st as isLowLatencyManifest, oi as isOddinMessage, Ae as licenseRequestHeaders, si as mountEmbed, fe as pollToLive, y as resolveStream, E as subscribeLiveState, ve as videoFullscreenActive, Te as watchStatus };
4520
+ export { E as LiveStateHttpError, Pr as ODDIN_THEME, n as PlaybackError, Ir as applyTheme, lt as baseUrlForEnv, xi as bootEmbed, ke as clearDeviceId, He as clearRememberedBandwidth, hi as createPlayer, O as deriveEventsBaseUrl, at as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ve as fetchCatalog, be as fetchTournament, Se as flattenMatches, C as fullscreenSupported, Oe as getDeviceId, st as isLowLatencyManifest, $ as isOddinMessage, je as licenseRequestHeaders, _i as mountEmbed, fe as pollToLive, y as resolveStream, D as subscribeLiveState, S as videoFullscreenActive, we as watchStatus };
4440
4521
 
4441
4522
  //# sourceMappingURL=index.js.map