@oddin-gg/havik-player 2.16.13 → 2.18.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
@@ -290,32 +290,32 @@ function he(e, t) {
290
290
  function ge(e) {
291
291
  return e.ownerDocument ?? document;
292
292
  }
293
- function x(e) {
293
+ function _e(e) {
294
294
  return e.fullscreenElement ?? e.webkitFullscreenElement ?? null;
295
295
  }
296
- function S(e, t) {
297
- let n = x(ge(e));
296
+ function x(e, t) {
297
+ let n = _e(ge(e));
298
298
  return n != null && (n === e || t != null && n === t) || e.webkitDisplayingFullscreen === !0;
299
299
  }
300
- async function C(e, t) {
300
+ async function S(e, t) {
301
301
  let n = t ?? e;
302
302
  if (typeof n.requestFullscreen == "function") return await n.requestFullscreen(), !0;
303
303
  if (typeof n.webkitRequestFullscreen == "function") return n.webkitRequestFullscreen(), !0;
304
304
  let r = e;
305
305
  return r.webkitSupportsFullscreen && typeof r.webkitEnterFullscreen == "function" ? (r.webkitEnterFullscreen(), !0) : !1;
306
306
  }
307
- async function w(e, t) {
307
+ async function C(e, t) {
308
308
  let n = e;
309
309
  if (n.webkitDisplayingFullscreen) {
310
310
  n.webkitExitFullscreen?.();
311
311
  return;
312
312
  }
313
- let r = ge(e), i = x(r);
313
+ let r = ge(e), i = _e(r);
314
314
  i != null && (i === e || t != null && i === t) && (typeof r.exitFullscreen == "function" ? await r.exitFullscreen() : r.webkitExitFullscreen?.());
315
315
  }
316
316
  //#endregion
317
317
  //#region src/core/catalog.ts
318
- async function _e(e) {
318
+ async function ve(e) {
319
319
  let t = new URLSearchParams();
320
320
  e.status && t.set("status", Array.isArray(e.status) ? e.status.join(",") : e.status), e.sport && t.set("sport", e.sport);
321
321
  let i = t.toString(), a = d(e.baseUrl, "/v1/catalog" + (i ? `?${i}` : "")), o = new Headers();
@@ -333,12 +333,12 @@ async function _e(e) {
333
333
  };
334
334
  if (!s.ok) throw new n(r(s.status), s.status, `catalog request failed (HTTP ${s.status})`);
335
335
  return {
336
- tournaments: ve(await s.json()),
336
+ tournaments: ye(await s.json()),
337
337
  etag: s.headers.get("etag") ?? void 0,
338
338
  notModified: !1
339
339
  };
340
340
  }
341
- function ve(e) {
341
+ function ye(e) {
342
342
  return (e.tournaments ?? []).map((e) => {
343
343
  let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
344
344
  return {
@@ -360,7 +360,7 @@ function ve(e) {
360
360
  };
361
361
  });
362
362
  }
363
- async function ye(e) {
363
+ async function be(e) {
364
364
  let t = d(e.baseUrl, `/v1/tournaments/${encodeURIComponent(e.urn)}`), i = new Headers();
365
365
  e.etag && i.set("If-None-Match", e.etag);
366
366
  let a = await p(t, {
@@ -380,13 +380,13 @@ async function ye(e) {
380
380
  };
381
381
  if (!a.ok) throw new n(r(a.status), a.status, `tournament request failed (HTTP ${a.status})`);
382
382
  return {
383
- tournament: be(await a.json()),
383
+ tournament: xe(await a.json()),
384
384
  etag: a.headers.get("etag") ?? void 0,
385
385
  notModified: !1,
386
386
  notFound: !1
387
387
  };
388
388
  }
389
- function be(e) {
389
+ function xe(e) {
390
390
  let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
391
391
  return {
392
392
  urn: t,
@@ -406,17 +406,17 @@ function be(e) {
406
406
  }))
407
407
  };
408
408
  }
409
- function xe(e) {
409
+ function Se(e) {
410
410
  return e.tournaments.flatMap((e) => e.matches);
411
411
  }
412
412
  //#endregion
413
413
  //#region src/core/poll.ts
414
- var Se = /* @__PURE__ */ new Set([
414
+ var Ce = /* @__PURE__ */ new Set([
415
415
  "NOT_FOUND",
416
416
  "GONE",
417
417
  "INVALID_URN"
418
418
  ]);
419
- function Ce(e) {
419
+ function we(e) {
420
420
  let t = Math.max(2e3, e.intervalMs ?? 1e4), i = new AbortController(), a, o, s, u = !1, f = () => {
421
421
  u || (a = setTimeout(m, t));
422
422
  }, m = async () => {
@@ -449,7 +449,7 @@ function Ce(e) {
449
449
  } catch (t) {
450
450
  if (!i.signal.aborted) {
451
451
  let r = t instanceof n ? t : new n("NETWORK", 0, String(t));
452
- Se.has(r.code) && (u = !0), e.onError?.(r);
452
+ Ce.has(r.code) && (u = !0), e.onError?.(r);
453
453
  }
454
454
  } finally {
455
455
  f();
@@ -461,15 +461,16 @@ function Ce(e) {
461
461
  }
462
462
  //#endregion
463
463
  //#region src/core/events.ts
464
- var we = 45e3;
465
- function T(e) {
466
- let t = Math.max(250, e.minBackoffMs ?? 1e3), n = Math.max(t, e.maxBackoffMs ?? 15e3), r = e.idleTimeoutMs ?? we, i = Number.isFinite(r) && r > 0 ? r : 0, a = new AbortController(), o = !1, c = (e) => new Promise((t) => {
464
+ var Te = 45e3;
465
+ function w(e) {
466
+ let t = Math.max(250, e.minBackoffMs ?? 1e3), n = Math.max(t, e.maxBackoffMs ?? 15e3), r = e.idleTimeoutMs ?? Te, i = Number.isFinite(r) && r > 0 ? r : 0, a = new AbortController(), o = !1, c = (e) => new Promise((t) => {
467
467
  if (a.signal.aborted) return t();
468
468
  let n = setTimeout(t, e);
469
469
  a.signal.addEventListener("abort", () => {
470
470
  clearTimeout(n), t();
471
471
  }, { once: !0 });
472
472
  }), l = (t) => {
473
+ if (o) return;
473
474
  e.onAlive?.();
474
475
  let n = "message", r = [];
475
476
  for (let e of t.split("\n")) {
@@ -544,7 +545,7 @@ function T(e) {
544
545
  o || (o = !0, a.abort());
545
546
  } };
546
547
  }
547
- function E(e) {
548
+ function T(e) {
548
549
  try {
549
550
  let t = new URL(e);
550
551
  return t.hostname = `events.${t.hostname}`, t.pathname = "", t.search = "", t.origin;
@@ -554,27 +555,27 @@ function E(e) {
554
555
  }
555
556
  //#endregion
556
557
  //#region src/core/drm/deviceId.ts
557
- var D = "havik.player.deviceId", O;
558
- function k() {
559
- if (O) return O;
558
+ var E = "havik.player.deviceId", D;
559
+ function O() {
560
+ if (D) return D;
560
561
  try {
561
- let e = globalThis.localStorage?.getItem(D);
562
- if (e) return O = e, e;
562
+ let e = globalThis.localStorage?.getItem(E);
563
+ if (e) return D = e, e;
563
564
  } catch {}
564
- let e = Ee();
565
- O = e;
565
+ let e = De();
566
+ D = e;
566
567
  try {
567
- globalThis.localStorage?.setItem(D, e);
568
+ globalThis.localStorage?.setItem(E, e);
568
569
  } catch {}
569
570
  return e;
570
571
  }
571
- function Te() {
572
- O = void 0;
572
+ function Ee() {
573
+ D = void 0;
573
574
  try {
574
- globalThis.localStorage?.removeItem(D);
575
+ globalThis.localStorage?.removeItem(E);
575
576
  } catch {}
576
577
  }
577
- function Ee() {
578
+ function De() {
578
579
  let e = globalThis.crypto;
579
580
  if (e && typeof e.randomUUID == "function") return e.randomUUID();
580
581
  let t = /* @__PURE__ */ new Uint8Array(16);
@@ -586,21 +587,69 @@ function Ee() {
586
587
  }
587
588
  //#endregion
588
589
  //#region src/core/drm/headers.ts
589
- function De(e, t, n = {}) {
590
+ function Oe(e, t, n = {}) {
590
591
  let r = {
591
592
  "x-api-key": t.apiKey,
592
593
  "X-Match-Urn": e.matchUrn,
593
- "X-Device-Id": n.deviceId ?? k(),
594
+ "X-Device-Id": n.deviceId ?? O(),
594
595
  "Content-Type": "application/octet-stream"
595
596
  };
596
597
  return n.userId && (r["X-User-Id"] = n.userId), r;
597
598
  }
598
599
  //#endregion
600
+ //#region src/core/bandwidthMemory.ts
601
+ var k = "havik.player.bandwidth", ke = .8, Ae = 2e7, je = 3e5;
602
+ function Me() {
603
+ return globalThis.navigator?.connection;
604
+ }
605
+ function Ne() {
606
+ let e = Me()?.type;
607
+ return typeof e == "string" && e !== "" && e !== "unknown" ? e : "any";
608
+ }
609
+ function Pe() {
610
+ let e = Me()?.downlink;
611
+ return typeof e == "number" && Number.isFinite(e) && e > 0 ? e * 1e6 : void 0;
612
+ }
613
+ function Fe() {
614
+ try {
615
+ let e = globalThis.localStorage?.getItem(k);
616
+ if (!e) return {};
617
+ let t = JSON.parse(e);
618
+ return t && typeof t == "object" && !Array.isArray(t) ? t : {};
619
+ } catch {
620
+ return {};
621
+ }
622
+ }
623
+ function Ie(e = Date.now()) {
624
+ let t = Fe()[Ne()];
625
+ if (!t || typeof t.bps != "number" || typeof t.at != "number" || !Number.isFinite(t.bps) || t.bps <= 0) return;
626
+ let n = e - t.at;
627
+ if (n < 0 || n > 2592e5) return;
628
+ let r = Pe(), i = r === void 0 ? t.bps : Math.min(t.bps, r);
629
+ return Math.round(Math.min(Ae, Math.max(je, i * ke)));
630
+ }
631
+ function Le(e, t = Date.now()) {
632
+ if (!Number.isFinite(e) || e <= 0) return;
633
+ let n = Fe();
634
+ n[Ne()] = {
635
+ bps: Math.round(e),
636
+ at: t
637
+ };
638
+ try {
639
+ globalThis.localStorage?.setItem(k, JSON.stringify(n));
640
+ } catch {}
641
+ }
642
+ function Re() {
643
+ try {
644
+ globalThis.localStorage?.removeItem(k);
645
+ } catch {}
646
+ }
647
+ //#endregion
599
648
  //#region src/core/drm/cdm.ts
600
- var A = /* @__PURE__ */ new WeakMap();
601
- function Oe(e, t) {
602
- let n = A.get(e);
603
- n || (n = /* @__PURE__ */ new Map(), A.set(e, n));
649
+ var ze = /* @__PURE__ */ new WeakMap();
650
+ function Be(e, t) {
651
+ let n = ze.get(e);
652
+ n || (n = /* @__PURE__ */ new Map(), ze.set(e, n));
604
653
  let r = n.get(t.keySystem);
605
654
  if (r) return r;
606
655
  let i = t.createMediaKeys().catch((e) => {
@@ -608,22 +657,22 @@ function Oe(e, t) {
608
657
  });
609
658
  return n.set(t.keySystem, i), i;
610
659
  }
611
- function j() {
660
+ function A() {
612
661
  return typeof navigator < "u" && typeof navigator.requestMediaKeySystemAccess == "function";
613
662
  }
614
- var M = ["SW_SECURE_DECODE", "SW_SECURE_CRYPTO"];
615
- function ke(e) {
663
+ var j = ["SW_SECURE_DECODE", "SW_SECURE_CRYPTO"];
664
+ function Ve(e) {
616
665
  return e.startsWith("com.widevine");
617
666
  }
618
- function N(e, t) {
619
- if (!ke(e)) return t;
667
+ function M(e, t) {
668
+ if (!Ve(e)) return t;
620
669
  let n = (e, t) => {
621
670
  let n = e.videoCapabilities;
622
671
  return !n?.length || !n.some((e) => e.robustness) ? !1 : n.every((e) => {
623
- let n = M.indexOf(e.robustness);
624
- return n === -1 || M.indexOf(t) > n;
672
+ let n = j.indexOf(e.robustness);
673
+ return n === -1 || j.indexOf(t) > n;
625
674
  });
626
- }, r = M.flatMap((e) => t.filter((t) => n(t, e)).map((t) => ({
675
+ }, r = j.flatMap((e) => t.filter((t) => n(t, e)).map((t) => ({
627
676
  ...t,
628
677
  videoCapabilities: t.videoCapabilities?.map((t) => ({
629
678
  ...t,
@@ -632,53 +681,53 @@ function N(e, t) {
632
681
  })));
633
682
  return [...t, ...r];
634
683
  }
635
- function P(e) {
636
- return (t, n) => j() ? navigator.requestMediaKeySystemAccess(t, N(t, n)).then((t) => ({
684
+ function He(e) {
685
+ return (t, n) => A() ? navigator.requestMediaKeySystemAccess(t, M(t, n)).then((t) => ({
637
686
  keySystem: t.keySystem,
638
687
  getConfiguration: () => t.getConfiguration(),
639
- createMediaKeys: () => Oe(e, t)
688
+ createMediaKeys: () => Be(e, t)
640
689
  })) : Promise.reject(/* @__PURE__ */ Error("navigator.requestMediaKeySystemAccess is unavailable — EME needs a secure context (https, or localhost)"));
641
690
  }
642
- var F = {
691
+ var N = {
643
692
  video: "SW_SECURE_DECODE",
644
693
  audio: "SW_SECURE_CRYPTO",
645
694
  scheme: "cbcs"
646
695
  };
647
- function Ae(e) {
648
- if (!j()) return;
696
+ function Ue(e) {
697
+ if (!A()) return;
649
698
  let t = (e, t) => ({
650
699
  contentType: e,
651
700
  robustness: t,
652
- encryptionScheme: F.scheme
701
+ encryptionScheme: N.scheme
653
702
  });
654
- P(e)("com.widevine.alpha", [{
703
+ He(e)("com.widevine.alpha", [{
655
704
  initDataTypes: ["cenc"],
656
705
  distinctiveIdentifier: "optional",
657
706
  persistentState: "optional",
658
707
  sessionTypes: ["temporary"],
659
- audioCapabilities: [t("audio/mp4; codecs=\"mp4a.40.2\"", F.audio)],
660
- videoCapabilities: [t("video/mp4; codecs=\"avc1.42e01e\"", F.video)]
708
+ audioCapabilities: [t("audio/mp4; codecs=\"mp4a.40.2\"", N.audio)],
709
+ videoCapabilities: [t("video/mp4; codecs=\"avc1.42e01e\"", N.video)]
661
710
  }]).then((e) => e.createMediaKeys()).catch(() => {});
662
711
  }
663
- function I(e) {
664
- let t = L(e);
712
+ function P(e) {
713
+ let t = F(e);
665
714
  if (/^avc[13]/.test(t)) return "avc";
666
715
  if (/^(hvc1|hev1)/.test(t)) return "hevc";
667
716
  if (/^(dvh1|dvhe)/.test(t)) return "dv";
668
717
  if (t.startsWith("av01")) return "av1";
669
718
  if (/^vp0?9/.test(t)) return "vp9";
670
719
  }
671
- function L(e) {
720
+ function F(e) {
672
721
  return (e ?? "").trim().toLowerCase();
673
722
  }
674
- var je = {
723
+ var We = {
675
724
  hevc: "hvc1.1.6.L93.90",
676
725
  dv: "dvh1.05.06",
677
726
  av1: "av01.0.05M.08",
678
727
  vp9: "vp09.00.10.08"
679
- }, Me = /* @__PURE__ */ new Map(), R = /* @__PURE__ */ new Map();
680
- function Ne(e) {
681
- let t = L(e), n = Me.get(t);
728
+ }, Ge = /* @__PURE__ */ new Map(), I = /* @__PURE__ */ new Map();
729
+ function Ke(e) {
730
+ let t = F(e), n = Ge.get(t);
682
731
  if (n) return n;
683
732
  let r = {
684
733
  initDataTypes: ["cenc"],
@@ -687,32 +736,32 @@ function Ne(e) {
687
736
  sessionTypes: ["temporary"],
688
737
  videoCapabilities: [{
689
738
  contentType: `video/mp4; codecs="${e.trim()}"`,
690
- robustness: F.video,
691
- encryptionScheme: F.scheme
739
+ robustness: N.video,
740
+ encryptionScheme: N.scheme
692
741
  }]
693
742
  }, i = (async () => {
694
743
  try {
695
- return await navigator.requestMediaKeySystemAccess("com.widevine.alpha", N("com.widevine.alpha", [r])), !0;
744
+ return await navigator.requestMediaKeySystemAccess("com.widevine.alpha", M("com.widevine.alpha", [r])), !0;
696
745
  } catch {
697
746
  return !1;
698
747
  }
699
- })().then((e) => (R.set(t, e), e));
700
- return Me.set(t, i), i;
748
+ })().then((e) => (I.set(t, e), e));
749
+ return Ge.set(t, i), i;
701
750
  }
702
- function Pe() {
703
- if (j()) for (let e of Object.values(je)) Ne(e);
751
+ function qe() {
752
+ if (A()) for (let e of Object.values(We)) Ke(e);
704
753
  }
705
- function Fe(e) {
706
- let t = I(e);
707
- if (t) return t === "avc" ? !0 : R.get(L(e)) ?? R.get(L(je[t]));
754
+ function Je(e) {
755
+ let t = P(e);
756
+ if (t) return t === "avc" ? !0 : I.get(F(e)) ?? I.get(F(We[t]));
708
757
  }
709
758
  //#endregion
710
759
  //#region src/core/drm/support.ts
711
- var Ie = 5e3, Le = "video/mp4; codecs=\"avc1.42E01E\"", Re = "audio/mp4; codecs=\"mp4a.40.2\"", ze = {
760
+ var Ye = 5e3, Xe = "video/mp4; codecs=\"avc1.42E01E\"", Ze = "audio/mp4; codecs=\"mp4a.40.2\"", Qe = {
712
761
  widevine: ["com.widevine.alpha"],
713
762
  fairplay: ["com.apple.fps"]
714
763
  };
715
- function Be(e) {
764
+ function $e(e) {
716
765
  return e === "fairplay" ? [{
717
766
  initDataTypes: [
718
767
  "sinf",
@@ -720,30 +769,30 @@ function Be(e) {
720
769
  "cenc"
721
770
  ],
722
771
  videoCapabilities: [{
723
- contentType: Le,
772
+ contentType: Xe,
724
773
  robustness: "",
725
774
  encryptionScheme: "cbcs"
726
775
  }],
727
776
  audioCapabilities: [{
728
- contentType: Re,
777
+ contentType: Ze,
729
778
  robustness: "",
730
779
  encryptionScheme: "cbcs"
731
780
  }]
732
- }] : N("com.widevine.alpha", [{
781
+ }] : M("com.widevine.alpha", [{
733
782
  initDataTypes: ["cenc"],
734
783
  videoCapabilities: [{
735
- contentType: Le,
736
- robustness: F.video,
737
- encryptionScheme: F.scheme
784
+ contentType: Xe,
785
+ robustness: N.video,
786
+ encryptionScheme: N.scheme
738
787
  }],
739
788
  audioCapabilities: [{
740
- contentType: Re,
741
- robustness: F.audio,
742
- encryptionScheme: F.scheme
789
+ contentType: Ze,
790
+ robustness: N.audio,
791
+ encryptionScheme: N.scheme
743
792
  }]
744
793
  }]);
745
794
  }
746
- function Ve(e, t) {
795
+ function et(e, t) {
747
796
  return new Promise((n, r) => {
748
797
  let i = setTimeout(() => r(/* @__PURE__ */ Error("probe-timeout")), t);
749
798
  e.then((e) => {
@@ -753,8 +802,8 @@ function Ve(e, t) {
753
802
  });
754
803
  });
755
804
  }
756
- async function He(e = {}) {
757
- let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? Ie, r = {};
805
+ async function tt(e = {}) {
806
+ let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? Ye, r = {};
758
807
  if (typeof window < "u" && window.isSecureContext === !1) return {
759
808
  verdict: "insecure-context",
760
809
  detail: r
@@ -766,7 +815,7 @@ async function He(e = {}) {
766
815
  };
767
816
  let a = !1, o = !1, s = Date.now() + n, c = () => Math.max(1, s - Date.now());
768
817
  for (let e of t) {
769
- let t = ze[e];
818
+ let t = Qe[e];
770
819
  if (!t) {
771
820
  r[e] = "unknown system";
772
821
  continue;
@@ -777,7 +826,7 @@ async function He(e = {}) {
777
826
  break;
778
827
  }
779
828
  try {
780
- return await Ve((await Ve(i(n, Be(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
829
+ return await et((await et(i(n, $e(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
781
830
  verdict: "supported",
782
831
  system: e,
783
832
  detail: r
@@ -801,49 +850,49 @@ async function He(e = {}) {
801
850
  }
802
851
  //#endregion
803
852
  //#region src/core/manifest.ts
804
- var Ue = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
805
- function We(e) {
806
- return Ue.test(e);
853
+ var nt = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
854
+ function rt(e) {
855
+ return nt.test(e);
807
856
  }
808
857
  //#endregion
809
858
  //#region src/core/env.ts
810
- var Ge = {
859
+ var it = {
811
860
  integration: "https://feed-dev.oddin-video.gg",
812
861
  production: "https://feed.oddin-video.gg"
813
862
  };
814
- function Ke(e) {
815
- let t = Object.prototype.hasOwnProperty.call(Ge, e) ? Ge[e] : void 0;
863
+ function L(e) {
864
+ let t = Object.prototype.hasOwnProperty.call(it, e) ? it[e] : void 0;
816
865
  if (!t) throw new n("INTERNAL", 0, `unknown env ${JSON.stringify(e)} — expected 'integration' or 'production'`);
817
866
  return t;
818
867
  }
819
868
  //#endregion
820
869
  //#region src/core/stats.ts
821
- var qe = { droppedFrames: 0 };
822
- function Je(e) {
870
+ var R = { droppedFrames: 0 };
871
+ function z(e) {
823
872
  try {
824
873
  return new URL(e).host;
825
874
  } catch {
826
875
  return "";
827
876
  }
828
877
  }
829
- var Ye = /\.(m3u8|ts|m4s|mp4|cmfv|cmfa)(\?|$)/i;
830
- function Xe(e) {
878
+ var at = /\.(m3u8|ts|m4s|mp4|cmfv|cmfa)(\?|$)/i;
879
+ function ot(e) {
831
880
  if (typeof performance > "u" || typeof performance.getEntriesByType != "function") return "";
832
881
  let t = "", n = -1, r = performance.getEntriesByType("resource");
833
- for (let i of r) if (!(i.startTime < e) && (i.initiatorType === "video" || i.initiatorType === "audio" || Ye.test(i.name)) && i.startTime >= n) {
834
- let e = Je(i.name);
882
+ for (let i of r) if (!(i.startTime < e) && (i.initiatorType === "video" || i.initiatorType === "audio" || at.test(i.name)) && i.startTime >= n) {
883
+ let e = z(i.name);
835
884
  e && (t = e, n = i.startTime);
836
885
  }
837
886
  return t;
838
887
  }
839
- function z(e) {
888
+ function B(e) {
840
889
  let t = e.getVideoPlaybackQuality?.();
841
890
  return {
842
891
  dropped: t?.droppedVideoFrames ?? 0,
843
892
  totalFrames: t?.totalVideoFrames ?? 0
844
893
  };
845
894
  }
846
- var B = class {
895
+ var st = class {
847
896
  video;
848
897
  baseFrames = 0;
849
898
  baseDropped = 0;
@@ -853,7 +902,7 @@ var B = class {
853
902
  this.video = e;
854
903
  }
855
904
  read() {
856
- let e = z(this.video);
905
+ let e = B(this.video);
857
906
  return e.totalFrames < this.lastFrames && (this.baseFrames += this.lastFrames), e.dropped < this.lastDropped && (this.baseDropped += this.lastDropped), this.lastFrames = e.totalFrames, this.lastDropped = e.dropped, {
858
907
  dropped: this.baseDropped + e.dropped,
859
908
  totalFrames: this.baseFrames + e.totalFrames
@@ -866,7 +915,7 @@ var B = class {
866
915
  };
867
916
  }
868
917
  };
869
- function Ze(e, t) {
918
+ function ct(e, t) {
870
919
  let n = [
871
920
  ["playing", () => t("playing")],
872
921
  ["waiting", () => t("buffering")],
@@ -883,8 +932,8 @@ function Ze(e, t) {
883
932
  }
884
933
  //#endregion
885
934
  //#region src/core/engine/native.ts
886
- var Qe = 2e3, $e = 3, V = 500, et = 6e3, tt = .25, nt = 250, rt = .05, it = 4;
887
- function at(e) {
935
+ var lt = 2e3, ut = 3, V = 500, dt = 6e3, ft = .25, pt = 250, mt = .05, ht = 4, gt = 12e3, _t = .25, vt = 1e3;
936
+ function yt(e) {
888
937
  try {
889
938
  let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
890
939
  return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
@@ -900,7 +949,7 @@ function U() {
900
949
  let e = "WebKitMediaKeys" in window || typeof HTMLMediaElement < "u" && "webkitSetMediaKeys" in HTMLMediaElement.prototype, t = typeof navigator < "u" && /Apple/i.test(navigator.vendor ?? "");
901
950
  return e || t;
902
951
  }
903
- function ot() {
952
+ function bt() {
904
953
  if (typeof navigator > "u" || !U()) return !1;
905
954
  let e = navigator.userAgent ?? "";
906
955
  return /iPhone|iPad|iPod/.test(e) ? !0 : /Macintosh/.test(e) && typeof navigator.maxTouchPoints == "number" && navigator.maxTouchPoints > 1;
@@ -922,6 +971,10 @@ var W = class {
922
971
  onEdgeReset = null;
923
972
  lastBufferedEnd = null;
924
973
  bufferedAdvancedAt = null;
974
+ lastSeekableEnd = null;
975
+ edgeStampedAt = null;
976
+ lastTickAt = null;
977
+ endedEmitted = !1;
925
978
  fairplayKeyIV;
926
979
  loadStarted;
927
980
  preloadTouched = !1;
@@ -931,12 +984,12 @@ var W = class {
931
984
  frameCounter;
932
985
  detachFrames = null;
933
986
  constructor(e) {
934
- this.deps = e, this.frameCounter = new B(e.video), this.loadStarted = e.deferLoad !== !0, this.builtAt = typeof performance < "u" ? performance.now() : 0;
987
+ this.deps = e, this.frameCounter = new st(e.video), this.loadStarted = e.deferLoad !== !0, this.builtAt = typeof performance < "u" ? performance.now() : 0;
935
988
  }
936
989
  async load() {
937
990
  this.detachFrames ??= this.frameCounter.attach();
938
991
  let { video: e, descriptor: t, emit: r } = this.deps;
939
- if (this.watchRefocus(), this.watchBufferedEnd(), this.loadStarted || (this.preloadTouched = !0, this.savedPreload = e.getAttribute("preload"), e.preload = "none"), t.drmEnabled) {
992
+ if (this.endedEmitted = !1, this.watchRefocus(), this.watchBufferedEnd(), this.loadStarted || (this.preloadTouched = !0, this.savedPreload = e.getAttribute("preload"), e.preload = "none"), t.drmEnabled) {
940
993
  if (!t.drm?.fairplay) {
941
994
  r({
942
995
  type: "error",
@@ -952,7 +1005,7 @@ var W = class {
952
1005
  type: "error",
953
1006
  error: new n("INTERNAL", 0, "native HLS playback error"),
954
1007
  fatal: !0
955
- }), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = ct(t.manifestUrl, t.analytics?.sid), e.load();
1008
+ }), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = St(t.manifestUrl, t.analytics?.sid), e.load();
956
1009
  }
957
1010
  startLoad() {
958
1011
  this.loadStarted || (this.loadStarted = !0, this.restorePreload());
@@ -974,7 +1027,7 @@ var W = class {
974
1027
  });
975
1028
  this.onEncrypted = (n) => {
976
1029
  let i = n;
977
- i.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = at(i.initData), (async () => {
1030
+ i.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = yt(i.initData), (async () => {
978
1031
  try {
979
1032
  let n = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
980
1033
  initDataTypes: [i.initDataType],
@@ -1073,10 +1126,10 @@ var W = class {
1073
1126
  droppedFrames: t.dropped,
1074
1127
  decodedFrames: t.totalFrames
1075
1128
  };
1076
- if (this.cdnHost ||= Xe(this.builtAt), this.cdnHost && (n.cdnHost = this.cdnHost), this.deps.descriptor.drmEnabled && (n.keySystem = "fairplay"), e.duration === Infinity) {
1129
+ if (this.cdnHost ||= ot(this.builtAt), this.cdnHost && (n.cdnHost = this.cdnHost), this.deps.descriptor.drmEnabled && (n.keySystem = "fairplay"), e.duration === Infinity) {
1077
1130
  n.isLive = !0;
1078
1131
  let t = this.liveEdgeEstimate(), r = t == null ? NaN : t - e.currentTime;
1079
- Number.isFinite(r) && (n.atLiveEdge = r <= it, !e.paused && r >= 0 && (n.latencySeconds = r));
1132
+ Number.isFinite(r) && (n.atLiveEdge = r <= ht, !e.paused && r >= 0 && (n.latencySeconds = r));
1080
1133
  } else Number.isFinite(e.duration) && e.duration > 0 && (n.isLive = !1);
1081
1134
  return n;
1082
1135
  }
@@ -1094,7 +1147,7 @@ var W = class {
1094
1147
  return;
1095
1148
  }
1096
1149
  let t = this.hiddenSince;
1097
- this.hiddenSince = null, t != null && e - t >= Qe && this.armRefocus();
1150
+ this.hiddenSince = null, t != null && e - t >= lt && this.armRefocus();
1098
1151
  }, this.onPageShow = (e) => {
1099
1152
  e.persisted && this.armRefocus();
1100
1153
  }, document.addEventListener("visibilitychange", this.onVisibility), typeof window < "u" && window.addEventListener("pageshow", this.onPageShow));
@@ -1104,7 +1157,7 @@ var W = class {
1104
1157
  let e = this.liveEnd(), t = G, n = t(), r = 0, i = () => {
1105
1158
  if (this.refocusTimer = null, Number.isFinite(this.deps.video.duration)) return;
1106
1159
  r += V;
1107
- let a = this.liveEnd(), o = a != null && (e == null || a >= e + tt), s = Math.max(r, t() - n) >= et;
1160
+ let a = this.liveEnd(), o = a != null && (e == null || a >= e + ft), s = Math.max(r, t() - n) >= dt;
1108
1161
  if (o || s && a != null) {
1109
1162
  this.snapToLive();
1110
1163
  return;
@@ -1119,21 +1172,43 @@ var W = class {
1119
1172
  watchBufferedEnd() {
1120
1173
  if (this.edgeTimer != null) return;
1121
1174
  let { video: e } = this.deps;
1122
- this.onEdgeReset = () => {
1123
- this.lastBufferedEnd = null, this.bufferedAdvancedAt = null;
1175
+ this.onEdgeReset = (e) => {
1176
+ this.lastBufferedEnd = null, this.bufferedAdvancedAt = null, e.type === "emptied" && this.resetEndStale();
1124
1177
  }, e.addEventListener("seeking", this.onEdgeReset), e.addEventListener("emptied", this.onEdgeReset), this.edgeTimer = setInterval(() => {
1125
1178
  if (e.duration !== Infinity) return;
1126
- let t = st(e, e.currentTime);
1127
- t != null && ((this.lastBufferedEnd == null || Math.abs(t - this.lastBufferedEnd) >= rt) && (this.bufferedAdvancedAt = G()), this.lastBufferedEnd = t);
1128
- }, nt);
1179
+ this.checkEndStale();
1180
+ let t = xt(e, e.currentTime);
1181
+ t != null && ((this.lastBufferedEnd == null || Math.abs(t - this.lastBufferedEnd) >= mt) && (this.bufferedAdvancedAt = G()), this.lastBufferedEnd = t);
1182
+ }, pt);
1183
+ }
1184
+ checkEndStale() {
1185
+ if (this.endedEmitted) return;
1186
+ let e = G(), t = this.lastTickAt == null ? 0 : e - this.lastTickAt;
1187
+ this.lastTickAt = e;
1188
+ let n = this.liveEnd();
1189
+ if (n == null) return;
1190
+ let r = this.lastSeekableEnd == null || Math.abs(n - this.lastSeekableEnd) >= _t;
1191
+ this.lastSeekableEnd = n;
1192
+ let i = this.deps.video, a = typeof document < "u" && document.visibilityState === "hidden";
1193
+ if (r || t > vt || a || i.paused) {
1194
+ this.edgeStampedAt = e;
1195
+ return;
1196
+ }
1197
+ e - (this.edgeStampedAt ?? e) > gt && (this.endedEmitted = !0, this.deps.emit({
1198
+ type: "ended",
1199
+ reason: "stalled"
1200
+ }));
1201
+ }
1202
+ resetEndStale() {
1203
+ this.lastSeekableEnd = null, this.edgeStampedAt = null, this.lastTickAt = null;
1129
1204
  }
1130
1205
  unwatchBufferedEnd() {
1131
- 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.lastBufferedEnd = null, this.bufferedAdvancedAt = null;
1206
+ 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;
1132
1207
  }
1133
1208
  liveEdgeEstimate() {
1134
1209
  let e = this.deps.video;
1135
1210
  if (e.duration !== Infinity) return null;
1136
- let t = this.liveEnd(), n = st(e, e.currentTime) ?? this.lastBufferedEnd;
1211
+ let t = this.liveEnd(), n = xt(e, e.currentTime) ?? this.lastBufferedEnd;
1137
1212
  if (n != null) {
1138
1213
  let e = this.bufferedAdvancedAt, r = n + (e == null ? 0 : Math.max(0, (G() - e) / 1e3));
1139
1214
  (t == null || r > t) && (t = r);
@@ -1149,7 +1224,7 @@ var W = class {
1149
1224
  snapToLive() {
1150
1225
  let e = this.deps.video, t = this.liveEnd();
1151
1226
  if (t == null) return;
1152
- let n = e.seekable.start(e.seekable.length - 1), r = Math.min(e.currentTime, t), i = Math.max(n, t - $e, r);
1227
+ let n = e.seekable.start(e.seekable.length - 1), r = Math.min(e.currentTime, t), i = Math.max(n, t - ut, r);
1153
1228
  try {
1154
1229
  e.currentTime = i;
1155
1230
  } catch {}
@@ -1170,7 +1245,7 @@ var W = class {
1170
1245
  function G() {
1171
1246
  return typeof performance < "u" ? performance.now() : Date.now();
1172
1247
  }
1173
- function st(e, t) {
1248
+ function xt(e, t) {
1174
1249
  let n = e.buffered;
1175
1250
  if (!n || !Number.isFinite(t)) return null;
1176
1251
  for (let e = 0; e < n.length; e += 1) if (n.start(e) <= t + .1 && n.end(e) > t) {
@@ -1179,7 +1254,7 @@ function st(e, t) {
1179
1254
  }
1180
1255
  return null;
1181
1256
  }
1182
- function ct(e, t) {
1257
+ function St(e, t) {
1183
1258
  return t ? `${e}${e.includes("?") ? "&" : "?"}CMCD=${encodeURIComponent(`sid="${t}"`)}` : e;
1184
1259
  }
1185
1260
  //#endregion
@@ -1196,8 +1271,8 @@ function K(e, t = Date.now()) {
1196
1271
  }
1197
1272
  //#endregion
1198
1273
  //#region src/core/engine/prefetch.ts
1199
- var lt = 15e3, ut = 5e3, dt = 6;
1200
- function ft(e) {
1274
+ var Ct = 15e3, wt = 5e3, Tt = 6;
1275
+ function Et(e) {
1201
1276
  try {
1202
1277
  let t = new URL(e);
1203
1278
  return t.searchParams.delete("CMCD"), t.toString();
@@ -1205,7 +1280,7 @@ function ft(e) {
1205
1280
  return e;
1206
1281
  }
1207
1282
  }
1208
- var pt = class {
1283
+ var Dt = class {
1209
1284
  cmcd;
1210
1285
  entries = /* @__PURE__ */ new Map();
1211
1286
  keyed = /* @__PURE__ */ new Set();
@@ -1228,10 +1303,10 @@ var pt = class {
1228
1303
  }
1229
1304
  prefetch(e, t, n) {
1230
1305
  if (!this.isActive(t)) return;
1231
- let r = ft(e);
1306
+ let r = Et(e);
1232
1307
  if (this.entries.has(r)) return;
1233
1308
  let i = this.perTrack.get(t) ?? 0;
1234
- if (i >= dt) return;
1309
+ if (i >= Tt) return;
1235
1310
  this.perTrack.set(t, i + 1);
1236
1311
  let a = new AbortController(), o = {
1237
1312
  started: performance.now(),
@@ -1245,10 +1320,10 @@ var pt = class {
1245
1320
  signal: a.signal
1246
1321
  }).then(async (e) => (o.first = performance.now(), e.ok ? e.arrayBuffer() : null)).catch(() => null), this.entries.set(r, o), this.stats.started += 1, setTimeout(() => {
1247
1322
  this.entries.get(r) === o && (this.entries.delete(r), a.abort(), this.stats.expired += 1);
1248
- }, lt);
1323
+ }, Ct);
1249
1324
  }
1250
1325
  take(e) {
1251
- let t = ft(e), n = this.entries.get(t);
1326
+ let t = Et(e), n = this.entries.get(t);
1252
1327
  return n && this.entries.delete(t), n;
1253
1328
  }
1254
1329
  destroy() {
@@ -1268,13 +1343,13 @@ var pt = class {
1268
1343
  }
1269
1344
  }
1270
1345
  };
1271
- function mt(t) {
1346
+ function Ot(t) {
1272
1347
  let n = e.DefaultConfig?.loader;
1273
1348
  if (typeof n != "function") return;
1274
1349
  class r extends n {
1275
1350
  served = !1;
1276
1351
  cancelled = !1;
1277
- stats = ht();
1352
+ stats = kt();
1278
1353
  load(e, n, r) {
1279
1354
  let i = e.rangeEnd ? void 0 : t.take(e.url);
1280
1355
  if (!i) {
@@ -1282,7 +1357,7 @@ function mt(t) {
1282
1357
  return;
1283
1358
  }
1284
1359
  this.served = !0;
1285
- let a = performance.now(), o = n?.loadPolicy?.maxTimeToFirstByteMs || ut, s = setTimeout(() => {
1360
+ let a = performance.now(), o = n?.loadPolicy?.maxTimeToFirstByteMs || wt, s = setTimeout(() => {
1286
1361
  !this.cancelled && this.served && (i.abort.abort(), t.stats.failed += 1, this.served = !1, super.load(e, n, r));
1287
1362
  }, o);
1288
1363
  i.bytes.then((o) => {
@@ -1309,7 +1384,7 @@ function mt(t) {
1309
1384
  }
1310
1385
  return r;
1311
1386
  }
1312
- function ht() {
1387
+ function kt() {
1313
1388
  return {
1314
1389
  aborted: !1,
1315
1390
  loaded: 0,
@@ -1333,10 +1408,10 @@ function ht() {
1333
1408
  }
1334
1409
  };
1335
1410
  }
1336
- function gt(e) {
1411
+ function At(e) {
1337
1412
  return e === "audio" ? "audio" : "main";
1338
1413
  }
1339
- function _t(e, t) {
1414
+ function jt(e, t) {
1340
1415
  let n = [];
1341
1416
  t.initSegment?.url && !t.initSegment.data && n.push({
1342
1417
  url: t.initSegment.url,
@@ -1355,24 +1430,24 @@ function _t(e, t) {
1355
1430
  }
1356
1431
  //#endregion
1357
1432
  //#region src/core/engine/hls.ts
1358
- function vt() {
1433
+ function Mt() {
1359
1434
  return e.isSupported();
1360
1435
  }
1361
- var yt = 3, bt = 5, xt = 3, St = 1e4, Ct = 3e4, wt = 12e3, Tt = 3e3, Et = .02, Dt = 6e3, Ot = .5, kt = 3, At = .5, jt = 1.1, Mt = .25, Nt = 5e3, Pt = 4e3, Ft = 2, It = 5, Lt = 2, Rt = 2, zt = 4, Bt = 4, Vt = 2e3, Ht = 3e4, Ut = .2, Wt = 30, Gt = 2, Kt = 2, qt = 9, Jt = .5, Yt = 64;
1362
- function Xt(e) {
1436
+ var Nt = 1e6, Pt = 20, Ft = 1e4, It = 3, Lt = 5, Rt = 3, zt = 1e4, Bt = 3e4, Vt = 12e3, Ht = 3e3, Ut = .02, Wt = 6e3, Gt = .5, Kt = 3, qt = .5, Jt = 1.1, Yt = .25, Xt = 5e3, Zt = 4e3, Qt = 2, $t = 5, en = 2, tn = 2, nn = 4, rn = 4, an = 2e3, on = 3e4, sn = .2, cn = 30, ln = 2, un = 2, dn = 9, fn = .5, pn = 64;
1437
+ function mn(e) {
1363
1438
  if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
1364
1439
  delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
1365
1440
  return;
1366
1441
  }
1367
- e.liveSyncDuration ??= Kt;
1368
- let t = Math.max(12, e.liveSyncDuration + qt);
1369
- (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Zt(e, e.liveSyncDuration);
1442
+ e.liveSyncDuration ??= un;
1443
+ let t = Math.max(12, e.liveSyncDuration + dn);
1444
+ (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), hn(e, e.liveSyncDuration);
1370
1445
  }
1371
- function Zt(e, t) {
1446
+ function hn(e, t) {
1372
1447
  let n = (e, t, n) => Math.min(n, Math.max(t, e));
1373
1448
  e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
1374
1449
  }
1375
- var Qt = class {
1450
+ var gn = class {
1376
1451
  name = "hls";
1377
1452
  hls = null;
1378
1453
  deps;
@@ -1424,6 +1499,8 @@ var Qt = class {
1424
1499
  starvedFrames = 0;
1425
1500
  deadTicks = 0;
1426
1501
  mainFragsSinceTick = 0;
1502
+ bwFragsLoaded = 0;
1503
+ bwSavedAt = 0;
1427
1504
  recentMainFrags = [];
1428
1505
  chaseSuspended = !1;
1429
1506
  savedLiveMaxLatency;
@@ -1438,6 +1515,8 @@ var Qt = class {
1438
1515
  capTimer = null;
1439
1516
  sizeCapLevel = null;
1440
1517
  bitrateCapLevel = -1;
1518
+ pushedCapLevel = -1;
1519
+ pushedCapReason = null;
1441
1520
  fpsCapBitrate = null;
1442
1521
  fpsCapFailedHeight = null;
1443
1522
  fpsCapManaged = !0;
@@ -1452,7 +1531,7 @@ var Qt = class {
1452
1531
  frameCounter;
1453
1532
  detachFrames = null;
1454
1533
  constructor(e) {
1455
- this.deps = e, this.frameCounter = new B(e.video), this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl, this.loadStarted = e.deferLoad !== !0;
1534
+ this.deps = e, this.frameCounter = new st(e.video), this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl, this.loadStarted = e.deferLoad !== !0;
1456
1535
  }
1457
1536
  async load() {
1458
1537
  this.detachFrames ??= this.frameCounter.attach();
@@ -1465,7 +1544,7 @@ var Qt = class {
1465
1544
  });
1466
1545
  return;
1467
1546
  }
1468
- e.drmEnabled && o && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && (Ae(r), Pe()), this.startHls(), t !== !1 && typeof document < "u" && (this.onVisibility = () => {
1547
+ e.drmEnabled && o && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && (Ue(r), qe()), this.startHls(), t !== !1 && typeof document < "u" && (this.onVisibility = () => {
1469
1548
  let e = this.hls;
1470
1549
  if (document.visibilityState !== "visible" || !e) return;
1471
1550
  let t = e.liveSyncPosition;
@@ -1488,7 +1567,7 @@ var Qt = class {
1488
1567
  }
1489
1568
  this.loadSuspended = !0;
1490
1569
  }
1491
- }, Nt));
1570
+ }, Xt));
1492
1571
  }
1493
1572
  resumeLoading() {
1494
1573
  if (this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.resetChaseWindow(), this.resetFrameDropWindow(), this.loadSuspended && (this.loadSuspended = !1, !this.stopped && this.hls)) {
@@ -1520,7 +1599,7 @@ var Qt = class {
1520
1599
  let t = e.liveSyncPosition;
1521
1600
  if (typeof t != "number" || !Number.isFinite(t)) return;
1522
1601
  let { video: n } = this.deps;
1523
- n.currentTime <= t + Jt || (n.currentTime = t);
1602
+ n.currentTime <= t + fn || (n.currentTime = t);
1524
1603
  }
1525
1604
  reanchorPartTimeline(e, t) {
1526
1605
  let { frag: n, part: r } = t;
@@ -1532,7 +1611,7 @@ var Qt = class {
1532
1611
  let o = n.sn, s = i.fragments.find((e) => e && e.sn === o) ?? (i.fragmentHint?.sn === o ? i.fragmentHint : void 0);
1533
1612
  if (!s) return;
1534
1613
  let c = a.startPTS - s.start;
1535
- if (!(!Number.isFinite(c) || Math.abs(c) < Et)) {
1614
+ if (!(!Number.isFinite(c) || Math.abs(c) < Ut)) {
1536
1615
  for (let e of i.fragments) e && e.setStart(e.start + c);
1537
1616
  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})`);
1538
1617
  }
@@ -1548,19 +1627,19 @@ var Qt = class {
1548
1627
  liveDurationInfinity: !0,
1549
1628
  maxBufferHole: .5,
1550
1629
  maxLiveSyncPlaybackRate: d ? 1 : 1.1,
1551
- abrEwmaDefaultEstimate: 1e6,
1630
+ abrEwmaDefaultEstimate: this.startEstimate(),
1552
1631
  capLevelToPlayerSize: !1,
1553
1632
  ...this.loadStarted ? {} : { autoStartLoad: !1 },
1554
1633
  ...u ? {
1555
1634
  emeEnabled: !0,
1556
1635
  drmSystems: { ...this.licenseUrl ? { "com.widevine.alpha": { licenseUrl: this.licenseUrl } } : {} },
1557
1636
  drmSystemOptions: {
1558
- videoRobustness: F.video,
1559
- audioRobustness: F.audio,
1560
- videoEncryptionScheme: F.scheme,
1561
- audioEncryptionScheme: F.scheme
1637
+ videoRobustness: N.video,
1638
+ audioRobustness: N.audio,
1639
+ videoEncryptionScheme: N.scheme,
1640
+ audioEncryptionScheme: N.scheme
1562
1641
  },
1563
- ...j() ? { requestMediaKeySystemAccessFunc: P(t) } : {},
1642
+ ...A() ? { requestMediaKeySystemAccessFunc: He(t) } : {},
1564
1643
  licenseXhrSetup: (e, t) => {
1565
1644
  let o = performance.now(), s = () => {
1566
1645
  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", () => {
@@ -1572,7 +1651,7 @@ var Qt = class {
1572
1651
  return;
1573
1652
  }
1574
1653
  let l = this.hls, u, d = new Promise((e) => {
1575
- u = setTimeout(() => e(void 0), St);
1654
+ u = setTimeout(() => e(void 0), zt);
1576
1655
  });
1577
1656
  return Promise.race([c().catch(() => void 0), d]).finally(() => clearTimeout(u)).then((e) => {
1578
1657
  if (!this.hls || this.hls !== l) throw Error("havik-player: engine torn down during refresh");
@@ -1588,31 +1667,31 @@ var Qt = class {
1588
1667
  ...c
1589
1668
  };
1590
1669
  if (this.prefetch?.destroy(), this.prefetch = null, u && this.deps.mediaDuringKey !== !1 && !f.fLoader) {
1591
- let e = new pt({
1670
+ let e = new Dt({
1592
1671
  sessionId: n.analytics?.sid,
1593
1672
  contentId: n.matchUrn
1594
- }), t = mt(e);
1673
+ }), t = Ot(e);
1595
1674
  t && (this.prefetch = e, f.fLoader = t);
1596
1675
  }
1597
- Xt(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;
1676
+ mn(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;
1598
1677
  let p = new e(f);
1599
1678
  if (this.hls = p, p.on(e.Events.FRAG_PARSED, (e, t) => this.reanchorPartTimeline(p, t)), p.on(e.Events.MANIFEST_PARSED, () => {
1600
1679
  this.mediaRecoveries = 0, this.networkRecoveries = 0, u && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && this.dropLevelsWidevineCannotPlay(p), this.measureLadder(p), this.applySizeCap(), this.loadSuspended = !1, t.paused && this.schedulePauseSuspend(), l({ type: "ready" });
1601
1680
  }), p.on(e.Events.FRAG_BUFFERED, (e, t) => {
1602
1681
  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(() => {
1603
1682
  this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.starvationRebuilds = 0, this.recoveryResetTimer = null;
1604
- }, Ct)));
1683
+ }, Bt)));
1605
1684
  }), p.on(e.Events.FRAG_LOADING, (e, t) => {
1606
1685
  t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
1607
1686
  }), p.on(e.Events.FRAG_LOADED, (e, t) => {
1608
- t?.frag?.type === "main" && (this.mainFragsSinceTick += 1);
1687
+ t?.frag?.type === "main" && (this.mainFragsSinceTick += 1, this.bwFragsLoaded += 1, this.maybeRememberBandwidth(!1));
1609
1688
  let n = t?.frag?.stats, r = n?.loaded;
1610
1689
  typeof r == "number" && (this.bytesLoaded += r), !this.reportedFirstBytes && typeof r == "number" && r > 0 && (this.reportedFirstBytes = !0, l({ type: "firstBytes" }));
1611
1690
  let i = n?.loading;
1612
- i && i.end > 0 && i.start > 0 && (this.requestCount += 1, this.requestTimesMs.push(Math.max(0, Math.round(i.end - i.start))), this.requestTimesMs.length > Yt && this.requestTimesMs.shift());
1691
+ i && i.end > 0 && i.start > 0 && (this.requestCount += 1, this.requestTimesMs.push(Math.max(0, Math.round(i.end - i.start))), this.requestTimesMs.length > pn && this.requestTimesMs.shift());
1613
1692
  let a = t?.frag?.url;
1614
1693
  if (a) {
1615
- let e = Je(a);
1694
+ let e = z(a);
1616
1695
  e && (this.cdnHost = e);
1617
1696
  }
1618
1697
  }), p.on(e.Events.LEVEL_LOADED, (e, t) => {
@@ -1626,15 +1705,15 @@ var Qt = class {
1626
1705
  }), p.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.prefetch) {
1627
1706
  let t = this.prefetch;
1628
1707
  p.on(e.Events.KEY_LOADING, (e, n) => {
1629
- let r = n?.frag, i = gt(r?.type);
1630
- if (r && t.isActive(i)) for (let e of _t(p, r)) t.prefetch(e.url, i, e.kind);
1631
- }), p.on(e.Events.KEY_LOADED, (e, n) => t.keyUsable(gt(n?.frag?.type)));
1708
+ let r = n?.frag, i = At(r?.type);
1709
+ if (r && t.isActive(i)) for (let e of jt(p, r)) t.prefetch(e.url, i, e.kind);
1710
+ }), p.on(e.Events.KEY_LOADED, (e, n) => t.keyUsable(At(n?.frag?.type)));
1632
1711
  }
1633
1712
  p.on(e.Events.KEY_LOADED, () => {
1634
1713
  u && !this.reportedDrmReady && (this.reportedDrmReady = !0, l({ type: "drmReady" }));
1635
1714
  }), this.endWatchdog && clearInterval(this.endWatchdog), this.endWatchdog = setInterval(() => {
1636
1715
  this.checkEndStale(), this.checkStarvation(), this.updateCatchUpBand(), this.checkFutileChase(), this.checkFrameDrops();
1637
- }, Tt), p.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1716
+ }, Ht), p.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1638
1717
  this.renditionSwitches += 1;
1639
1718
  let n = this.lastLevelIndex >= 0 ? p.levels[this.lastLevelIndex] : void 0, r = p.levels[t.level];
1640
1719
  n && r && r.bitrate !== n.bitrate && (r.bitrate > n.bitrate ? this.upshifts += 1 : this.downshifts += 1), this.lastLevelIndex = t.level, l({
@@ -1698,7 +1777,7 @@ var Qt = class {
1698
1777
  return;
1699
1778
  }
1700
1779
  this.capTimer && clearTimeout(this.capTimer);
1701
- let t = e > this.sizeCapLevel ? Vt : Ht;
1780
+ let t = e > this.sizeCapLevel ? an : on;
1702
1781
  this.capTimer = setTimeout(() => {
1703
1782
  this.capTimer = null, this.computeSizeCapTarget() === e ? this.applySizeCap() : this.onMediaResize();
1704
1783
  }, t);
@@ -1708,9 +1787,9 @@ var Qt = class {
1708
1787
  this.ladderHeights = t.length ? [...new Set(t)].sort((e, t) => e - t) : void 0, this.ladderTopBitrateKbps = e.levels.length ? Math.round(Math.max(...e.levels.map((e) => e.bitrate)) / 1e3) : 0;
1709
1788
  }
1710
1789
  dropLevelsWidevineCannotPlay(e) {
1711
- this.removeLevelsWhere(e, (e) => Fe(e) !== !0);
1712
- let t = new Set(e.levels.map((e) => e.videoCodec).filter((e) => !!e && I(e) !== "avc" && !!I(e)));
1713
- for (let n of t) Ne(n).then((t) => {
1790
+ this.removeLevelsWhere(e, (e) => Je(e) !== !0);
1791
+ let t = new Set(e.levels.map((e) => e.videoCodec).filter((e) => !!e && P(e) !== "avc" && !!P(e)));
1792
+ for (let n of t) Ke(n).then((t) => {
1714
1793
  t || this.hls !== e || this.removeLevelsWhere(e, (e) => e === n) && (this.measureLadder(e), this.applySizeCap());
1715
1794
  });
1716
1795
  }
@@ -1718,10 +1797,10 @@ var Qt = class {
1718
1797
  let n = e.levels ?? [], r = [], i = /* @__PURE__ */ new Set();
1719
1798
  if (n.forEach((e, n) => {
1720
1799
  let a = e.videoCodec;
1721
- a && I(a) && t(a) && (r.push(n), i.add(a));
1800
+ a && P(a) && t(a) && (r.push(n), i.add(a));
1722
1801
  }), !r.length) return !1;
1723
1802
  let a = [...i].join(", ");
1724
- if (!n.some((e, t) => r.includes(t) ? !1 : !I(e.videoCodec) || Fe(e.videoCodec) === !0)) return console.warn(`havik-player: every rendition is ${a}, which Widevine may not play on this browser; keeping them`), !1;
1803
+ if (!n.some((e, t) => r.includes(t) ? !1 : !P(e.videoCodec) || Je(e.videoCodec) === !0)) return console.warn(`havik-player: every rendition is ${a}, which Widevine may not play on this browser; keeping them`), !1;
1725
1804
  for (let t = r.length - 1; t >= 0; --t) e.removeLevel(r[t]);
1726
1805
  return console.info(`havik-player: skipped ${r.length} rendition(s) Widevine cannot play on this browser (${a})`), !0;
1727
1806
  }
@@ -1735,11 +1814,12 @@ var Qt = class {
1735
1814
  let e = this.hls;
1736
1815
  if (!e) return;
1737
1816
  let t = [
1738
- this.sizeCapLevel ?? -1,
1739
- this.bitrateCapLevel,
1740
- this.fpsCapLevel(e)
1741
- ].filter((e) => e >= 0);
1742
- e.autoLevelCapping = t.length ? Math.min(...t) : -1;
1817
+ ["size", this.sizeCapLevel ?? -1],
1818
+ ["bitrate", this.bitrateCapLevel],
1819
+ ["fps", this.fpsCapLevel(e)]
1820
+ ], n = -1, r = null;
1821
+ for (let [e, i] of t) i >= 0 && (n < 0 || i < n) && (n = i, r = e);
1822
+ e.autoLevelCapping = n, this.pushedCapLevel = n, this.pushedCapReason = r;
1743
1823
  }
1744
1824
  getAudioTracks() {
1745
1825
  return this.hls ? this.hls.audioTracks.map((e) => ({
@@ -1792,7 +1872,14 @@ var Qt = class {
1792
1872
  if (this.cdnHost && (n.cdnHost = this.cdnHost), this.licenseRequested && (n.keySystem = "widevine", n.drmSecurityLevel = "L3"), e) {
1793
1873
  n.qualityPinned = !e.autoLevelEnabled, typeof e.latency == "number" && (n.latencySeconds = e.latency), typeof e.targetLatency == "number" && e.targetLatency > 0 && (n.targetLatencySeconds = e.targetLatency), typeof e.bandwidthEstimate == "number" && (n.bandwidthKbps = Math.round(e.bandwidthEstimate / 1e3));
1794
1874
  let t = e.currentLevel >= 0 ? e.levels[e.currentLevel] : void 0;
1795
- t && (n.levelHeight = t.height), 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));
1875
+ t && (n.levelHeight = t.height);
1876
+ let r = e.autoLevelCapping, i = r >= 0 ? e.levels[r] : void 0;
1877
+ if (i && i.height > 0) {
1878
+ n.maxLevelHeight = i.height;
1879
+ let t = e.config ?? {};
1880
+ t.capLevelToPlayerSize !== !0 && t.capLevelOnFPSDrop !== !0 && r === this.pushedCapLevel && this.pushedCapReason && (n.capReason = this.pushedCapReason);
1881
+ }
1882
+ 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));
1796
1883
  }
1797
1884
  return n;
1798
1885
  }
@@ -1819,7 +1906,7 @@ var Qt = class {
1819
1906
  return;
1820
1907
  }
1821
1908
  let e = Date.now() - this.lastAdvanceMs;
1822
- !this.chaseSuspended && e > Dt && this.suspendChasing(), e > wt && this.emitEnded("stalled");
1909
+ !this.chaseSuspended && e > Wt && this.suspendChasing(), e > Vt && this.emitEnded("stalled");
1823
1910
  }
1824
1911
  resetStarvationWindow() {
1825
1912
  this.starvedTicks = 0, this.deadTicks = 0, this.mainFragsSinceTick = 0, this.recentMainFrags = [];
@@ -1834,7 +1921,7 @@ var Qt = class {
1834
1921
  this.resetStarvationWindow();
1835
1922
  return;
1836
1923
  }
1837
- let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < Dt, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > Pt, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = z(t), c = o - s, l = c !== this.starvedFrames;
1924
+ let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < Wt, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > Zt, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = B(t), c = o - s, l = c !== this.starvedFrames;
1838
1925
  if (this.starvedFrames = c, !a || l) {
1839
1926
  (t.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA || l) && this.clearKick(), this.resetStarvationWindow();
1840
1927
  return;
@@ -1843,12 +1930,12 @@ var Qt = class {
1843
1930
  this.resetStarvationWindow();
1844
1931
  return;
1845
1932
  }
1846
- 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 > zt && this.recentMainFrags.shift(), this.clearRecoveryReset(), this.kickPending && this.kickTicks >= Lt && (this.clearKick(), this.starvationRebuilds < Rt)) {
1933
+ 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 > nn && this.recentMainFrags.shift(), this.clearRecoveryReset(), this.kickPending && this.kickTicks >= en && (this.clearKick(), this.starvationRebuilds < tn)) {
1847
1934
  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");
1848
1935
  return;
1849
1936
  }
1850
- let u = i && this.starvedTicks >= Ft, d = !i && this.deadTicks >= zt && this.recentMainFrags.reduce((e, t) => e + t, 0) >= Bt;
1851
- if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >= It))) {
1937
+ let u = i && this.starvedTicks >= Qt, d = !i && this.deadTicks >= nn && this.recentMainFrags.reduce((e, t) => e + t, 0) >= rn;
1938
+ if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >= $t))) {
1852
1939
  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");
1853
1940
  try {
1854
1941
  e.stopLoad(), e.startLoad(-1);
@@ -1863,7 +1950,7 @@ var Qt = class {
1863
1950
  let t = e.latency, n = e.targetLatency;
1864
1951
  if (typeof t != "number" || typeof n != "number" || !Number.isFinite(t)) return;
1865
1952
  let r = t - n;
1866
- !this.catchUpEngaged && r > Ot ? this.catchUpEngaged = !0 : this.catchUpEngaged && r <= Ot && this.deps.video.playbackRate === 1 && (this.catchUpEngaged = !1);
1953
+ !this.catchUpEngaged && r > Gt ? this.catchUpEngaged = !0 : this.catchUpEngaged && r <= Gt && this.deps.video.playbackRate === 1 && (this.catchUpEngaged = !1);
1867
1954
  let i = this.catchUpEngaged ? this.catchUpRate : 1;
1868
1955
  e.config.maxLiveSyncPlaybackRate !== i && (e.config.maxLiveSyncPlaybackRate = i);
1869
1956
  }
@@ -1884,16 +1971,16 @@ var Qt = class {
1884
1971
  this.resetFrameDropWindow();
1885
1972
  return;
1886
1973
  }
1887
- let { totalFrames: n, dropped: r } = z(t), i = this.fpsLastFrames < 0, a = n - this.fpsLastFrames, o = r - this.fpsLastDropped;
1888
- if (this.fpsLastFrames = n, this.fpsLastDropped = r, i || a < Wt || o < 0) {
1974
+ let { totalFrames: n, dropped: r } = B(t), i = this.fpsLastFrames < 0, a = n - this.fpsLastFrames, o = r - this.fpsLastDropped;
1975
+ if (this.fpsLastFrames = n, this.fpsLastDropped = r, i || a < cn || o < 0) {
1889
1976
  this.fpsBadTicks = 0;
1890
1977
  return;
1891
1978
  }
1892
- if (o <= Ut * a) {
1979
+ if (o <= sn * a) {
1893
1980
  this.fpsBadTicks = 0;
1894
1981
  return;
1895
1982
  }
1896
- if (this.fpsBadTicks += 1, this.fpsBadTicks < Gt) return;
1983
+ if (this.fpsBadTicks += 1, this.fpsBadTicks < ln) return;
1897
1984
  this.fpsBadTicks = 0;
1898
1985
  let s = e.levels, c = s[e.currentLevel];
1899
1986
  if (!c) return;
@@ -1910,20 +1997,20 @@ var Qt = class {
1910
1997
  return;
1911
1998
  }
1912
1999
  let n = e.latency, r = t.playbackRate, i = e.config.maxLiveSyncPlaybackRate;
1913
- if (!(!t.paused && typeof i == "number" && i > 1 && r >= Math.min(jt, i) && r <= i && typeof n == "number" && Number.isFinite(n))) {
2000
+ if (!(!t.paused && typeof i == "number" && i > 1 && r >= Math.min(Jt, i) && r <= i && typeof n == "number" && Number.isFinite(n))) {
1914
2001
  this.resetChaseWindow();
1915
2002
  return;
1916
2003
  }
1917
- if (this.chaseLatency < 0 || this.chaseLatency - n >= At) {
2004
+ if (this.chaseLatency < 0 || this.chaseLatency - n >= qt) {
1918
2005
  this.chaseLatency = n, this.chaseTicks = 0;
1919
2006
  return;
1920
2007
  }
1921
- if (this.chaseTicks += 1, this.chaseTicks < kt) return;
2008
+ if (this.chaseTicks += 1, this.chaseTicks < Kt) return;
1922
2009
  this.resetChaseWindow();
1923
2010
  let a = typeof e.config.liveMaxLatencyDuration == "number" ? e.config.liveMaxLatencyDuration : e.maxLatency;
1924
2011
  if (!(a > 1)) return;
1925
2012
  let o = typeof e.targetLatency == "number" ? e.targetLatency : 0, s = Math.min(n, a - 1);
1926
- s < o + Mt || (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);
2013
+ s < o + Yt || (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);
1927
2014
  }
1928
2015
  suspendChasing() {
1929
2016
  let e = this.hls;
@@ -1952,8 +2039,18 @@ var Qt = class {
1952
2039
  this.deps.video.pause();
1953
2040
  } catch {}
1954
2041
  }
2042
+ startEstimate() {
2043
+ return this.deps.rememberBandwidth === !1 ? Nt : Ie() ?? Nt;
2044
+ }
2045
+ maybeRememberBandwidth(e) {
2046
+ if (this.deps.rememberBandwidth === !1 || this.bwFragsLoaded < Pt) return;
2047
+ let t = Date.now();
2048
+ if (!e && t - this.bwSavedAt < Ft) return;
2049
+ let n = this.hls?.bandwidthEstimate;
2050
+ typeof n != "number" || !Number.isFinite(n) || n <= 0 || (this.bwSavedAt = t, Le(n));
2051
+ }
1955
2052
  destroy() {
1956
- if (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.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) {
2053
+ 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.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) {
1957
2054
  try {
1958
2055
  this.hls.destroy();
1959
2056
  } catch (e) {
@@ -1968,7 +2065,7 @@ var Qt = class {
1968
2065
  scheduleLicenseRetry(t) {
1969
2066
  if (t.details !== e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED && t.details !== e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED) return !1;
1970
2067
  let n = t.response?.code ?? 0;
1971
- if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= xt) return !1;
2068
+ if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Rt) return !1;
1972
2069
  this.licenseRecoveries += 1;
1973
2070
  let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
1974
2071
  return this.licenseRetryTimer = setTimeout(() => {
@@ -2000,7 +2097,7 @@ var Qt = class {
2000
2097
  return;
2001
2098
  }
2002
2099
  if (t.type === e.ErrorTypes.NETWORK_ERROR) {
2003
- if (this.networkRecoveries < bt) {
2100
+ if (this.networkRecoveries < Lt) {
2004
2101
  this.networkRecoveries += 1;
2005
2102
  let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
2006
2103
  this.networkRetryTimer = setTimeout(() => {
@@ -2017,7 +2114,7 @@ var Qt = class {
2017
2114
  return;
2018
2115
  }
2019
2116
  if (t.type === e.ErrorTypes.MEDIA_ERROR) {
2020
- if (this.mediaRecoveries < yt) {
2117
+ if (this.mediaRecoveries < It) {
2021
2118
  this.mediaRecoveries += 1, n.recoverMediaError();
2022
2119
  return;
2023
2120
  }
@@ -2041,19 +2138,19 @@ var Qt = class {
2041
2138
  let r = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
2042
2139
  return new n(t === "DRM" ? i(r) : r === 401 ? "UNAUTHORIZED" : r === 403 ? "FORBIDDEN" : "INTERNAL", r, `${t} error: ${a}${o ? ` (${o})` : ""}`);
2043
2140
  }
2044
- }, $t = 15e3, en = 6e5, tn = 3e5, nn = 100, q = 1, rn = 1e3, an = 3e4, on = 1e3, sn = {
2141
+ }, _n = 15e3, vn = 6e5, yn = 3e5, bn = 100, q = 1, xn = 1e3, Sn = 3e4, Cn = 1e3, wn = {
2045
2142
  inline: "PRESENTATION_INLINE",
2046
2143
  fullscreen: "PRESENTATION_FULLSCREEN",
2047
2144
  pip: "PRESENTATION_PIP"
2048
- }, cn = {
2145
+ }, Tn = {
2049
2146
  widevine: "KEY_SYSTEM_WIDEVINE",
2050
2147
  fairplay: "KEY_SYSTEM_FAIRPLAY",
2051
2148
  none: "KEY_SYSTEM_NONE"
2052
2149
  };
2053
- function ln(e, t) {
2150
+ function En(e, t) {
2054
2151
  return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
2055
2152
  }
2056
- var un = /* @__PURE__ */ new Set([
2153
+ var Dn = /* @__PURE__ */ new Set([
2057
2154
  "INVALID_URN",
2058
2155
  "NOT_FOUND",
2059
2156
  "GONE",
@@ -2073,7 +2170,7 @@ var un = /* @__PURE__ */ new Set([
2073
2170
  ended: "PLAYER_STATE_ENDED",
2074
2171
  error: "PLAYER_STATE_ERROR"
2075
2172
  };
2076
- function dn(e) {
2173
+ function On(e) {
2077
2174
  try {
2078
2175
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
2079
2176
  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);
@@ -2081,8 +2178,8 @@ function dn(e) {
2081
2178
  return;
2082
2179
  }
2083
2180
  }
2084
- var fn = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), pn = /* @__PURE__ */ new Set(), Y = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), mn = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
2085
- function hn(e) {
2181
+ var kn = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), An = /* @__PURE__ */ new Set(), Y = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), jn = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
2182
+ function Mn(e) {
2086
2183
  if (e.disposition !== "enforce" || e.effectiveDirective !== "connect-src") return;
2087
2184
  let t;
2088
2185
  try {
@@ -2091,19 +2188,19 @@ function hn(e) {
2091
2188
  } catch {
2092
2189
  return;
2093
2190
  }
2094
- if (pn.has(t) && !Y.has(t)) {
2095
- Y.add(t), g(mn(new URL(t).origin));
2191
+ if (An.has(t) && !Y.has(t)) {
2192
+ Y.add(t), g(jn(new URL(t).origin));
2096
2193
  for (let e of X) e.noteCspBlocked();
2097
2194
  }
2098
2195
  }
2099
- function Z(e, t) {
2196
+ function Nn(e, t) {
2100
2197
  return g(t, e), { disabledReason: t };
2101
2198
  }
2102
- function gn(e, t, n) {
2103
- if (n) return t && !fn(t, n) && g(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
2199
+ function Pn(e, t, n) {
2200
+ if (n) return t && !kn(t, n) && g(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
2104
2201
  if (t) return { endpoint: t };
2105
- if (t !== void 0) return Z("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).");
2106
- let r = dn(e);
2202
+ if (t !== void 0) return Nn("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).");
2203
+ let r = On(e);
2107
2204
  if (r) return { endpoint: r };
2108
2205
  let i;
2109
2206
  try {
@@ -2111,9 +2208,9 @@ function gn(e, t, n) {
2111
2208
  } catch {
2112
2209
  i = e;
2113
2210
  }
2114
- return Z(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).`);
2211
+ return Nn(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).`);
2115
2212
  }
2116
- var _n = class {
2213
+ var Fn = class {
2117
2214
  opts;
2118
2215
  url;
2119
2216
  cspKey;
@@ -2195,8 +2292,8 @@ var _n = class {
2195
2292
  constructor(e) {
2196
2293
  this.opts = {
2197
2294
  ...e,
2198
- heartbeatMs: e.heartbeatMs ?? $t,
2199
- idleMs: e.idleMs ?? en
2295
+ heartbeatMs: e.heartbeatMs ?? _n,
2296
+ idleMs: e.idleMs ?? vn
2200
2297
  }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`;
2201
2298
  try {
2202
2299
  let e = new URL(this.url);
@@ -2213,11 +2310,11 @@ var _n = class {
2213
2310
  sdkVersion: this.opts.sdkVersion,
2214
2311
  ...this.opts.environment
2215
2312
  };
2216
- this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), rn), 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 && pn.add(this.cspKey), document.addEventListener("securitypolicyviolation", hn), X.add(this), this.noteCspBlocked(), this.onVisibility());
2313
+ this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), xn), 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 && An.add(this.cspKey), document.addEventListener("securitypolicyviolation", Mn), X.add(this), this.noteCspBlocked(), this.onVisibility());
2217
2314
  }
2218
2315
  checkSuspension(e) {
2219
2316
  if (this.endedFlag || !this.opts.idleMs) return !1;
2220
- let t = Math.max(this.opts.idleMs, tn);
2317
+ let t = Math.max(this.opts.idleMs, yn);
2221
2318
  return this.lastTickMs > 0 && e - this.lastTickMs >= t ? (this.endIdle(this.lastTickMs), !0) : (this.lastTickMs = e, !1);
2222
2319
  }
2223
2320
  syncIdle(e) {
@@ -2240,12 +2337,12 @@ var _n = class {
2240
2337
  let a = {
2241
2338
  fromState: J[e] ?? "PLAYER_STATE_UNSPECIFIED",
2242
2339
  toState: J[t] ?? "PLAYER_STATE_UNSPECIFIED",
2243
- presentation: sn[this.presentation]
2340
+ presentation: wn[this.presentation]
2244
2341
  }, o = this.causeOf(e, t, i);
2245
2342
  o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
2246
2343
  }
2247
2344
  seekInduced(e) {
2248
- return this.lastSeekMs >= 0 && e - this.lastSeekMs < on;
2345
+ return this.lastSeekMs >= 0 && e - this.lastSeekMs < Cn;
2249
2346
  }
2250
2347
  causeOf(e, t, n) {
2251
2348
  if (t === "error") return "TRANSITION_CAUSE_ERROR";
@@ -2319,7 +2416,7 @@ var _n = class {
2319
2416
  let r = n ?? "";
2320
2417
  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";
2321
2418
  if (e === "DRM_CLIENT") return "ERROR_STAGE_DRM";
2322
- if (un.has(e)) return "ERROR_STAGE_RESOLVE";
2419
+ if (Dn.has(e)) return "ERROR_STAGE_RESOLVE";
2323
2420
  let i = t ?? "";
2324
2421
  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";
2325
2422
  }
@@ -2366,14 +2463,14 @@ var _n = class {
2366
2463
  if (this.lastRequestCount = g, _ > 0) {
2367
2464
  o.requestCountDelta = _;
2368
2465
  let e = t.requestTimesMs ?? [], n = e.slice(Math.max(0, e.length - _)).sort((e, t) => e - t);
2369
- n.length > 0 && (o.requestTimeP50Ms = ln(n, 50), o.requestTimeP95Ms = ln(n, 95));
2466
+ n.length > 0 && (o.requestTimeP50Ms = En(n, 50), o.requestTimeP95Ms = En(n, 95));
2370
2467
  }
2371
2468
  typeof t.latencySeconds == "number" && typeof t.targetLatencySeconds == "number" && (o.driftMs = Math.round((t.latencySeconds - t.targetLatencySeconds) * 1e3)), typeof t.playbackRate == "number" && t.playbackRate > 0 && (o.playbackRate = t.playbackRate), t.qualityPinned === !0 && (o.qualityPinned = !0), t.cdnHost && (o.cdnHost = t.cdnHost), this.maybeEmitMediaInfo(t), this.enqueue({ heartbeat: o }), this.flush(), s > 0 && this.noteFirstBytes();
2372
2469
  }
2373
2470
  maybeEmitMediaInfo(e) {
2374
2471
  if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
2375
2472
  let t = {};
2376
- e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = cn[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);
2473
+ e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = Tn[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);
2377
2474
  let n = JSON.stringify(t);
2378
2475
  n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
2379
2476
  }
@@ -2393,7 +2490,7 @@ var _n = class {
2393
2490
  let c = r?.positionMs ?? 0;
2394
2491
  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));
2395
2492
  let l = e !== "END_REASON_ENDED";
2396
- l && this.lastStallMs > 0 && n - this.lastStallMs <= an && (i.abandonAfterStallMs = Math.max(q, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= an && (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);
2493
+ l && this.lastStallMs > 0 && n - this.lastStallMs <= Sn && (i.abandonAfterStallMs = Math.max(q, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= Sn && (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);
2397
2494
  let u = r?.decodedFrames ?? this.lastDecoded;
2398
2495
  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");
2399
2496
  }
@@ -2413,13 +2510,13 @@ var _n = class {
2413
2510
  };
2414
2511
  }
2415
2512
  noteCspBlocked() {
2416
- this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(mn(new URL(this.url).origin)));
2513
+ this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(jn(new URL(this.url).origin)));
2417
2514
  }
2418
2515
  cspBlocked() {
2419
2516
  return !!this.cspKey && Y.has(this.cspKey);
2420
2517
  }
2421
2518
  enqueue(e) {
2422
- if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > nn;) {
2519
+ if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > bn;) {
2423
2520
  let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
2424
2521
  this.pending.splice(e >= 0 ? e : 0, 1);
2425
2522
  }
@@ -2446,7 +2543,7 @@ var _n = class {
2446
2543
  };
2447
2544
  //#endregion
2448
2545
  //#region src/core/environment.ts
2449
- function vn() {
2546
+ function In() {
2450
2547
  if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
2451
2548
  let e;
2452
2549
  try {
@@ -2471,14 +2568,14 @@ function vn() {
2471
2568
  integration: "INTEGRATION_KIND_IFRAME"
2472
2569
  };
2473
2570
  }
2474
- function yn() {
2571
+ function Ln() {
2475
2572
  if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
2476
2573
  let e = navigator.userAgentData, t = e?.platform ?? "";
2477
2574
  if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
2478
2575
  let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
2479
2576
  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";
2480
2577
  }
2481
- function bn() {
2578
+ function Rn() {
2482
2579
  let e = typeof navigator < "u" ? navigator.connection : void 0;
2483
2580
  if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
2484
2581
  let t = {
@@ -2497,17 +2594,17 @@ function bn() {
2497
2594
  }
2498
2595
  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;
2499
2596
  }
2500
- function xn(e) {
2501
- let t = vn(), n = {
2502
- deviceClass: yn(),
2597
+ function zn(e) {
2598
+ let t = In(), n = {
2599
+ deviceClass: Ln(),
2503
2600
  integration: t.integration,
2504
- ...bn()
2601
+ ...Rn()
2505
2602
  };
2506
2603
  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);
2507
2604
  let r = e.getBoundingClientRect?.();
2508
2605
  return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
2509
2606
  }
2510
- async function Sn() {
2607
+ async function Bn() {
2511
2608
  let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
2512
2609
  if (!e?.getHighEntropyValues) return {};
2513
2610
  try {
@@ -2519,7 +2616,7 @@ async function Sn() {
2519
2616
  }
2520
2617
  //#endregion
2521
2618
  //#region src/core/version.ts
2522
- var Cn = "2.16.13", wn = {
2619
+ var Vn = "2.18.0", Hn = {
2523
2620
  accent: "#E1B600",
2524
2621
  accentText: "#1B1C23",
2525
2622
  background: "#0e0f13",
@@ -2530,7 +2627,7 @@ var Cn = "2.16.13", wn = {
2530
2627
  border: "rgba(255, 255, 255, 0.1)",
2531
2628
  radius: "8px",
2532
2629
  fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
2533
- }, Tn = {
2630
+ }, Un = {
2534
2631
  accent: "--havik-accent",
2535
2632
  accentText: "--havik-accent-text",
2536
2633
  background: "--havik-bg",
@@ -2542,26 +2639,26 @@ var Cn = "2.16.13", wn = {
2542
2639
  radius: "--havik-radius",
2543
2640
  fontFamily: "--havik-font"
2544
2641
  };
2545
- function En(e, t) {
2642
+ function Wn(e, t) {
2546
2643
  let n = {
2547
- ...wn,
2644
+ ...Hn,
2548
2645
  ...t
2549
2646
  };
2550
- for (let t of Object.keys(Tn)) e.style.setProperty(Tn[t], n[t]);
2647
+ for (let t of Object.keys(Un)) e.style.setProperty(Un[t], n[t]);
2551
2648
  n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
2552
2649
  }
2553
2650
  //#endregion
2554
2651
  //#region src/ui/styles.ts
2555
- var Dn = "havik-player-styles", On = "\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", kn = /* @__PURE__ */ new WeakMap();
2556
- function An(e = document, t) {
2557
- let n = e.querySelector(`style#${Dn}`);
2558
- if (n && (!t || kn.get(n) === t)) return;
2652
+ var Gn = "havik-player-styles", Kn = "\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", Z = /* @__PURE__ */ new WeakMap();
2653
+ function qn(e = document, t) {
2654
+ let n = e.querySelector(`style#${Gn}`);
2655
+ if (n && (!t || Z.get(n) === t)) return;
2559
2656
  let r = e.createElement("style");
2560
- r.id = Dn, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = On, kn.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
2657
+ r.id = Gn, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = Kn, Z.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
2561
2658
  }
2562
2659
  //#endregion
2563
2660
  //#region src/ui/icons.ts
2564
- var jn = {
2661
+ var Jn = {
2565
2662
  play: "<path d=\"M8 5v14l11-7z\"/>",
2566
2663
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
2567
2664
  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\"/>",
@@ -2575,20 +2672,20 @@ var jn = {
2575
2672
  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\"/>"
2576
2673
  };
2577
2674
  function Q(e) {
2578
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${jn[e]}</svg>`;
2675
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${Jn[e]}</svg>`;
2579
2676
  }
2580
2677
  //#endregion
2581
2678
  //#region src/ui/controls.ts
2582
- var Mn = 3e3, Nn = 250, Pn = /* @__PURE__ */ new Set([
2679
+ var Yn = 3e3, Xn = 250, Zn = /* @__PURE__ */ new Set([
2583
2680
  "GONE",
2584
2681
  "NOT_FOUND",
2585
2682
  "INVALID_URN",
2586
2683
  "UNAUTHORIZED"
2587
2684
  ]);
2588
- function Fn(e) {
2589
- return new In(e);
2685
+ function Qn(e) {
2686
+ return new $n(e);
2590
2687
  }
2591
- var In = class {
2688
+ var $n = class {
2592
2689
  root;
2593
2690
  video;
2594
2691
  player;
@@ -2635,7 +2732,7 @@ var In = class {
2635
2732
  scrubbing = !1;
2636
2733
  offFns = [];
2637
2734
  constructor(e) {
2638
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Mn, 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, An(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");
2735
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Yn, 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, qn(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");
2639
2736
  let t = (e, t, n) => {
2640
2737
  let r = this.doc.createElement(e);
2641
2738
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -2721,7 +2818,7 @@ var In = class {
2721
2818
  let t = this.surfaceWasHidden;
2722
2819
  this.tapTimer = setTimeout(() => {
2723
2820
  this.tapTimer = void 0, this.toggleBar(t);
2724
- }, Nn);
2821
+ }, Xn);
2725
2822
  }
2726
2823
  toggleBar(e) {
2727
2824
  e ? this.poke() : this.hideIfIdle();
@@ -2756,7 +2853,7 @@ var In = class {
2756
2853
  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);
2757
2854
  } else {
2758
2855
  let r = this.doc.createElement("div");
2759
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !Pn.has(this.lastError.code)) {
2856
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !Zn.has(this.lastError.code)) {
2760
2857
  let e = this.doc.createElement("button");
2761
2858
  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);
2762
2859
  }
@@ -2797,7 +2894,7 @@ var In = class {
2797
2894
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
2798
2895
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
2799
2896
  }
2800
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Ln(this.video.currentTime)} / ${Ln(this.video.duration)}`);
2897
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${er(this.video.currentTime)} / ${er(this.video.duration)}`);
2801
2898
  }
2802
2899
  syncLive() {
2803
2900
  let e = this.player.getStats(), t = e.isLive !== !1, n = this.player.state === "ended", r = !t && !this.streamEnded();
@@ -2847,17 +2944,17 @@ var In = class {
2847
2944
  renderQualityMenu() {
2848
2945
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
2849
2946
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
2850
- for (let n of e) this.menu.append(this.menuItem(Rn(n), t === n.index, () => this.player.setQuality(n.index)));
2947
+ for (let n of e) this.menu.append(this.menuItem(tr(n), t === n.index, () => this.player.setQuality(n.index)));
2851
2948
  }
2852
2949
  renderAudioMenu() {
2853
2950
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
2854
2951
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
2855
- for (let t of e) this.menu.append(this.menuItem(zn(t), t.default, () => this.player.setAudioTrack(t.id)));
2952
+ for (let t of e) this.menu.append(this.menuItem(nr(t), t.default, () => this.player.setAudioTrack(t.id)));
2856
2953
  }
2857
2954
  renderCcMenu() {
2858
2955
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
2859
2956
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
2860
- for (let t of e) this.menu.append(this.menuItem(Bn(t), !1, () => this.player.setTextTrack(t.id)));
2957
+ for (let t of e) this.menu.append(this.menuItem(rr(t), !1, () => this.player.setTextTrack(t.id)));
2861
2958
  }
2862
2959
  onTracksChanged() {
2863
2960
  this.openMenu && this.renderMenu(this.openMenu);
@@ -2877,11 +2974,11 @@ var In = class {
2877
2974
  }
2878
2975
  async toggleFullscreen() {
2879
2976
  try {
2880
- S(this.video, this.root) ? await w(this.video, this.root) : await C(this.video, this.root);
2977
+ x(this.video, this.root) ? await C(this.video, this.root) : await S(this.video, this.root);
2881
2978
  } catch {}
2882
2979
  }
2883
2980
  syncFullscreen() {
2884
- let e = S(this.video, this.root);
2981
+ let e = x(this.video, this.root);
2885
2982
  this.setIcon(this.fsBtn, e ? "fullscreenExit" : "fullscreen"), this.fsBtn.setAttribute("aria-label", e ? "Exit fullscreen" : "Fullscreen");
2886
2983
  }
2887
2984
  poke() {
@@ -2958,41 +3055,41 @@ var In = class {
2958
3055
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
2959
3056
  }
2960
3057
  };
2961
- function Ln(e) {
3058
+ function er(e) {
2962
3059
  (!Number.isFinite(e) || e < 0) && (e = 0);
2963
3060
  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");
2964
3061
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
2965
3062
  }
2966
- function Rn(e) {
3063
+ function tr(e) {
2967
3064
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
2968
3065
  }
2969
- function zn(e) {
3066
+ function nr(e) {
2970
3067
  return e.name || e.lang || `Track ${e.id}`;
2971
3068
  }
2972
- function Bn(e) {
3069
+ function rr(e) {
2973
3070
  return e.name || e.lang || `Track ${e.id}`;
2974
3071
  }
2975
3072
  //#endregion
2976
3073
  //#region src/managed/index.ts
2977
- var Vn = 6e3, Hn = 45e3, Un = 2e3, Wn = 3, Gn = 2e3, Kn = 48e4, qn = "keySystemLicenseRequestFailed";
2978
- function Jn(e) {
3074
+ var ir = 6e3, ar = 45e3, or = 2e3, sr = 3, cr = 5e3, lr = 2e3, ur = 48e4, dr = "keySystemLicenseRequestFailed";
3075
+ function fr(e) {
2979
3076
  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)})`);
2980
- if (e.env !== void 0) return Ke(e.env);
3077
+ if (e.env !== void 0) return L(e.env);
2981
3078
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
2982
3079
  return e.baseUrl;
2983
3080
  }
2984
- async function Yn(e) {
3081
+ async function pr(e) {
2985
3082
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
2986
- let t = Jn(e);
3083
+ let t = fr(e);
2987
3084
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
2988
3085
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
2989
- let r = new Xn({
3086
+ let r = new mr({
2990
3087
  ...e,
2991
3088
  baseUrl: t
2992
3089
  });
2993
3090
  return await r.start(), r;
2994
3091
  }
2995
- var Xn = class {
3092
+ var mr = class {
2996
3093
  opts;
2997
3094
  abort = new AbortController();
2998
3095
  listeners = {};
@@ -3006,6 +3103,9 @@ var Xn = class {
3006
3103
  refreshTimer;
3007
3104
  refreshInFlight;
3008
3105
  onRefocus;
3106
+ onReturnVisible = null;
3107
+ onReturnPageShow = null;
3108
+ hiddenAtMs = null;
3009
3109
  staleLicenseReloaded = !1;
3010
3110
  _state = "idle";
3011
3111
  _descriptor = null;
@@ -3063,7 +3163,7 @@ var Xn = class {
3063
3163
  };
3064
3164
  }
3065
3165
  beginArming(e, t, n = !1) {
3066
- let r = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? E(this.opts.baseUrl);
3166
+ let r = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? T(this.opts.baseUrl);
3067
3167
  r ? this.armWithSse(e, r, t, n) : this.beginArmingByPolling(e);
3068
3168
  }
3069
3169
  beginArmingByPolling(e) {
@@ -3084,7 +3184,7 @@ var Xn = class {
3084
3184
  setTimeout(() => {
3085
3185
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
3086
3186
  phase: i ? "unavailable" : "tooEarly",
3087
- retryInMs: Hn,
3187
+ retryInMs: ar,
3088
3188
  attempt: 1,
3089
3189
  elapsedMs: 0,
3090
3190
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -3123,13 +3223,13 @@ var Xn = class {
3123
3223
  !a && !p() && h();
3124
3224
  }, e);
3125
3225
  };
3126
- _(Vn), this.armingSub = T({
3226
+ _(ir), this.armingSub = w({
3127
3227
  eventsBaseUrl: t,
3128
3228
  matchUrn: this.opts.matchUrn,
3129
3229
  credential: this.opts.credential,
3130
- onAlive: () => _(Hn),
3230
+ onAlive: () => _(ar),
3131
3231
  onState: (e) => {
3132
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Un);
3232
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * or);
3133
3233
  },
3134
3234
  onError: () => h()
3135
3235
  });
@@ -3152,7 +3252,7 @@ var Xn = class {
3152
3252
  let n = await s(this.opts.credential);
3153
3253
  if (this.destroyed || t !== this.loadGen) return;
3154
3254
  this.setState("loading");
3155
- let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = Ze(this.opts.video, (e) => {
3255
+ let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = ct(this.opts.video, (e) => {
3156
3256
  this._state !== "ended" && this._state !== "error" && this.setState(e);
3157
3257
  });
3158
3258
  if (await i.load(), this.destroyed || t !== this.loadGen) {
@@ -3163,7 +3263,7 @@ var Xn = class {
3163
3263
  a(), i.destroy();
3164
3264
  return;
3165
3265
  }
3166
- this.engine = i, this.detachMedia = a, this.opts.maxBitrate != null && i.setMaxBitrate(this.opts.maxBitrate), this.startStats(), this.scheduleLicenseRefresh(), this.startLiveState(t), this.startAnalytics(e, i, t), r && (this.pendingStartLoad = !0, this.setState("paused"), i.name === "native" && setTimeout(() => {
3266
+ this.engine = i, this.detachMedia = a, this.opts.maxBitrate != null && i.setMaxBitrate(this.opts.maxBitrate), this.startStats(), this.scheduleLicenseRefresh(), this.startLiveState(t), this.watchReturn(), this.startAnalytics(e, i, t), r && (this.pendingStartLoad = !0, this.setState("paused"), i.name === "native" && setTimeout(() => {
3167
3267
  this.destroyed || t !== this.loadGen || this.engine !== i || (this.analyticsSession?.noteReady(), this.emit("ready"));
3168
3268
  }, 0));
3169
3269
  }
@@ -3174,26 +3274,26 @@ var Xn = class {
3174
3274
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
3175
3275
  let i = e.analytics?.sid;
3176
3276
  if (!i) return;
3177
- let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = gn(this.opts.baseUrl, a, e.analytics?.endpoint);
3277
+ let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = Pn(this.opts.baseUrl, a, e.analytics?.endpoint);
3178
3278
  if (!o) {
3179
3279
  s && setTimeout(() => {
3180
3280
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, s));
3181
3281
  }, 0);
3182
3282
  return;
3183
3283
  }
3184
- let c = this.opts.video, l = new _n({
3284
+ let c = this.opts.video, l = new Fn({
3185
3285
  endpoint: o,
3186
3286
  sid: i,
3187
3287
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
3188
- sdkVersion: Cn,
3189
- environment: xn(c),
3288
+ sdkVersion: Vn,
3289
+ environment: zn(c),
3190
3290
  idleMs: typeof this.opts.analytics == "object" ? this.opts.analytics.idleMs : void 0,
3191
3291
  onIdle: () => this.onAnalyticsIdle(t),
3192
3292
  onBlocked: (e) => setTimeout(() => {
3193
3293
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, e));
3194
3294
  }, 0),
3195
3295
  sample: () => {
3196
- let e = this.engine?.getStats() ?? qe, t = c.buffered.length ? Math.max(0, c.buffered.end(c.buffered.length - 1) - c.currentTime) : 0;
3296
+ let e = this.engine?.getStats() ?? R, t = c.buffered.length ? Math.max(0, c.buffered.end(c.buffered.length - 1) - c.currentTime) : 0;
3197
3297
  return {
3198
3298
  ...e,
3199
3299
  bufferedMs: Math.round(t * 1e3),
@@ -3207,7 +3307,7 @@ var Xn = class {
3207
3307
  let u = () => l.noteSeek(), d = () => l.noteVolume(c.muted, c.volume);
3208
3308
  c.addEventListener("seeking", u), c.addEventListener("volumechange", d), this.detachMediaListeners = () => {
3209
3309
  c.removeEventListener("seeking", u), c.removeEventListener("volumechange", d);
3210
- }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), Sn().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
3310
+ }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), Bn().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
3211
3311
  }
3212
3312
  currentPresentation() {
3213
3313
  let e = this.opts.video;
@@ -3222,10 +3322,11 @@ var Xn = class {
3222
3322
  video: this.opts.video,
3223
3323
  descriptor: e,
3224
3324
  credential: t,
3225
- deviceId: k(),
3325
+ deviceId: O(),
3226
3326
  userId: this.opts.userId,
3227
3327
  lowLatency: this.opts.lowLatency === void 0 || this.opts.lowLatency === "auto" || this.opts.lowLatency,
3228
3328
  snapToLiveOnRefocus: this.opts.snapToLiveOnRefocus,
3329
+ rememberBandwidth: this.opts.rememberBandwidth,
3229
3330
  mediaDuringKey: this.opts.mediaDuringKey,
3230
3331
  debug: this.opts.debug,
3231
3332
  deferLoad: r,
@@ -3234,9 +3335,9 @@ var Xn = class {
3234
3335
  freshLicenseUrl: () => this.refreshLicense()
3235
3336
  }, o = this.opts.engine ?? "auto";
3236
3337
  if (o === "native") return new W(a);
3237
- if (o === "hls") return new Qt(a);
3238
- if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && H(this.opts.video) && U() || !e.drmEnabled && ot() && H(this.opts.video)) return new W(a);
3239
- if (vt()) return new Qt(a);
3338
+ if (o === "hls") return new gn(a);
3339
+ if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && H(this.opts.video) && U() || !e.drmEnabled && bt() && H(this.opts.video)) return new W(a);
3340
+ if (Mt()) return new gn(a);
3240
3341
  if (H(this.opts.video)) return new W(a);
3241
3342
  throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
3242
3343
  }
@@ -3284,11 +3385,11 @@ var Xn = class {
3284
3385
  }
3285
3386
  startLiveState(e) {
3286
3387
  if (this.opts.liveStateEvents === !1) return;
3287
- let t = this.opts.eventsBaseUrl ?? E(this.opts.baseUrl);
3388
+ let t = this.opts.eventsBaseUrl ?? T(this.opts.baseUrl);
3288
3389
  if (!t) return;
3289
3390
  this.closeLiveState();
3290
3391
  let r = 0, i = !1;
3291
- this.liveStateSub = T({
3392
+ this.liveStateSub = w({
3292
3393
  eventsBaseUrl: t,
3293
3394
  matchUrn: this.opts.matchUrn,
3294
3395
  credential: this.opts.credential,
@@ -3299,13 +3400,31 @@ var Xn = class {
3299
3400
  r = 0, i = !1;
3300
3401
  },
3301
3402
  onError: () => {
3302
- this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= Wn && (i = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} after ${r} attempts — end-of-stream detection degraded to the HLS staleness watchdog. Check the events host, the api-key's allowed origins, and CSP connect-src.`))));
3403
+ this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= sr && (i = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} after ${r} attempts — end-of-stream detection degraded to the HLS staleness watchdog. Check the events host, the api-key's allowed origins, and CSP connect-src.`))));
3303
3404
  }
3304
3405
  });
3305
3406
  }
3306
3407
  closeLiveState() {
3307
3408
  this.liveStateSub?.close(), this.liveStateSub = null;
3308
3409
  }
3410
+ watchReturn() {
3411
+ this.onReturnVisible || typeof document > "u" || this.opts.liveStateEvents !== !1 && (document.visibilityState === "hidden" && (this.hiddenAtMs = Date.now()), this.onReturnVisible = () => {
3412
+ if (document.visibilityState === "hidden") {
3413
+ this.hiddenAtMs ??= Date.now();
3414
+ return;
3415
+ }
3416
+ let e = this.hiddenAtMs;
3417
+ this.hiddenAtMs = null, e != null && Date.now() - e >= cr && this.resyncLiveState();
3418
+ }, this.onReturnPageShow = (e) => {
3419
+ e.persisted && this.resyncLiveState();
3420
+ }, document.addEventListener("visibilitychange", this.onReturnVisible), typeof window < "u" && window.addEventListener("pageshow", this.onReturnPageShow));
3421
+ }
3422
+ unwatchReturn() {
3423
+ this.onReturnVisible && typeof document < "u" && document.removeEventListener("visibilitychange", this.onReturnVisible), this.onReturnPageShow && typeof window < "u" && window.removeEventListener("pageshow", this.onReturnPageShow), this.onReturnVisible = null, this.onReturnPageShow = null, this.hiddenAtMs = null;
3424
+ }
3425
+ resyncLiveState() {
3426
+ !this.destroyed && this.liveStateSub && this.startLiveState(this.loadGen);
3427
+ }
3309
3428
  onStreamEnded(e = !1, t) {
3310
3429
  if (!this.destroyed) {
3311
3430
  if ((e || !this.rejoinEnabled()) && (this.cancelRejoin(), this.closeLiveState()), this.endedHandled) {
@@ -3327,7 +3446,7 @@ var Xn = class {
3327
3446
  scheduleRejoin(e) {
3328
3447
  this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
3329
3448
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
3330
- }, Math.random() * Un));
3449
+ }, Math.random() * or));
3331
3450
  }
3332
3451
  cancelRejoin() {
3333
3452
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -3351,7 +3470,7 @@ var Xn = class {
3351
3470
  }
3352
3471
  t.controls = !1;
3353
3472
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
3354
- r && r.insertBefore(n, t), n.appendChild(t), En(n, this.opts.theme), this.container = n, this.controlBar = Fn({
3473
+ r && r.insertBefore(n, t), n.appendChild(t), Wn(n, this.opts.theme), this.container = n, this.controlBar = Qn({
3355
3474
  root: n,
3356
3475
  video: t,
3357
3476
  player: this,
@@ -3389,7 +3508,7 @@ var Xn = class {
3389
3508
  }
3390
3509
  startStats() {
3391
3510
  this.stopStats();
3392
- let e = Math.max(500, this.opts.statsIntervalMs ?? Gn);
3511
+ let e = Math.max(500, this.opts.statsIntervalMs ?? lr);
3393
3512
  this.statsTimer = setInterval(() => {
3394
3513
  if (!this.engine) return;
3395
3514
  let e = this.engine.getStats();
@@ -3400,14 +3519,14 @@ var Xn = class {
3400
3519
  this.statsTimer && clearInterval(this.statsTimer), this.statsTimer = void 0;
3401
3520
  }
3402
3521
  isStaleLicenseDenial(e) {
3403
- return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== qn || e.error.httpStatus !== 403 ? !1 : K(this._descriptor?.drm?.widevine?.licenseUrl);
3522
+ return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== dr || e.error.httpStatus !== 403 ? !1 : K(this._descriptor?.drm?.widevine?.licenseUrl);
3404
3523
  }
3405
3524
  scheduleLicenseRefresh() {
3406
3525
  if (this.stopRefresh(), !this._descriptor?.drmEnabled) return;
3407
3526
  typeof document < "u" && this.engine?.name === "hls" && (this.onRefocus = () => {
3408
3527
  document.visibilityState !== "visible" || this.destroyed || K(this._descriptor?.drm?.widevine?.licenseUrl) && this.refreshLicense();
3409
3528
  }, document.addEventListener("visibilitychange", this.onRefocus));
3410
- let e = this.opts.licenseRefreshMs ?? Kn;
3529
+ let e = this.opts.licenseRefreshMs ?? ur;
3411
3530
  e && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
3412
3531
  }
3413
3532
  stopRefresh() {
@@ -3464,7 +3583,7 @@ var Xn = class {
3464
3583
  this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
3465
3584
  }
3466
3585
  getStats() {
3467
- return this.engine?.getStats() ?? qe;
3586
+ return this.engine?.getStats() ?? R;
3468
3587
  }
3469
3588
  getQualityLevels() {
3470
3589
  return this.engine?.getQualityLevels() ?? [];
@@ -3503,10 +3622,10 @@ var Xn = class {
3503
3622
  e.pictureInPictureElement === this.opts.video && typeof e.exitPictureInPicture == "function" && await e.exitPictureInPicture();
3504
3623
  }
3505
3624
  async enterFullscreen() {
3506
- return !this.destroyed && C(this.opts.video, this.container ?? void 0);
3625
+ return !this.destroyed && S(this.opts.video, this.container ?? void 0);
3507
3626
  }
3508
3627
  async exitFullscreen() {
3509
- await w(this.opts.video, this.container ?? void 0);
3628
+ await C(this.opts.video, this.container ?? void 0);
3510
3629
  }
3511
3630
  retry() {
3512
3631
  return this.reload();
@@ -3531,7 +3650,7 @@ var Xn = class {
3531
3650
  return n.add(t), () => n.delete(t);
3532
3651
  }
3533
3652
  destroy() {
3534
- this.destroyed || (this.destroyed = !0, this.loadGen += 1, this.abort.abort(), this.stopStats(), this.stopRefresh(), this.unmountControls(), this.cancelRejoin(), this.closeArming(), this.closeLiveState(), this.detachMedia?.(), this.detachPip?.(), this.detachEndedPlayGuard?.(), this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.engine?.destroy(), this.engine = null, this.setState("idle"), this.clearListeners());
3653
+ this.destroyed || (this.destroyed = !0, this.loadGen += 1, this.abort.abort(), this.stopStats(), this.stopRefresh(), this.unmountControls(), this.cancelRejoin(), this.closeArming(), this.closeLiveState(), this.unwatchReturn(), this.detachMedia?.(), this.detachPip?.(), this.detachEndedPlayGuard?.(), this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.engine?.destroy(), this.engine = null, this.setState("idle"), this.clearListeners());
3535
3654
  }
3536
3655
  clearListeners() {
3537
3656
  for (let e of Object.keys(this.listeners)) this.listeners[e]?.clear();
@@ -3574,7 +3693,7 @@ function $(e) {
3574
3693
  }
3575
3694
  //#endregion
3576
3695
  //#region src/embed/host.ts
3577
- function Zn(e) {
3696
+ function hr(e) {
3578
3697
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
3579
3698
  if (!e.src) throw Error("mountEmbed: opts.src is required");
3580
3699
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -3633,15 +3752,15 @@ function Zn(e) {
3633
3752
  }
3634
3753
  //#endregion
3635
3754
  //#region src/embed/page.ts
3636
- var Qn = /^[A-Za-z0-9:_.-]{1,128}$/;
3637
- function $n(e) {
3755
+ var gr = /^[A-Za-z0-9:_.-]{1,128}$/;
3756
+ function _r(e) {
3638
3757
  if (typeof e == "boolean") return e;
3639
3758
  if (typeof e == "object" && e) {
3640
3759
  let t = e.endpoint;
3641
3760
  return typeof t == "string" ? { endpoint: t } : {};
3642
3761
  }
3643
3762
  }
3644
- function er() {
3763
+ function vr() {
3645
3764
  let e = window.HAVIK_EMBED_CONFIG;
3646
3765
  if (e) return {
3647
3766
  baseUrl: e.baseUrl ?? "",
@@ -3651,7 +3770,7 @@ function er() {
3651
3770
  autoplay: e.autoplay ?? !0,
3652
3771
  muted: e.muted ?? !0,
3653
3772
  waitForLive: e.waitForLive ?? !0,
3654
- analytics: $n(e.analytics)
3773
+ analytics: _r(e.analytics)
3655
3774
  };
3656
3775
  let t = new URLSearchParams(location.search);
3657
3776
  return {
@@ -3664,9 +3783,9 @@ function er() {
3664
3783
  waitForLive: !0
3665
3784
  };
3666
3785
  }
3667
- async function tr() {
3668
- let e = er();
3669
- rr();
3786
+ async function yr() {
3787
+ let e = vr();
3788
+ xr();
3670
3789
  let t = document.createElement("video");
3671
3790
  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);
3672
3791
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -3721,12 +3840,12 @@ async function tr() {
3721
3840
  code: e.code,
3722
3841
  message: e.message
3723
3842
  })), e.on("autoplayblocked", () => {
3724
- i({ type: "oddin:autoplayblocked" }), nr(t, e);
3843
+ i({ type: "oddin:autoplayblocked" }), br(t, e);
3725
3844
  });
3726
3845
  }, s = async (r) => {
3727
3846
  a?.destroy(), a = null;
3728
3847
  try {
3729
- let n = await Yn({
3848
+ let n = await pr({
3730
3849
  video: t,
3731
3850
  baseUrl: e.baseUrl,
3732
3851
  matchUrn: r,
@@ -3788,7 +3907,7 @@ async function tr() {
3788
3907
  a?.retry();
3789
3908
  break;
3790
3909
  case "oddin:load":
3791
- typeof e.matchUrn == "string" && Qn.test(e.matchUrn) && s(e.matchUrn);
3910
+ typeof e.matchUrn == "string" && gr.test(e.matchUrn) && s(e.matchUrn);
3792
3911
  break;
3793
3912
  case "oddin:destroy": c();
3794
3913
  }
@@ -3800,18 +3919,18 @@ async function tr() {
3800
3919
  protocol: 1
3801
3920
  });
3802
3921
  }
3803
- function nr(e, t) {
3922
+ function br(e, t) {
3804
3923
  if (document.getElementById("havik-tap")) return;
3805
3924
  let n = document.createElement("button");
3806
3925
  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", () => {
3807
3926
  n.remove(), t.play();
3808
3927
  }), document.body.appendChild(n);
3809
3928
  }
3810
- function rr() {
3929
+ function xr() {
3811
3930
  let e = document.createElement("style");
3812
3931
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
3813
3932
  }
3814
3933
  //#endregion
3815
- export { wn as ODDIN_THEME, n as PlaybackError, En as applyTheme, Ke as baseUrlForEnv, tr as bootEmbed, Te as clearDeviceId, Yn as createPlayer, E as deriveEventsBaseUrl, He as detectDrmSupport, C as enterVideoFullscreen, w as exitVideoFullscreen, _e as fetchCatalog, ye as fetchTournament, xe as flattenMatches, k as getDeviceId, We as isLowLatencyManifest, $ as isOddinMessage, De as licenseRequestHeaders, Zn as mountEmbed, de as pollToLive, y as resolveStream, T as subscribeLiveState, S as videoFullscreenActive, Ce as watchStatus };
3934
+ export { Hn as ODDIN_THEME, n as PlaybackError, Wn as applyTheme, L as baseUrlForEnv, yr as bootEmbed, Ee as clearDeviceId, Re as clearRememberedBandwidth, pr as createPlayer, T as deriveEventsBaseUrl, tt as detectDrmSupport, S as enterVideoFullscreen, C as exitVideoFullscreen, ve as fetchCatalog, be as fetchTournament, Se as flattenMatches, O as getDeviceId, rt as isLowLatencyManifest, $ as isOddinMessage, Oe as licenseRequestHeaders, hr as mountEmbed, de as pollToLive, y as resolveStream, w as subscribeLiveState, x as videoFullscreenActive, we as watchStatus };
3816
3935
 
3817
3936
  //# sourceMappingURL=index.js.map