@oddin-gg/havik-player 2.2.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -516,12 +516,56 @@ function ve(e, t, n = {}) {
516
516
  return n.userId && (r["X-User-Id"] = n.userId), r;
517
517
  }
518
518
  //#endregion
519
+ //#region src/core/drm/cdm.ts
520
+ var j = /* @__PURE__ */ new WeakMap();
521
+ function ye(e, t) {
522
+ let n = j.get(e);
523
+ n || (n = /* @__PURE__ */ new Map(), j.set(e, n));
524
+ let r = n.get(t.keySystem);
525
+ if (r) return r;
526
+ let i = t.createMediaKeys().catch((e) => {
527
+ throw n.get(t.keySystem) === i && n.delete(t.keySystem), e;
528
+ });
529
+ return n.set(t.keySystem, i), i;
530
+ }
531
+ function M() {
532
+ return typeof navigator < "u" && typeof navigator.requestMediaKeySystemAccess == "function";
533
+ }
534
+ var N = ["SW_SECURE_DECODE", "SW_SECURE_CRYPTO"];
535
+ function be(e) {
536
+ return e.startsWith("com.widevine");
537
+ }
538
+ function P(e, t) {
539
+ if (!be(e)) return t;
540
+ let n = (e, t) => {
541
+ let n = e.videoCapabilities;
542
+ return !n?.length || !n.some((e) => e.robustness) ? !1 : n.every((e) => {
543
+ let n = N.indexOf(e.robustness);
544
+ return n === -1 || N.indexOf(t) > n;
545
+ });
546
+ }, r = N.flatMap((e) => t.filter((t) => n(t, e)).map((t) => ({
547
+ ...t,
548
+ videoCapabilities: t.videoCapabilities?.map((t) => ({
549
+ ...t,
550
+ robustness: e
551
+ }))
552
+ })));
553
+ return [...t, ...r];
554
+ }
555
+ function xe(e) {
556
+ return (t, n) => M() ? navigator.requestMediaKeySystemAccess(t, P(t, n)).then((t) => ({
557
+ keySystem: t.keySystem,
558
+ getConfiguration: () => t.getConfiguration(),
559
+ createMediaKeys: () => ye(e, t)
560
+ })) : Promise.reject(/* @__PURE__ */ Error("navigator.requestMediaKeySystemAccess is unavailable — EME needs a secure context (https, or localhost)"));
561
+ }
562
+ //#endregion
519
563
  //#region src/core/drm/support.ts
520
- var ye = 5e3, j = "video/mp4; codecs=\"avc1.42E01E\"", M = "audio/mp4; codecs=\"mp4a.40.2\"", be = {
564
+ var Se = 5e3, Ce = "video/mp4; codecs=\"avc1.42E01E\"", F = "audio/mp4; codecs=\"mp4a.40.2\"", we = {
521
565
  widevine: ["com.widevine.alpha"],
522
566
  fairplay: ["com.apple.fps"]
523
567
  };
524
- function xe(e) {
568
+ function Te(e) {
525
569
  return e === "fairplay" ? [{
526
570
  initDataTypes: [
527
571
  "sinf",
@@ -529,30 +573,30 @@ function xe(e) {
529
573
  "cenc"
530
574
  ],
531
575
  videoCapabilities: [{
532
- contentType: j,
576
+ contentType: Ce,
533
577
  robustness: "",
534
578
  encryptionScheme: "cbcs"
535
579
  }],
536
580
  audioCapabilities: [{
537
- contentType: M,
581
+ contentType: F,
538
582
  robustness: "",
539
583
  encryptionScheme: "cbcs"
540
584
  }]
541
- }] : [{
585
+ }] : P("com.widevine.alpha", [{
542
586
  initDataTypes: ["cenc"],
543
587
  videoCapabilities: [{
544
- contentType: j,
588
+ contentType: Ce,
545
589
  robustness: "SW_SECURE_DECODE",
546
590
  encryptionScheme: "cbcs"
547
591
  }],
548
592
  audioCapabilities: [{
549
- contentType: M,
593
+ contentType: F,
550
594
  robustness: "SW_SECURE_CRYPTO",
551
595
  encryptionScheme: "cbcs"
552
596
  }]
553
- }];
597
+ }]);
554
598
  }
555
- function N(e, t) {
599
+ function I(e, t) {
556
600
  return new Promise((n, r) => {
557
601
  let i = setTimeout(() => r(/* @__PURE__ */ Error("probe-timeout")), t);
558
602
  e.then((e) => {
@@ -562,8 +606,8 @@ function N(e, t) {
562
606
  });
563
607
  });
564
608
  }
565
- async function Se(e = {}) {
566
- let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? ye, r = {};
609
+ async function Ee(e = {}) {
610
+ let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? Se, r = {};
567
611
  if (typeof window < "u" && window.isSecureContext === !1) return {
568
612
  verdict: "insecure-context",
569
613
  detail: r
@@ -575,7 +619,7 @@ async function Se(e = {}) {
575
619
  };
576
620
  let a = !1, o = !1, s = Date.now() + n, c = () => Math.max(1, s - Date.now());
577
621
  for (let e of t) {
578
- let t = be[e];
622
+ let t = we[e];
579
623
  if (!t) {
580
624
  r[e] = "unknown system";
581
625
  continue;
@@ -586,7 +630,7 @@ async function Se(e = {}) {
586
630
  break;
587
631
  }
588
632
  try {
589
- return await N((await N(i(n, xe(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
633
+ return await I((await I(i(n, Te(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
590
634
  verdict: "supported",
591
635
  system: e,
592
636
  detail: r
@@ -610,44 +654,21 @@ async function Se(e = {}) {
610
654
  }
611
655
  //#endregion
612
656
  //#region src/core/manifest.ts
613
- var Ce = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
614
- function we(e) {
615
- return Ce.test(e);
616
- }
617
- //#endregion
618
- //#region src/core/drm/cdm.ts
619
- var Te = /* @__PURE__ */ new WeakMap();
620
- function Ee(e, t) {
621
- let n = Te.get(e);
622
- n || (n = /* @__PURE__ */ new Map(), Te.set(e, n));
623
- let r = n.get(t.keySystem);
624
- if (r) return r;
625
- let i = t.createMediaKeys().catch((e) => {
626
- throw n.get(t.keySystem) === i && n.delete(t.keySystem), e;
627
- });
628
- return n.set(t.keySystem, i), i;
629
- }
630
- function P() {
631
- return typeof navigator < "u" && typeof navigator.requestMediaKeySystemAccess == "function";
632
- }
633
- function De(e) {
634
- return (t, n) => P() ? navigator.requestMediaKeySystemAccess(t, n).then((t) => ({
635
- keySystem: t.keySystem,
636
- getConfiguration: () => t.getConfiguration(),
637
- createMediaKeys: () => Ee(e, t)
638
- })) : Promise.reject(/* @__PURE__ */ Error("navigator.requestMediaKeySystemAccess is unavailable — EME needs a secure context (https, or localhost)"));
657
+ var De = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
658
+ function Oe(e) {
659
+ return De.test(e);
639
660
  }
640
661
  //#endregion
641
662
  //#region src/core/stats.ts
642
- var F = { droppedFrames: 0 };
643
- function I(e) {
663
+ var L = { droppedFrames: 0 };
664
+ function R(e) {
644
665
  let t = e.getVideoPlaybackQuality?.();
645
666
  return {
646
667
  dropped: t?.droppedVideoFrames ?? 0,
647
668
  decoded: t?.totalVideoFrames ?? 0
648
669
  };
649
670
  }
650
- function Oe(e, t) {
671
+ function ke(e, t) {
651
672
  let n = [
652
673
  ["playing", () => t("playing")],
653
674
  ["waiting", () => t("buffering")],
@@ -664,7 +685,7 @@ function Oe(e, t) {
664
685
  }
665
686
  //#endregion
666
687
  //#region src/core/engine/native.ts
667
- function ke(e) {
688
+ function Ae(e) {
668
689
  try {
669
690
  let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
670
691
  return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
@@ -672,15 +693,15 @@ function ke(e) {
672
693
  return;
673
694
  }
674
695
  }
675
- function L(e) {
696
+ function z(e) {
676
697
  return !!(e ?? (typeof document < "u" ? document.createElement("video") : void 0))?.canPlayType("application/vnd.apple.mpegurl");
677
698
  }
678
- function R() {
699
+ function B() {
679
700
  if (typeof window > "u") return !1;
680
701
  let e = "WebKitMediaKeys" in window || typeof HTMLMediaElement < "u" && "webkitSetMediaKeys" in HTMLMediaElement.prototype, t = typeof navigator < "u" && /Apple/i.test(navigator.vendor ?? "");
681
702
  return e || t;
682
703
  }
683
- var z = class {
704
+ var V = class {
684
705
  name = "native";
685
706
  deps;
686
707
  onLoaded;
@@ -714,7 +735,7 @@ var z = class {
714
735
  type: "error",
715
736
  error: new n("INTERNAL", 0, "native HLS playback error"),
716
737
  fatal: !0
717
- }), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = Ae(t.manifestUrl, t.analytics?.sid), e.load();
738
+ }), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = je(t.manifestUrl, t.analytics?.sid), e.load();
718
739
  }
719
740
  startLoad() {
720
741
  this.loadStarted || (this.loadStarted = !0, this.restorePreload());
@@ -736,7 +757,7 @@ var z = class {
736
757
  });
737
758
  this.onEncrypted = (n) => {
738
759
  let i = n;
739
- !i.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV = ke(i.initData), (async () => {
760
+ !i.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV = Ae(i.initData), (async () => {
740
761
  try {
741
762
  let n = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
742
763
  initDataTypes: [i.initDataType],
@@ -831,7 +852,7 @@ var z = class {
831
852
  e.seekable.length > 0 && (e.currentTime = e.seekable.end(e.seekable.length - 1));
832
853
  }
833
854
  getStats() {
834
- let e = this.deps.video, t = I(e), n = {
855
+ let e = this.deps.video, t = R(e), n = {
835
856
  droppedFrames: t.dropped,
836
857
  decodedFrames: t.decoded
837
858
  };
@@ -854,36 +875,36 @@ var z = class {
854
875
  } catch {}
855
876
  }
856
877
  };
857
- function Ae(e, t) {
878
+ function je(e, t) {
858
879
  return t ? `${e}${e.includes("?") ? "&" : "?"}CMCD=${encodeURIComponent(`sid="${t}"`)}` : e;
859
880
  }
860
881
  //#endregion
861
882
  //#region src/core/engine/hls.ts
862
- function je() {
883
+ function Me() {
863
884
  return e.isSupported();
864
885
  }
865
- var Me = 3, Ne = 5, Pe = 3, Fe = 3e4, Ie = 12e3, Le = 3e3, B = 6e3, Re = 4e3, ze = 2, Be = 5, Ve = 2, He = 2, Ue = 2e3, We = 3e4, Ge = 2, Ke = 9, qe = .5, Je = 64;
866
- function Ye(e) {
886
+ var Ne = 3, Pe = 5, Fe = 3, Ie = 3e4, Le = 12e3, Re = 3e3, H = 6e3, ze = 4e3, Be = 2, Ve = 5, He = 2, Ue = 2, We = 2e3, Ge = 3e4, Ke = 2, qe = 9, Je = .5, Ye = 64;
887
+ function Xe(e) {
867
888
  try {
868
889
  return new URL(e).host;
869
890
  } catch {
870
891
  return "";
871
892
  }
872
893
  }
873
- function Xe(e) {
894
+ function Ze(e) {
874
895
  if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
875
896
  delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
876
897
  return;
877
898
  }
878
- e.liveSyncDuration ??= Ge;
879
- let t = Math.max(12, e.liveSyncDuration + Ke);
880
- (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Ze(e, e.liveSyncDuration);
899
+ e.liveSyncDuration ??= Ke;
900
+ let t = Math.max(12, e.liveSyncDuration + qe);
901
+ (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Qe(e, e.liveSyncDuration);
881
902
  }
882
- function Ze(e, t) {
903
+ function Qe(e, t) {
883
904
  let n = (e, t, n) => Math.min(n, Math.max(t, e));
884
905
  e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
885
906
  }
886
- var V = class {
907
+ var U = class {
887
908
  name = "hls";
888
909
  hls = null;
889
910
  deps;
@@ -937,7 +958,7 @@ var V = class {
937
958
  this.deps = e, this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl, this.loadStarted = e.deferLoad !== !0;
938
959
  }
939
960
  async load() {
940
- let { descriptor: e, snapToLiveOnRefocus: t, video: r, emit: i } = this.deps, a = L(r) && R(), o = !!e.drm?.widevine?.licenseUrl && !a;
961
+ let { descriptor: e, snapToLiveOnRefocus: t, video: r, emit: i } = this.deps, a = z(r) && B(), o = !!e.drm?.widevine?.licenseUrl && !a;
941
962
  if (e.drmEnabled && !o) {
942
963
  i({
943
964
  type: "error",
@@ -977,7 +998,7 @@ var V = class {
977
998
  let t = e.liveSyncPosition;
978
999
  if (typeof t != "number" || !Number.isFinite(t)) return;
979
1000
  let { video: n } = this.deps;
980
- n.currentTime <= t + qe || (n.currentTime = t);
1001
+ n.currentTime <= t + Je || (n.currentTime = t);
981
1002
  }
982
1003
  startHls() {
983
1004
  let { video: t, descriptor: n, credential: r, deviceId: i, userId: a, lowLatency: o, debug: s, hlsConfig: c, emit: l } = this.deps;
@@ -1001,7 +1022,7 @@ var V = class {
1001
1022
  videoEncryptionScheme: "cbcs",
1002
1023
  audioEncryptionScheme: "cbcs"
1003
1024
  },
1004
- ...P() ? { requestMediaKeySystemAccessFunc: De(t) } : {},
1025
+ ...M() ? { requestMediaKeySystemAccessFunc: xe(t) } : {},
1005
1026
  licenseXhrSetup: (e, t) => {
1006
1027
  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;
1007
1028
  let o = performance.now();
@@ -1017,7 +1038,7 @@ var V = class {
1017
1038
  } } : {},
1018
1039
  ...c
1019
1040
  };
1020
- Xe(d), this.sizeCapManaged = d.capLevelToPlayerSize !== !0, this.loadStarted = d.autoStartLoad !== !1;
1041
+ Ze(d), this.sizeCapManaged = d.capLevelToPlayerSize !== !0, this.loadStarted = d.autoStartLoad !== !1;
1021
1042
  let f = new e(d);
1022
1043
  this.hls = f, f.on(e.Events.MANIFEST_PARSED, () => {
1023
1044
  this.mediaRecoveries = 0, this.networkRecoveries = 0;
@@ -1026,17 +1047,17 @@ var V = class {
1026
1047
  }), f.on(e.Events.FRAG_BUFFERED, (e, t) => {
1027
1048
  t?.frag?.type === "main" && (this.kickPending = !1, this.kickTicks = 0, (this.mediaRecoveries > 0 || this.networkRecoveries > 0 || this.licenseRecoveries > 0 || this.starvationKicks > 0 || this.starvationRebuilds > 0) && !this.recoveryResetTimer && (this.recoveryResetTimer = setTimeout(() => {
1028
1049
  this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.starvationRebuilds = 0, this.recoveryResetTimer = null;
1029
- }, Fe)));
1050
+ }, Ie)));
1030
1051
  }), f.on(e.Events.FRAG_LOADING, (e, t) => {
1031
1052
  t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
1032
1053
  }), f.on(e.Events.FRAG_LOADED, (e, t) => {
1033
1054
  let n = t?.frag?.stats, r = n?.loaded;
1034
1055
  typeof r == "number" && (this.bytesLoaded += r);
1035
1056
  let i = n?.loading;
1036
- 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 > Je && this.requestTimesMs.shift());
1057
+ 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 > Ye && this.requestTimesMs.shift());
1037
1058
  let a = t?.frag?.url;
1038
1059
  if (a) {
1039
- let e = Ye(a);
1060
+ let e = Xe(a);
1040
1061
  e && (this.cdnHost = e);
1041
1062
  }
1042
1063
  }), f.on(e.Events.LEVEL_LOADED, (e, t) => {
@@ -1049,7 +1070,7 @@ var V = class {
1049
1070
  }));
1050
1071
  }), f.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.endWatchdog && clearInterval(this.endWatchdog), this.endWatchdog = setInterval(() => {
1051
1072
  this.checkEndStale(), this.checkStarvation();
1052
- }, Le), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1073
+ }, Re), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1053
1074
  this.renditionSwitches += 1;
1054
1075
  let n = this.lastLevelIndex >= 0 ? f.levels[this.lastLevelIndex] : void 0, r = f.levels[t.level];
1055
1076
  n && r && r.bitrate !== n.bitrate && (r.bitrate > n.bitrate ? this.upshifts += 1 : this.downshifts += 1), this.lastLevelIndex = t.level, l({
@@ -1113,7 +1134,7 @@ var V = class {
1113
1134
  return;
1114
1135
  }
1115
1136
  this.capTimer && clearTimeout(this.capTimer);
1116
- let t = e > this.sizeCapLevel ? Ue : We;
1137
+ let t = e > this.sizeCapLevel ? We : Ge;
1117
1138
  this.capTimer = setTimeout(() => {
1118
1139
  this.capTimer = null, this.computeSizeCapTarget() === e ? this.applySizeCap() : this.onMediaResize();
1119
1140
  }, t);
@@ -1160,7 +1181,7 @@ var V = class {
1160
1181
  e.widevine && (this.licenseUrl = e.widevine);
1161
1182
  }
1162
1183
  getStats() {
1163
- let e = this.hls, t = I(this.deps.video), n = {
1184
+ let e = this.hls, t = R(this.deps.video), n = {
1164
1185
  droppedFrames: t.dropped,
1165
1186
  decodedFrames: t.decoded,
1166
1187
  lowLatency: this.detectedLowLatency,
@@ -1202,7 +1223,7 @@ var V = class {
1202
1223
  return;
1203
1224
  }
1204
1225
  let e = Date.now() - this.lastAdvanceMs;
1205
- !this.chaseSuspended && e > B && this.suspendChasing(), e > Ie && this.emitEnded("stalled");
1226
+ !this.chaseSuspended && e > H && this.suspendChasing(), e > Le && this.emitEnded("stalled");
1206
1227
  }
1207
1228
  checkStarvation() {
1208
1229
  let e = this.hls, { video: t } = this.deps;
@@ -1211,17 +1232,17 @@ var V = class {
1211
1232
  this.starvedTicks = 0;
1212
1233
  return;
1213
1234
  }
1214
- let n = this.lastAdvanceMs !== 0 && Date.now() - this.lastAdvanceMs < B, r = this.lastFragLoadingMs !== 0 && Date.now() - this.lastFragLoadingMs > Re;
1235
+ let n = this.lastAdvanceMs !== 0 && Date.now() - this.lastAdvanceMs < H, r = this.lastFragLoadingMs !== 0 && Date.now() - this.lastFragLoadingMs > ze;
1215
1236
  if (!(t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused && n && r)) {
1216
1237
  this.starvedTicks = 0;
1217
1238
  return;
1218
1239
  }
1219
- if (this.kickPending && (this.kickTicks += 1), !(++this.starvedTicks < ze)) {
1220
- if (this.starvedTicks = 0, this.kickPending && this.kickTicks >= Ve && (this.kickPending = !1, this.kickTicks = 0, this.starvationRebuilds < He)) {
1240
+ if (this.kickPending && (this.kickTicks += 1), !(++this.starvedTicks < Be)) {
1241
+ if (this.starvedTicks = 0, this.kickPending && this.kickTicks >= He && (this.kickPending = !1, this.kickTicks = 0, this.starvationRebuilds < Ue)) {
1221
1242
  this.starvationRebuilds += 1, 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");
1222
1243
  return;
1223
1244
  }
1224
- if (!(this.starvationKicks >= Be)) {
1245
+ if (!(this.starvationKicks >= Ve)) {
1225
1246
  this.starvationKicks += 1, this.kickPending = !0, this.kickTicks = 0, console.warn("havik-player: video starved while the live playlist advances (wedged hls.js fragment pipeline) — restarting the load");
1226
1247
  try {
1227
1248
  e.stopLoad(), e.startLoad(-1);
@@ -1271,7 +1292,7 @@ var V = class {
1271
1292
  scheduleLicenseRetry(t) {
1272
1293
  if (t.details !== e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED && t.details !== e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED) return !1;
1273
1294
  let n = t.response?.code ?? 0;
1274
- if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Pe) return !1;
1295
+ if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Fe) return !1;
1275
1296
  this.licenseRecoveries += 1;
1276
1297
  let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
1277
1298
  return this.licenseRetryTimer = setTimeout(() => {
@@ -1303,7 +1324,7 @@ var V = class {
1303
1324
  return;
1304
1325
  }
1305
1326
  if (t.type === e.ErrorTypes.NETWORK_ERROR) {
1306
- if (this.networkRecoveries < Ne) {
1327
+ if (this.networkRecoveries < Pe) {
1307
1328
  this.networkRecoveries += 1;
1308
1329
  let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
1309
1330
  this.networkRetryTimer = setTimeout(() => {
@@ -1320,7 +1341,7 @@ var V = class {
1320
1341
  return;
1321
1342
  }
1322
1343
  if (t.type === e.ErrorTypes.MEDIA_ERROR) {
1323
- if (this.mediaRecoveries < Me) {
1344
+ if (this.mediaRecoveries < Ne) {
1324
1345
  this.mediaRecoveries += 1, n.recoverMediaError();
1325
1346
  return;
1326
1347
  }
@@ -1344,19 +1365,19 @@ var V = class {
1344
1365
  let r = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
1345
1366
  return new n(t === "DRM" ? i(r) : r === 401 ? "UNAUTHORIZED" : r === 403 ? "FORBIDDEN" : "INTERNAL", r, `${t} error: ${a}${o ? ` (${o})` : ""}`);
1346
1367
  }
1347
- }, Qe = 15e3, $e = 100, H = 1, et = 1e3, U = 3e4, tt = {
1368
+ }, $e = 15e3, et = 100, W = 1, tt = 1e3, G = 3e4, nt = {
1348
1369
  inline: "PRESENTATION_INLINE",
1349
1370
  fullscreen: "PRESENTATION_FULLSCREEN",
1350
1371
  pip: "PRESENTATION_PIP"
1351
- }, nt = {
1372
+ }, rt = {
1352
1373
  widevine: "KEY_SYSTEM_WIDEVINE",
1353
1374
  fairplay: "KEY_SYSTEM_FAIRPLAY",
1354
1375
  none: "KEY_SYSTEM_NONE"
1355
1376
  };
1356
- function W(e, t) {
1377
+ function K(e, t) {
1357
1378
  return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
1358
1379
  }
1359
- var rt = /* @__PURE__ */ new Set([
1380
+ var it = /* @__PURE__ */ new Set([
1360
1381
  "INVALID_URN",
1361
1382
  "NOT_FOUND",
1362
1383
  "GONE",
@@ -1366,7 +1387,7 @@ var rt = /* @__PURE__ */ new Set([
1366
1387
  "FORBIDDEN",
1367
1388
  "RATE_LIMITED",
1368
1389
  "TIMEOUT"
1369
- ]), G = {
1390
+ ]), q = {
1370
1391
  idle: "PLAYER_STATE_IDLE",
1371
1392
  waiting: "PLAYER_STATE_WAITING",
1372
1393
  loading: "PLAYER_STATE_LOADING",
@@ -1376,7 +1397,7 @@ var rt = /* @__PURE__ */ new Set([
1376
1397
  ended: "PLAYER_STATE_ENDED",
1377
1398
  error: "PLAYER_STATE_ERROR"
1378
1399
  };
1379
- function it(e) {
1400
+ function at(e) {
1380
1401
  try {
1381
1402
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
1382
1403
  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);
@@ -1384,7 +1405,7 @@ function it(e) {
1384
1405
  return;
1385
1406
  }
1386
1407
  }
1387
- var at = class {
1408
+ var ot = class {
1388
1409
  opts;
1389
1410
  url;
1390
1411
  now;
@@ -1451,7 +1472,7 @@ var at = class {
1451
1472
  constructor(e) {
1452
1473
  this.opts = {
1453
1474
  ...e,
1454
- heartbeatMs: e.heartbeatMs ?? Qe
1475
+ heartbeatMs: e.heartbeatMs ?? $e
1455
1476
  }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`, this.now = e.now ?? (() => Date.now()), this.bornMs = this.now(), this.presentationSinceMs = this.bornMs;
1456
1477
  }
1457
1478
  start() {
@@ -1461,16 +1482,16 @@ var at = class {
1461
1482
  sdkVersion: this.opts.sdkVersion,
1462
1483
  ...this.opts.environment
1463
1484
  };
1464
- this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), et), this.timer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs), typeof window < "u" && window.addEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.addEventListener("visibilitychange", this.onVisibility), this.onVisibility());
1485
+ this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), tt), this.timer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs), typeof window < "u" && window.addEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.addEventListener("visibilitychange", this.onVisibility), this.onVisibility());
1465
1486
  }
1466
1487
  noteStateChange(e, t, n, r) {
1467
1488
  if (this.endedFlag || e === t) return;
1468
1489
  let i = this.now();
1469
- t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(H, i - this.bornMs)), this.firstAudioMs === 0 && !this.isMuted && (this.firstAudioMs = Math.max(H, i - this.bornMs)), this.isMuted && this.mutedSinceMs === null && (this.mutedSinceMs = i)) : this.playingSinceMs !== null && (this.playingTotalMs += i - this.playingSinceMs, this.playingSinceMs = null, this.mutedSinceMs !== null && (this.mutedTotalMs += i - this.mutedSinceMs, this.mutedSinceMs = null)), this.isPlaying = t === "playing", this.syncActive(), t === "waiting" ? this.waitingSinceMs = i : this.waitingSinceMs !== null && (this.waitingTotalMs += i - this.waitingSinceMs, this.waitingSinceMs = null), t === "buffering" && e === "playing" ? (this.isHidden || (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(H, Math.round(this.playingTotalMs)))) : e === "buffering" && (this.isBuffering = !1), this.syncBuffering(), t === "paused" && (this.pauseCount += 1);
1490
+ t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(W, i - this.bornMs)), this.firstAudioMs === 0 && !this.isMuted && (this.firstAudioMs = Math.max(W, i - this.bornMs)), this.isMuted && this.mutedSinceMs === null && (this.mutedSinceMs = i)) : this.playingSinceMs !== null && (this.playingTotalMs += i - this.playingSinceMs, this.playingSinceMs = null, this.mutedSinceMs !== null && (this.mutedTotalMs += i - this.mutedSinceMs, this.mutedSinceMs = null)), this.isPlaying = t === "playing", this.syncActive(), t === "waiting" ? this.waitingSinceMs = i : this.waitingSinceMs !== null && (this.waitingTotalMs += i - this.waitingSinceMs, this.waitingSinceMs = null), t === "buffering" && e === "playing" ? (this.isHidden || (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(W, Math.round(this.playingTotalMs)))) : e === "buffering" && (this.isBuffering = !1), this.syncBuffering(), t === "paused" && (this.pauseCount += 1);
1470
1491
  let a = {
1471
- fromState: G[e] ?? "PLAYER_STATE_UNSPECIFIED",
1472
- toState: G[t] ?? "PLAYER_STATE_UNSPECIFIED",
1473
- presentation: tt[this.presentation]
1492
+ fromState: q[e] ?? "PLAYER_STATE_UNSPECIFIED",
1493
+ toState: q[t] ?? "PLAYER_STATE_UNSPECIFIED",
1494
+ presentation: nt[this.presentation]
1474
1495
  }, o = this.causeOf(e, t, i);
1475
1496
  o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
1476
1497
  }
@@ -1509,16 +1530,16 @@ var at = class {
1509
1530
  }
1510
1531
  this.muteChanges += 1;
1511
1532
  let r = this.now();
1512
- n ? this.isPlaying && this.mutedSinceMs === null && (this.mutedSinceMs = r) : (this.mutedSinceMs !== null && (this.mutedTotalMs += r - this.mutedSinceMs, this.mutedSinceMs = null), this.firstAudioMs === 0 && this.isPlaying && (this.firstAudioMs = Math.max(H, r - this.bornMs))), this.isMuted = n, this.syncActive();
1533
+ n ? this.isPlaying && this.mutedSinceMs === null && (this.mutedSinceMs = r) : (this.mutedSinceMs !== null && (this.mutedTotalMs += r - this.mutedSinceMs, this.mutedSinceMs = null), this.firstAudioMs === 0 && this.isPlaying && (this.firstAudioMs = Math.max(W, r - this.bornMs))), this.isMuted = n, this.syncActive();
1513
1534
  }
1514
1535
  notePresentation(e) {
1515
1536
  this.endedFlag || e === this.presentation || (this.closePresentation(), this.presentation = e);
1516
1537
  }
1517
1538
  noteReady() {
1518
- this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(H, this.now() - this.bornMs));
1539
+ this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(W, this.now() - this.bornMs));
1519
1540
  }
1520
1541
  noteFirstBytes() {
1521
- this.sawFirstBytes = !0, this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(H, this.now() - this.bornMs));
1542
+ this.sawFirstBytes = !0, this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(W, this.now() - this.bornMs));
1522
1543
  }
1523
1544
  noteSeek() {
1524
1545
  this.endedFlag || (this.seekCount += 1, this.lastSeekMs = this.now());
@@ -1537,7 +1558,7 @@ var at = class {
1537
1558
  let r = n ?? "";
1538
1559
  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";
1539
1560
  if (e === "DRM_CLIENT") return "ERROR_STAGE_DRM";
1540
- if (rt.has(e)) return "ERROR_STAGE_RESOLVE";
1561
+ if (it.has(e)) return "ERROR_STAGE_RESOLVE";
1541
1562
  let i = t ?? "";
1542
1563
  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";
1543
1564
  }
@@ -1563,7 +1584,7 @@ var at = class {
1563
1584
  let s = e.droppedFrames ?? 0;
1564
1585
  s > this.lastDropped && (i.droppedFramesDelta = s - this.lastDropped), this.lastDropped = s;
1565
1586
  let c = e.renditionSwitches ?? 0;
1566
- c > this.lastSwitches && (i.renditionSwitchesDelta = c - this.lastSwitches), this.lastSwitches = c, n > this.lastHbPlayingMs && (i.playingMsDelta = Math.round(n - this.lastHbPlayingMs)), this.lastHbPlayingMs = n, r > this.lastHbHiddenMs && (i.hiddenMsDelta = Math.round(r - this.lastHbHiddenMs)), this.lastHbHiddenMs = r, typeof e.latencySeconds == "number" && (i.latencyMs = Math.round(e.latencySeconds * 1e3)), typeof e.targetLatencySeconds == "number" && (i.targetLatencyMs = Math.round(e.targetLatencySeconds * 1e3)), typeof e.bandwidthKbps == "number" && (i.bandwidthEstKbps = e.bandwidthKbps), typeof e.levelHeight == "number" && (i.renditionHeight = e.levelHeight), typeof e.renditionBitrateKbps == "number" && (i.renditionBitrateKbps = e.renditionBitrateKbps), e.atLiveEdge === !0 && (i.atLiveEdge = !0), typeof e.bufferedMs == "number" && e.bufferedMs > 0 && (i.bufferedMs = Math.round(e.bufferedMs)), i.playerState = G[e.state] ?? "PLAYER_STATE_UNSPECIFIED";
1587
+ c > this.lastSwitches && (i.renditionSwitchesDelta = c - this.lastSwitches), this.lastSwitches = c, n > this.lastHbPlayingMs && (i.playingMsDelta = Math.round(n - this.lastHbPlayingMs)), this.lastHbPlayingMs = n, r > this.lastHbHiddenMs && (i.hiddenMsDelta = Math.round(r - this.lastHbHiddenMs)), this.lastHbHiddenMs = r, typeof e.latencySeconds == "number" && (i.latencyMs = Math.round(e.latencySeconds * 1e3)), typeof e.targetLatencySeconds == "number" && (i.targetLatencyMs = Math.round(e.targetLatencySeconds * 1e3)), typeof e.bandwidthKbps == "number" && (i.bandwidthEstKbps = e.bandwidthKbps), typeof e.levelHeight == "number" && (i.renditionHeight = e.levelHeight), typeof e.renditionBitrateKbps == "number" && (i.renditionBitrateKbps = e.renditionBitrateKbps), e.atLiveEdge === !0 && (i.atLiveEdge = !0), typeof e.bufferedMs == "number" && e.bufferedMs > 0 && (i.bufferedMs = Math.round(e.bufferedMs)), i.playerState = q[e.state] ?? "PLAYER_STATE_UNSPECIFIED";
1567
1588
  let l = this.mutedTotalMs;
1568
1589
  this.mutedSinceMs !== null && (l += t - this.mutedSinceMs), l > this.lastHbMutedMs && (i.mutedMsDelta = Math.round(l - this.lastHbMutedMs)), this.lastHbMutedMs = l;
1569
1590
  let u = e.upshifts ?? 0;
@@ -1578,14 +1599,14 @@ var at = class {
1578
1599
  if (this.lastRequestCount = m, h > 0) {
1579
1600
  i.requestCountDelta = h;
1580
1601
  let t = e.requestTimesMs ?? [], n = t.slice(Math.max(0, t.length - h)).sort((e, t) => e - t);
1581
- n.length > 0 && (i.requestTimeP50Ms = W(n, 50), i.requestTimeP95Ms = W(n, 95));
1602
+ n.length > 0 && (i.requestTimeP50Ms = K(n, 50), i.requestTimeP95Ms = K(n, 95));
1582
1603
  }
1583
1604
  typeof e.latencySeconds == "number" && typeof e.targetLatencySeconds == "number" && (i.driftMs = Math.round((e.latencySeconds - e.targetLatencySeconds) * 1e3)), typeof e.playbackRate == "number" && e.playbackRate > 0 && (i.playbackRate = e.playbackRate), e.qualityPinned === !0 && (i.qualityPinned = !0), e.cdnHost && (i.cdnHost = e.cdnHost), this.maybeEmitMediaInfo(e), this.enqueue({ heartbeat: i }), this.flush(), a > 0 && this.noteFirstBytes();
1584
1605
  }
1585
1606
  maybeEmitMediaInfo(e) {
1586
1607
  if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
1587
1608
  let t = {};
1588
- e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = nt[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);
1609
+ e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = rt[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);
1589
1610
  let n = JSON.stringify(t);
1590
1611
  n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
1591
1612
  }
@@ -1605,7 +1626,7 @@ var at = class {
1605
1626
  let s = n?.positionMs ?? 0;
1606
1627
  s > 0 && (r.exitPositionMs = Math.round(s)), this.activeTotalMs > 0 && (r.activeWatchTimeMs = Math.round(this.activeTotalMs)), this.mutedTotalMs > 0 && (r.mutedTimeMs = Math.round(this.mutedTotalMs));
1607
1628
  let c = e !== "END_REASON_ENDED";
1608
- c && this.lastStallMs > 0 && t - this.lastStallMs <= U && (r.abandonAfterStallMs = Math.max(H, Math.round(t - this.lastStallMs))), c && this.lastErrorMs > 0 && t - this.lastErrorMs <= U && (r.abandonAfterErrorMs = Math.max(H, Math.round(t - this.lastErrorMs))), this.fullscreenTotalMs > 0 && (r.fullscreenTimeMs = Math.round(this.fullscreenTotalMs)), this.pipTotalMs > 0 && (r.pipTimeMs = Math.round(this.pipTotalMs)), this.volumeChanges > 0 && (r.volumeChanges = this.volumeChanges), this.muteChanges > 0 && (r.muteChanges = this.muteChanges);
1629
+ c && this.lastStallMs > 0 && t - this.lastStallMs <= G && (r.abandonAfterStallMs = Math.max(W, Math.round(t - this.lastStallMs))), c && this.lastErrorMs > 0 && t - this.lastErrorMs <= G && (r.abandonAfterErrorMs = Math.max(W, Math.round(t - this.lastErrorMs))), this.fullscreenTotalMs > 0 && (r.fullscreenTimeMs = Math.round(this.fullscreenTotalMs)), this.pipTotalMs > 0 && (r.pipTimeMs = Math.round(this.pipTotalMs)), this.volumeChanges > 0 && (r.volumeChanges = this.volumeChanges), this.muteChanges > 0 && (r.muteChanges = this.muteChanges);
1609
1630
  let l = n?.decodedFrames ?? this.lastDecoded;
1610
1631
  l > 0 && (r.decodedFramesTotal = l), this.startupTimeMs === 0 && (r.failedStage = this.lastErrorStage ?? (this.manifestLoadMs === 0 ? "ERROR_STAGE_MANIFEST" : this.firstFragLoadMs === 0 ? "ERROR_STAGE_FIRST_SEGMENT" : "ERROR_STAGE_OTHER")), this.pending.push(this.wrap({ sessionEnd: r })), this.flush(e === "END_REASON_PAGEHIDE");
1611
1632
  }
@@ -1625,7 +1646,7 @@ var at = class {
1625
1646
  };
1626
1647
  }
1627
1648
  enqueue(e) {
1628
- for (this.pending.push(this.wrap(e)); this.pending.length > $e;) {
1649
+ for (this.pending.push(this.wrap(e)); this.pending.length > et;) {
1629
1650
  let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
1630
1651
  this.pending.splice(e >= 0 ? e : 0, 1);
1631
1652
  }
@@ -1650,7 +1671,7 @@ var at = class {
1650
1671
  };
1651
1672
  //#endregion
1652
1673
  //#region src/core/environment.ts
1653
- function ot() {
1674
+ function st() {
1654
1675
  if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
1655
1676
  let e;
1656
1677
  try {
@@ -1675,14 +1696,14 @@ function ot() {
1675
1696
  integration: "INTEGRATION_KIND_IFRAME"
1676
1697
  };
1677
1698
  }
1678
- function st() {
1699
+ function ct() {
1679
1700
  if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
1680
1701
  let e = navigator.userAgentData, t = e?.platform ?? "";
1681
1702
  if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
1682
1703
  let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
1683
1704
  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";
1684
1705
  }
1685
- function ct() {
1706
+ function lt() {
1686
1707
  let e = typeof navigator < "u" ? navigator.connection : void 0;
1687
1708
  if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
1688
1709
  let t = { connectionType: e.type ? {
@@ -1692,17 +1713,17 @@ function ct() {
1692
1713
  }[e.type] ?? "CONNECTION_TYPE_OTHER" : "CONNECTION_TYPE_UNKNOWN" };
1693
1714
  return typeof e.downlink == "number" && e.downlink > 0 && (t.downlinkKbps = Math.round(e.downlink * 1e3)), e.saveData && (t.saveData = !0), t;
1694
1715
  }
1695
- function lt(e) {
1696
- let t = ot(), n = {
1697
- deviceClass: st(),
1716
+ function ut(e) {
1717
+ let t = st(), n = {
1718
+ deviceClass: ct(),
1698
1719
  integration: t.integration,
1699
- ...ct()
1720
+ ...lt()
1700
1721
  };
1701
1722
  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);
1702
1723
  let r = e.getBoundingClientRect?.();
1703
1724
  return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
1704
1725
  }
1705
- async function ut() {
1726
+ async function dt() {
1706
1727
  let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
1707
1728
  if (!e?.getHighEntropyValues) return {};
1708
1729
  try {
@@ -1714,7 +1735,7 @@ async function ut() {
1714
1735
  }
1715
1736
  //#endregion
1716
1737
  //#region src/core/version.ts
1717
- var dt = "2.2.0", K = {
1738
+ var ft = "2.2.1", J = {
1718
1739
  accent: "#E1B600",
1719
1740
  accentText: "#1B1C23",
1720
1741
  background: "#0e0f13",
@@ -1725,7 +1746,7 @@ var dt = "2.2.0", K = {
1725
1746
  border: "rgba(255, 255, 255, 0.1)",
1726
1747
  radius: "8px",
1727
1748
  fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
1728
- }, q = {
1749
+ }, Y = {
1729
1750
  accent: "--havik-accent",
1730
1751
  accentText: "--havik-accent-text",
1731
1752
  background: "--havik-bg",
@@ -1737,25 +1758,25 @@ var dt = "2.2.0", K = {
1737
1758
  radius: "--havik-radius",
1738
1759
  fontFamily: "--havik-font"
1739
1760
  };
1740
- function J(e, t) {
1761
+ function X(e, t) {
1741
1762
  let n = {
1742
- ...K,
1763
+ ...J,
1743
1764
  ...t
1744
1765
  };
1745
- for (let t of Object.keys(q)) e.style.setProperty(q[t], n[t]);
1766
+ for (let t of Object.keys(Y)) e.style.setProperty(Y[t], n[t]);
1746
1767
  n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
1747
1768
  }
1748
1769
  //#endregion
1749
1770
  //#region src/ui/styles.ts
1750
- var Y = "havik-player-styles", ft = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n.havik-spinner{width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n";
1751
- function pt(e = document) {
1752
- if (e.getElementById(Y)) return;
1771
+ var Z = "havik-player-styles", pt = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n.havik-spinner{width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n";
1772
+ function mt(e = document) {
1773
+ if (e.getElementById(Z)) return;
1753
1774
  let t = e.createElement("style");
1754
- t.id = Y, t.textContent = ft, e.head.appendChild(t);
1775
+ t.id = Z, t.textContent = pt, e.head.appendChild(t);
1755
1776
  }
1756
1777
  //#endregion
1757
1778
  //#region src/ui/icons.ts
1758
- var mt = {
1779
+ var ht = {
1759
1780
  play: "<path d=\"M8 5v14l11-7z\"/>",
1760
1781
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
1761
1782
  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\"/>",
@@ -1768,21 +1789,21 @@ var mt = {
1768
1789
  liveDot: "<circle cx=\"12\" cy=\"12\" r=\"6\"/>",
1769
1790
  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\"/>"
1770
1791
  };
1771
- function X(e) {
1772
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${mt[e]}</svg>`;
1792
+ function Q(e) {
1793
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${ht[e]}</svg>`;
1773
1794
  }
1774
1795
  //#endregion
1775
1796
  //#region src/ui/controls.ts
1776
- var ht = 3e3, gt = /* @__PURE__ */ new Set([
1797
+ var gt = 3e3, _t = /* @__PURE__ */ new Set([
1777
1798
  "GONE",
1778
1799
  "NOT_FOUND",
1779
1800
  "INVALID_URN",
1780
1801
  "UNAUTHORIZED"
1781
1802
  ]);
1782
- function _t(e) {
1783
- return new vt(e);
1803
+ function vt(e) {
1804
+ return new yt(e);
1784
1805
  }
1785
- var vt = class {
1806
+ var yt = class {
1786
1807
  root;
1787
1808
  video;
1788
1809
  player;
@@ -1824,7 +1845,7 @@ var vt = class {
1824
1845
  scrubbing = !1;
1825
1846
  offFns = [];
1826
1847
  constructor(e) {
1827
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? ht, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, pt(this.doc), this.root.classList.add("havik-player"), this.root.hasAttribute("tabindex") || (this.root.tabIndex = 0), this.root.setAttribute("role", "region"), this.root.setAttribute("aria-label", "Video player");
1848
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? gt, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, mt(this.doc), this.root.classList.add("havik-player"), this.root.hasAttribute("tabindex") || (this.root.tabIndex = 0), this.root.setAttribute("role", "region"), this.root.setAttribute("aria-label", "Video player");
1828
1849
  let t = (e, t, n) => {
1829
1850
  let r = this.doc.createElement(e);
1830
1851
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -1835,12 +1856,12 @@ var vt = class {
1835
1856
  "aria-label": r,
1836
1857
  title: r
1837
1858
  });
1838
- return i.innerHTML = X(n), i;
1859
+ return i.innerHTML = Q(n), i;
1839
1860
  };
1840
1861
  this.overlay = t("div", "havik-overlay"), this.center = t("button", "havik-center", {
1841
1862
  type: "button",
1842
1863
  "aria-label": "Play"
1843
- }), this.center.innerHTML = X("play"), this.spinner = t("div", "havik-spinner"), this.spinner.style.display = "none", this.overlay.append(this.center, this.spinner), this.gradient = t("div", "havik-gradient"), this.bar = t("div", "havik-bar"), this.seek = t("div", "havik-seek", {
1864
+ }), this.center.innerHTML = Q("play"), this.spinner = t("div", "havik-spinner"), this.spinner.style.display = "none", this.overlay.append(this.center, this.spinner), this.gradient = t("div", "havik-gradient"), this.bar = t("div", "havik-bar"), this.seek = t("div", "havik-seek", {
1844
1865
  role: "slider",
1845
1866
  "aria-label": "Seek",
1846
1867
  "aria-valuemin": "0",
@@ -1858,7 +1879,7 @@ var vt = class {
1858
1879
  "aria-label": "Volume"
1859
1880
  });
1860
1881
  let a = t("div", "havik-vol");
1861
- a.append(this.muteBtn, this.volSlider), this.live = t("div", "havik-live"), this.live.innerHTML = X("liveDot");
1882
+ a.append(this.muteBtn, this.volSlider), this.live = t("div", "havik-live"), this.live.innerHTML = Q("liveDot");
1862
1883
  let o = this.doc.createElement("span");
1863
1884
  o.textContent = "LIVE", this.liveGo = t("button", "havik-live__go", { type: "button" }), this.liveGo.textContent = "Go live", this.live.append(o, this.liveGo), this.time = t("div", "havik-time"), this.time.textContent = "0:00", this.time.style.display = "none";
1864
1885
  let s = t("div", "havik-spacer");
@@ -1912,12 +1933,12 @@ var vt = class {
1912
1933
  }
1913
1934
  if (e === "preplay") {
1914
1935
  let e = this.doc.createElement("button");
1915
- e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = X("play"), e.addEventListener("click", () => void this.player.play()), n.append(e);
1936
+ 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);
1916
1937
  } else {
1917
1938
  let r = this.doc.createElement("div");
1918
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !gt.has(this.lastError.code)) {
1939
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !_t.has(this.lastError.code)) {
1919
1940
  let e = this.doc.createElement("button");
1920
- e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${X("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), n.append(e);
1941
+ 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);
1921
1942
  }
1922
1943
  }
1923
1944
  this.root.append(n), this.statusBox = n, this.show();
@@ -1956,7 +1977,7 @@ var vt = class {
1956
1977
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
1957
1978
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
1958
1979
  }
1959
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Z(this.video.currentTime)} / ${Z(this.video.duration)}`);
1980
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${bt(this.video.currentTime)} / ${bt(this.video.duration)}`);
1960
1981
  }
1961
1982
  syncLive() {
1962
1983
  let e = this.player.getStats(), t = e.isLive !== !1;
@@ -2006,17 +2027,17 @@ var vt = class {
2006
2027
  renderQualityMenu() {
2007
2028
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
2008
2029
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
2009
- for (let n of e) this.menu.append(this.menuItem(yt(n), t === n.index, () => this.player.setQuality(n.index)));
2030
+ for (let n of e) this.menu.append(this.menuItem(xt(n), t === n.index, () => this.player.setQuality(n.index)));
2010
2031
  }
2011
2032
  renderAudioMenu() {
2012
2033
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
2013
2034
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
2014
- for (let t of e) this.menu.append(this.menuItem(bt(t), t.default, () => this.player.setAudioTrack(t.id)));
2035
+ for (let t of e) this.menu.append(this.menuItem(St(t), t.default, () => this.player.setAudioTrack(t.id)));
2015
2036
  }
2016
2037
  renderCcMenu() {
2017
2038
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
2018
2039
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
2019
- for (let t of e) this.menu.append(this.menuItem(xt(t), !1, () => this.player.setTextTrack(t.id)));
2040
+ for (let t of e) this.menu.append(this.menuItem(Ct(t), !1, () => this.player.setTextTrack(t.id)));
2020
2041
  }
2021
2042
  onTracksChanged() {
2022
2043
  this.openMenu && this.renderMenu(this.openMenu);
@@ -2099,7 +2120,7 @@ var vt = class {
2099
2120
  }
2100
2121
  }
2101
2122
  setIcon(e, t) {
2102
- e.innerHTML = X(t);
2123
+ e.innerHTML = Q(t);
2103
2124
  }
2104
2125
  destroy() {
2105
2126
  this.hideTimer && clearTimeout(this.hideTimer);
@@ -2116,32 +2137,32 @@ var vt = class {
2116
2137
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
2117
2138
  }
2118
2139
  };
2119
- function Z(e) {
2140
+ function bt(e) {
2120
2141
  (!Number.isFinite(e) || e < 0) && (e = 0);
2121
2142
  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");
2122
2143
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
2123
2144
  }
2124
- function yt(e) {
2145
+ function xt(e) {
2125
2146
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
2126
2147
  }
2127
- function bt(e) {
2148
+ function St(e) {
2128
2149
  return e.name || e.lang || `Track ${e.id}`;
2129
2150
  }
2130
- function xt(e) {
2151
+ function Ct(e) {
2131
2152
  return e.name || e.lang || `Track ${e.id}`;
2132
2153
  }
2133
2154
  //#endregion
2134
2155
  //#region src/managed/index.ts
2135
- var St = 6e3, Q = 45e3, Ct = 2e3, wt = 3, Tt = 2e3, Et = 48e4;
2136
- async function Dt(e) {
2156
+ var wt = 6e3, Tt = 45e3, Et = 2e3, Dt = 3, Ot = 2e3, kt = 48e4;
2157
+ async function At(e) {
2137
2158
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
2138
2159
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: baseUrl is required");
2139
2160
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
2140
2161
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
2141
- let t = new Ot(e);
2162
+ let t = new jt(e);
2142
2163
  return await t.start(), t;
2143
2164
  }
2144
- var Ot = class {
2165
+ var jt = class {
2145
2166
  opts;
2146
2167
  abort = new AbortController();
2147
2168
  listeners = {};
@@ -2228,7 +2249,7 @@ var Ot = class {
2228
2249
  setTimeout(() => {
2229
2250
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
2230
2251
  phase: i ? "unavailable" : "tooEarly",
2231
- retryInMs: Q,
2252
+ retryInMs: Tt,
2232
2253
  attempt: 1,
2233
2254
  elapsedMs: 0,
2234
2255
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -2267,13 +2288,13 @@ var Ot = class {
2267
2288
  !a && !p() && h();
2268
2289
  }, e);
2269
2290
  };
2270
- _(St), this.armingSub = E({
2291
+ _(wt), this.armingSub = E({
2271
2292
  eventsBaseUrl: t,
2272
2293
  matchUrn: this.opts.matchUrn,
2273
2294
  credential: this.opts.credential,
2274
- onAlive: () => _(Q),
2295
+ onAlive: () => _(Tt),
2275
2296
  onState: (e) => {
2276
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Ct);
2297
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Et);
2277
2298
  },
2278
2299
  onError: () => h()
2279
2300
  });
@@ -2296,7 +2317,7 @@ var Ot = class {
2296
2317
  let n = await s(this.opts.credential);
2297
2318
  if (this.destroyed || t !== this.loadGen) return;
2298
2319
  this.setState("loading");
2299
- let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = Oe(this.opts.video, (e) => {
2320
+ let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = ke(this.opts.video, (e) => {
2300
2321
  this._state !== "ended" && this._state !== "error" && this.setState(e);
2301
2322
  });
2302
2323
  if (await i.load(), this.destroyed || t !== this.loadGen) {
@@ -2315,17 +2336,17 @@ var Ot = class {
2315
2336
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
2316
2337
  let r = e.analytics?.sid;
2317
2338
  if (!r) return;
2318
- let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? it(this.opts.baseUrl);
2339
+ let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? at(this.opts.baseUrl);
2319
2340
  if (!i) return;
2320
- let a = this.opts.video, o = new at({
2341
+ let a = this.opts.video, o = new ot({
2321
2342
  endpoint: i,
2322
2343
  sid: r,
2323
2344
  apiKey: n,
2324
2345
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
2325
- sdkVersion: dt,
2326
- environment: lt(a),
2346
+ sdkVersion: ft,
2347
+ environment: ut(a),
2327
2348
  sample: () => {
2328
- let e = this.engine?.getStats() ?? F, t = a.buffered.length ? Math.max(0, a.buffered.end(a.buffered.length - 1) - a.currentTime) : 0;
2349
+ let e = this.engine?.getStats() ?? L, t = a.buffered.length ? Math.max(0, a.buffered.end(a.buffered.length - 1) - a.currentTime) : 0;
2329
2350
  return {
2330
2351
  ...e,
2331
2352
  bufferedMs: Math.round(t * 1e3),
@@ -2339,7 +2360,7 @@ var Ot = class {
2339
2360
  let s = () => o.noteSeek(), c = () => o.noteVolume(a.muted, a.volume);
2340
2361
  a.addEventListener("seeking", s), a.addEventListener("volumechange", c), this.detachMediaListeners = () => {
2341
2362
  a.removeEventListener("seeking", s), a.removeEventListener("volumechange", c);
2342
- }, o.start(), o.seedVolume(a.muted, a.volume), o.notePresentation(this.currentPresentation()), ut().then((e) => o.noteEnvironment(e)), this.opts.autoplay === !1 && o.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
2363
+ }, o.start(), o.seedVolume(a.muted, a.volume), o.notePresentation(this.currentPresentation()), dt().then((e) => o.noteEnvironment(e)), this.opts.autoplay === !1 && o.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
2343
2364
  }
2344
2365
  currentPresentation() {
2345
2366
  let e = this.opts.video;
@@ -2363,11 +2384,11 @@ var Ot = class {
2363
2384
  hlsConfig: i,
2364
2385
  emit: (e) => this.onEngineEvent(e)
2365
2386
  }, o = this.opts.engine ?? "auto";
2366
- if (o === "native") return new z(a);
2367
- if (o === "hls") return new V(a);
2368
- if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && L(this.opts.video) && R()) return new z(a);
2369
- if (je()) return new V(a);
2370
- if (L(this.opts.video)) return new z(a);
2387
+ if (o === "native") return new V(a);
2388
+ if (o === "hls") return new U(a);
2389
+ if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && z(this.opts.video) && B()) return new V(a);
2390
+ if (Me()) return new U(a);
2391
+ if (z(this.opts.video)) return new V(a);
2371
2392
  throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
2372
2393
  }
2373
2394
  onEngineEvent(e) {
@@ -2410,7 +2431,7 @@ var Ot = class {
2410
2431
  r = 0, i = !1;
2411
2432
  },
2412
2433
  onError: () => {
2413
- this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= wt && (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.`))));
2434
+ this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= Dt && (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.`))));
2414
2435
  }
2415
2436
  });
2416
2437
  }
@@ -2432,7 +2453,7 @@ var Ot = class {
2432
2453
  scheduleRejoin(e) {
2433
2454
  !this.rejoinEnabled() || this.rejoinTimer || (this.rejoinTimer = setTimeout(() => {
2434
2455
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
2435
- }, Math.random() * Ct));
2456
+ }, Math.random() * Et));
2436
2457
  }
2437
2458
  cancelRejoin() {
2438
2459
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -2456,7 +2477,7 @@ var Ot = class {
2456
2477
  }
2457
2478
  t.controls = !1;
2458
2479
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
2459
- r && r.insertBefore(n, t), n.appendChild(t), J(n, this.opts.theme), this.container = n, this.controlBar = _t({
2480
+ r && r.insertBefore(n, t), n.appendChild(t), X(n, this.opts.theme), this.container = n, this.controlBar = vt({
2460
2481
  root: n,
2461
2482
  video: t,
2462
2483
  player: this,
@@ -2486,7 +2507,7 @@ var Ot = class {
2486
2507
  }
2487
2508
  startStats() {
2488
2509
  this.stopStats();
2489
- let e = Math.max(500, this.opts.statsIntervalMs ?? Tt);
2510
+ let e = Math.max(500, this.opts.statsIntervalMs ?? Ot);
2490
2511
  this.statsTimer = setInterval(() => {
2491
2512
  if (!this.engine) return;
2492
2513
  let e = this.engine.getStats();
@@ -2498,7 +2519,7 @@ var Ot = class {
2498
2519
  }
2499
2520
  scheduleLicenseRefresh() {
2500
2521
  this.stopRefresh();
2501
- let e = this.opts.licenseRefreshMs ?? Et;
2522
+ let e = this.opts.licenseRefreshMs ?? kt;
2502
2523
  !e || !this._descriptor?.drmEnabled || (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
2503
2524
  }
2504
2525
  stopRefresh() {
@@ -2540,7 +2561,7 @@ var Ot = class {
2540
2561
  this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
2541
2562
  }
2542
2563
  getStats() {
2543
- return this.engine?.getStats() ?? F;
2564
+ return this.engine?.getStats() ?? L;
2544
2565
  }
2545
2566
  getQualityLevels() {
2546
2567
  return this.engine?.getQualityLevels() ?? [];
@@ -2650,7 +2671,7 @@ function $(e) {
2650
2671
  }
2651
2672
  //#endregion
2652
2673
  //#region src/embed/host.ts
2653
- function kt(e) {
2674
+ function Mt(e) {
2654
2675
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
2655
2676
  if (!e.src) throw Error("mountEmbed: opts.src is required");
2656
2677
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -2709,8 +2730,8 @@ function kt(e) {
2709
2730
  }
2710
2731
  //#endregion
2711
2732
  //#region src/embed/page.ts
2712
- var At = /^[A-Za-z0-9:_.-]{1,128}$/;
2713
- function jt() {
2733
+ var Nt = /^[A-Za-z0-9:_.-]{1,128}$/;
2734
+ function Pt() {
2714
2735
  let e = window.HAVIK_EMBED_CONFIG;
2715
2736
  if (e) return {
2716
2737
  baseUrl: e.baseUrl ?? "",
@@ -2732,9 +2753,9 @@ function jt() {
2732
2753
  waitForLive: !0
2733
2754
  };
2734
2755
  }
2735
- async function Mt() {
2736
- let e = jt();
2737
- Pt();
2756
+ async function Ft() {
2757
+ let e = Pt();
2758
+ Lt();
2738
2759
  let t = document.createElement("video");
2739
2760
  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);
2740
2761
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -2789,12 +2810,12 @@ async function Mt() {
2789
2810
  code: e.code,
2790
2811
  message: e.message
2791
2812
  })), e.on("autoplayblocked", () => {
2792
- i({ type: "oddin:autoplayblocked" }), Nt(t, e);
2813
+ i({ type: "oddin:autoplayblocked" }), It(t, e);
2793
2814
  });
2794
2815
  }, s = async (r) => {
2795
2816
  a?.destroy(), a = null;
2796
2817
  try {
2797
- let n = await Dt({
2818
+ let n = await At({
2798
2819
  video: t,
2799
2820
  baseUrl: e.baseUrl,
2800
2821
  matchUrn: r,
@@ -2855,7 +2876,7 @@ async function Mt() {
2855
2876
  a?.retry();
2856
2877
  break;
2857
2878
  case "oddin:load":
2858
- typeof e.matchUrn == "string" && At.test(e.matchUrn) && s(e.matchUrn);
2879
+ typeof e.matchUrn == "string" && Nt.test(e.matchUrn) && s(e.matchUrn);
2859
2880
  break;
2860
2881
  case "oddin:destroy": c();
2861
2882
  }
@@ -2867,18 +2888,18 @@ async function Mt() {
2867
2888
  protocol: 1
2868
2889
  });
2869
2890
  }
2870
- function Nt(e, t) {
2891
+ function It(e, t) {
2871
2892
  if (document.getElementById("havik-tap")) return;
2872
2893
  let n = document.createElement("button");
2873
2894
  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", () => {
2874
2895
  n.remove(), t.play();
2875
2896
  }), document.body.appendChild(n);
2876
2897
  }
2877
- function Pt() {
2898
+ function Lt() {
2878
2899
  let e = document.createElement("style");
2879
2900
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
2880
2901
  }
2881
2902
  //#endregion
2882
- export { K as ODDIN_THEME, n as PlaybackError, J as applyTheme, Mt as bootEmbed, ge as clearDeviceId, Dt as createPlayer, D as deriveEventsBaseUrl, Se as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ue as fetchCatalog, fe as fetchTournament, me as flattenMatches, A as getDeviceId, we as isLowLatencyManifest, $ as isOddinMessage, ve as licenseRequestHeaders, kt as mountEmbed, v as pollToLive, b as resolveStream, E as subscribeLiveState, C as videoFullscreenActive, he as watchStatus };
2903
+ export { J as ODDIN_THEME, n as PlaybackError, X as applyTheme, Ft as bootEmbed, ge as clearDeviceId, At as createPlayer, D as deriveEventsBaseUrl, Ee as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ue as fetchCatalog, fe as fetchTournament, me as flattenMatches, A as getDeviceId, Oe as isLowLatencyManifest, $ as isOddinMessage, ve as licenseRequestHeaders, Mt as mountEmbed, v as pollToLive, b as resolveStream, E as subscribeLiveState, C as videoFullscreenActive, he as watchStatus };
2883
2904
 
2884
2905
  //# sourceMappingURL=index.js.map