@oddin-gg/havik-player 2.0.4 → 2.1.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
@@ -244,32 +244,32 @@ function ce(e, t) {
244
244
  function S(e) {
245
245
  return e.ownerDocument ?? document;
246
246
  }
247
- function C(e) {
247
+ function le(e) {
248
248
  return e.fullscreenElement ?? e.webkitFullscreenElement ?? null;
249
249
  }
250
- function w(e, t) {
251
- let n = C(S(e));
250
+ function C(e, t) {
251
+ let n = le(S(e));
252
252
  return n != null && (n === e || t != null && n === t) || e.webkitDisplayingFullscreen === !0;
253
253
  }
254
- async function T(e, t) {
254
+ async function w(e, t) {
255
255
  let n = t ?? e;
256
256
  if (typeof n.requestFullscreen == "function") return await n.requestFullscreen(), !0;
257
257
  if (typeof n.webkitRequestFullscreen == "function") return n.webkitRequestFullscreen(), !0;
258
258
  let r = e;
259
259
  return r.webkitSupportsFullscreen && typeof r.webkitEnterFullscreen == "function" ? (r.webkitEnterFullscreen(), !0) : !1;
260
260
  }
261
- async function E(e, t) {
261
+ async function T(e, t) {
262
262
  let n = e;
263
263
  if (n.webkitDisplayingFullscreen) {
264
264
  n.webkitExitFullscreen?.();
265
265
  return;
266
266
  }
267
- let r = S(e), i = C(r);
267
+ let r = S(e), i = le(r);
268
268
  i != null && (i === e || t != null && i === t) && (typeof r.exitFullscreen == "function" ? await r.exitFullscreen() : r.webkitExitFullscreen?.());
269
269
  }
270
270
  //#endregion
271
271
  //#region src/core/catalog.ts
272
- async function le(e) {
272
+ async function ue(e) {
273
273
  let t = new URLSearchParams();
274
274
  e.status && t.set("status", Array.isArray(e.status) ? e.status.join(",") : e.status), e.sport && t.set("sport", e.sport);
275
275
  let i = t.toString(), a = c(e.baseUrl, "/v1/catalog" + (i ? `?${i}` : "")), o = new Headers();
@@ -287,12 +287,12 @@ async function le(e) {
287
287
  };
288
288
  if (!s.ok) throw new n(r(s.status), s.status, `catalog request failed (HTTP ${s.status})`);
289
289
  return {
290
- tournaments: ue(await s.json()),
290
+ tournaments: de(await s.json()),
291
291
  etag: s.headers.get("etag") ?? void 0,
292
292
  notModified: !1
293
293
  };
294
294
  }
295
- function ue(e) {
295
+ function de(e) {
296
296
  return (e.tournaments ?? []).map((e) => {
297
297
  let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
298
298
  return {
@@ -312,7 +312,7 @@ function ue(e) {
312
312
  };
313
313
  });
314
314
  }
315
- async function de(e) {
315
+ async function fe(e) {
316
316
  let t = c(e.baseUrl, `/v1/tournaments/${encodeURIComponent(e.urn)}`), i = new Headers();
317
317
  e.etag && i.set("If-None-Match", e.etag);
318
318
  let a = await u(t, {
@@ -332,13 +332,13 @@ async function de(e) {
332
332
  };
333
333
  if (!a.ok) throw new n(r(a.status), a.status, `tournament request failed (HTTP ${a.status})`);
334
334
  return {
335
- tournament: fe(await a.json()),
335
+ tournament: pe(await a.json()),
336
336
  etag: a.headers.get("etag") ?? void 0,
337
337
  notModified: !1,
338
338
  notFound: !1
339
339
  };
340
340
  }
341
- function fe(e) {
341
+ function pe(e) {
342
342
  let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
343
343
  return {
344
344
  urn: t,
@@ -356,12 +356,12 @@ function fe(e) {
356
356
  }))
357
357
  };
358
358
  }
359
- function pe(e) {
359
+ function me(e) {
360
360
  return e.tournaments.flatMap((e) => e.matches);
361
361
  }
362
362
  //#endregion
363
363
  //#region src/core/poll.ts
364
- function me(e) {
364
+ function he(e) {
365
365
  let t = Math.max(2e3, e.intervalMs ?? 1e4), i = new AbortController(), a, o, s, l = !1, d = () => {
366
366
  l || (a = setTimeout(f, t));
367
367
  }, f = async () => {
@@ -397,7 +397,7 @@ function me(e) {
397
397
  }
398
398
  //#endregion
399
399
  //#region src/core/events.ts
400
- function D(e) {
400
+ function E(e) {
401
401
  let t = Math.max(250, e.minBackoffMs ?? 1e3), n = Math.max(t, e.maxBackoffMs ?? 15e3), r = new AbortController(), i = !1, a = (e) => new Promise((t) => {
402
402
  if (r.signal.aborted) return t();
403
403
  let n = setTimeout(t, e);
@@ -464,7 +464,7 @@ function D(e) {
464
464
  i || (i = !0, r.abort());
465
465
  } };
466
466
  }
467
- function O(e) {
467
+ function D(e) {
468
468
  try {
469
469
  let t = new URL(e);
470
470
  return t.hostname = `events.${t.hostname}`, t.pathname = "", t.search = "", t.origin;
@@ -474,27 +474,27 @@ function O(e) {
474
474
  }
475
475
  //#endregion
476
476
  //#region src/core/drm/deviceId.ts
477
- var k = "havik.player.deviceId", A;
478
- function j() {
479
- if (A) return A;
477
+ var O = "havik.player.deviceId", k;
478
+ function A() {
479
+ if (k) return k;
480
480
  try {
481
- let e = globalThis.localStorage?.getItem(k);
482
- if (e) return A = e, e;
481
+ let e = globalThis.localStorage?.getItem(O);
482
+ if (e) return k = e, e;
483
483
  } catch {}
484
- let e = ge();
485
- A = e;
484
+ let e = _e();
485
+ k = e;
486
486
  try {
487
- globalThis.localStorage?.setItem(k, e);
487
+ globalThis.localStorage?.setItem(O, e);
488
488
  } catch {}
489
489
  return e;
490
490
  }
491
- function he() {
492
- A = void 0;
491
+ function ge() {
492
+ k = void 0;
493
493
  try {
494
- globalThis.localStorage?.removeItem(k);
494
+ globalThis.localStorage?.removeItem(O);
495
495
  } catch {}
496
496
  }
497
- function ge() {
497
+ function _e() {
498
498
  let e = globalThis.crypto;
499
499
  if (e && typeof e.randomUUID == "function") return e.randomUUID();
500
500
  let t = /* @__PURE__ */ new Uint8Array(16);
@@ -506,22 +506,22 @@ function ge() {
506
506
  }
507
507
  //#endregion
508
508
  //#region src/core/drm/headers.ts
509
- function _e(e, t, n = {}) {
509
+ function ve(e, t, n = {}) {
510
510
  let r = {
511
511
  "x-api-key": t.apiKey,
512
512
  "X-Match-Urn": e.matchUrn,
513
- "X-Device-Id": n.deviceId ?? j(),
513
+ "X-Device-Id": n.deviceId ?? A(),
514
514
  "Content-Type": "application/octet-stream"
515
515
  };
516
516
  return n.userId && (r["X-User-Id"] = n.userId), r;
517
517
  }
518
518
  //#endregion
519
519
  //#region src/core/drm/support.ts
520
- var ve = 5e3, M = "video/mp4; codecs=\"avc1.42E01E\"", N = "audio/mp4; codecs=\"mp4a.40.2\"", ye = {
520
+ var ye = 5e3, j = "video/mp4; codecs=\"avc1.42E01E\"", M = "audio/mp4; codecs=\"mp4a.40.2\"", be = {
521
521
  widevine: ["com.widevine.alpha"],
522
522
  fairplay: ["com.apple.fps"]
523
523
  };
524
- function be(e) {
524
+ function xe(e) {
525
525
  return e === "fairplay" ? [{
526
526
  initDataTypes: [
527
527
  "sinf",
@@ -529,30 +529,30 @@ function be(e) {
529
529
  "cenc"
530
530
  ],
531
531
  videoCapabilities: [{
532
- contentType: M,
532
+ contentType: j,
533
533
  robustness: "",
534
534
  encryptionScheme: "cbcs"
535
535
  }],
536
536
  audioCapabilities: [{
537
- contentType: N,
537
+ contentType: M,
538
538
  robustness: "",
539
539
  encryptionScheme: "cbcs"
540
540
  }]
541
541
  }] : [{
542
542
  initDataTypes: ["cenc"],
543
543
  videoCapabilities: [{
544
- contentType: M,
544
+ contentType: j,
545
545
  robustness: "SW_SECURE_DECODE",
546
546
  encryptionScheme: "cbcs"
547
547
  }],
548
548
  audioCapabilities: [{
549
- contentType: N,
549
+ contentType: M,
550
550
  robustness: "SW_SECURE_CRYPTO",
551
551
  encryptionScheme: "cbcs"
552
552
  }]
553
553
  }];
554
554
  }
555
- function P(e, t) {
555
+ function N(e, t) {
556
556
  return new Promise((n, r) => {
557
557
  let i = setTimeout(() => r(/* @__PURE__ */ Error("probe-timeout")), t);
558
558
  e.then((e) => {
@@ -562,8 +562,8 @@ function P(e, t) {
562
562
  });
563
563
  });
564
564
  }
565
- async function xe(e = {}) {
566
- let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? ve, r = {};
565
+ async function Se(e = {}) {
566
+ let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? ye, r = {};
567
567
  if (typeof window < "u" && window.isSecureContext === !1) return {
568
568
  verdict: "insecure-context",
569
569
  detail: r
@@ -575,7 +575,7 @@ async function xe(e = {}) {
575
575
  };
576
576
  let a = !1, o = !1, s = Date.now() + n, c = () => Math.max(1, s - Date.now());
577
577
  for (let e of t) {
578
- let t = ye[e];
578
+ let t = be[e];
579
579
  if (!t) {
580
580
  r[e] = "unknown system";
581
581
  continue;
@@ -586,7 +586,7 @@ async function xe(e = {}) {
586
586
  break;
587
587
  }
588
588
  try {
589
- return await P((await P(i(n, be(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
589
+ return await N((await N(i(n, xe(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
590
590
  verdict: "supported",
591
591
  system: e,
592
592
  detail: r
@@ -610,17 +610,44 @@ async function xe(e = {}) {
610
610
  }
611
611
  //#endregion
612
612
  //#region src/core/manifest.ts
613
- var Se = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
614
- function Ce(e) {
615
- return Se.test(e);
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)"));
616
639
  }
617
640
  //#endregion
618
641
  //#region src/core/stats.ts
619
642
  var F = { droppedFrames: 0 };
620
643
  function I(e) {
621
- return e.getVideoPlaybackQuality?.()?.droppedVideoFrames ?? 0;
644
+ let t = e.getVideoPlaybackQuality?.();
645
+ return {
646
+ dropped: t?.droppedVideoFrames ?? 0,
647
+ decoded: t?.totalVideoFrames ?? 0
648
+ };
622
649
  }
623
- function we(e, t) {
650
+ function Oe(e, t) {
624
651
  let n = [
625
652
  ["playing", () => t("playing")],
626
653
  ["waiting", () => t("buffering")],
@@ -637,7 +664,7 @@ function we(e, t) {
637
664
  }
638
665
  //#endregion
639
666
  //#region src/core/engine/native.ts
640
- function Te(e) {
667
+ function ke(e) {
641
668
  try {
642
669
  let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
643
670
  return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
@@ -687,7 +714,7 @@ var z = class {
687
714
  type: "error",
688
715
  error: new n("INTERNAL", 0, "native HLS playback error"),
689
716
  fatal: !0
690
- }), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = Ee(t.manifestUrl, t.analytics?.sid), e.load();
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();
691
718
  }
692
719
  startLoad() {
693
720
  this.loadStarted || (this.loadStarted = !0, this.restorePreload());
@@ -709,7 +736,7 @@ var z = class {
709
736
  });
710
737
  this.onEncrypted = (n) => {
711
738
  let i = n;
712
- !i.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV = Te(i.initData), (async () => {
739
+ !i.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV = ke(i.initData), (async () => {
713
740
  try {
714
741
  let n = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
715
742
  initDataTypes: [i.initDataType],
@@ -804,8 +831,11 @@ var z = class {
804
831
  e.seekable.length > 0 && (e.currentTime = e.seekable.end(e.seekable.length - 1));
805
832
  }
806
833
  getStats() {
807
- let e = this.deps.video, t = { droppedFrames: I(e) };
808
- return e.duration === Infinity && (t.isLive = !0, e.seekable.length > 0 && (t.atLiveEdge = e.currentTime >= e.seekable.end(e.seekable.length - 1) - 2)), t;
834
+ let e = this.deps.video, t = I(e), n = {
835
+ droppedFrames: t.dropped,
836
+ decodedFrames: t.decoded
837
+ };
838
+ return this.deps.descriptor.drmEnabled && (n.keySystem = "fairplay"), e.duration === Infinity && (n.isLive = !0, e.seekable.length > 0 && (n.atLiveEdge = e.currentTime >= e.seekable.end(e.seekable.length - 1) - 2)), n;
809
839
  }
810
840
  stop() {
811
841
  try {
@@ -824,25 +854,32 @@ var z = class {
824
854
  } catch {}
825
855
  }
826
856
  };
827
- function Ee(e, t) {
857
+ function Ae(e, t) {
828
858
  return t ? `${e}${e.includes("?") ? "&" : "?"}CMCD=${encodeURIComponent(`sid="${t}"`)}` : e;
829
859
  }
830
860
  //#endregion
831
861
  //#region src/core/engine/hls.ts
832
- function De() {
862
+ function je() {
833
863
  return e.isSupported();
834
864
  }
835
- var Oe = 3, ke = 5, Ae = 3, je = 3e4, Me = 12e3, Ne = 3e3, B = 6e3, Pe = 4e3, Fe = 2, Ie = 5, Le = 2, Re = 2, ze = 2e3, Be = 3e4, Ve = 2, He = 9, Ue = .5;
836
- function We(e) {
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) {
867
+ try {
868
+ return new URL(e).host;
869
+ } catch {
870
+ return "";
871
+ }
872
+ }
873
+ function Xe(e) {
837
874
  if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
838
875
  delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
839
876
  return;
840
877
  }
841
- e.liveSyncDuration ??= Ve;
842
- let t = Math.max(12, e.liveSyncDuration + He);
843
- (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Ge(e, e.liveSyncDuration);
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);
844
881
  }
845
- function Ge(e, t) {
882
+ function Ze(e, t) {
846
883
  let n = (e, t, n) => Math.min(n, Math.max(t, e));
847
884
  e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
848
885
  }
@@ -856,6 +893,16 @@ var V = class {
856
893
  playlistBytesLoaded = 0;
857
894
  renditionSwitches = 0;
858
895
  licenseTimeMs = 0;
896
+ licenseRequested = !1;
897
+ upshifts = 0;
898
+ downshifts = 0;
899
+ lastLevelIndex = -1;
900
+ requestCount = 0;
901
+ requestErrors = 0;
902
+ requestTimesMs = [];
903
+ cdnHost = "";
904
+ ladderHeights;
905
+ ladderTopBitrateKbps = 0;
859
906
  mediaRecoveries = 0;
860
907
  networkRecoveries = 0;
861
908
  licenseRecoveries = 0;
@@ -930,7 +977,7 @@ var V = class {
930
977
  let t = e.liveSyncPosition;
931
978
  if (typeof t != "number" || !Number.isFinite(t)) return;
932
979
  let { video: n } = this.deps;
933
- n.currentTime <= t + Ue || (n.currentTime = t);
980
+ n.currentTime <= t + qe || (n.currentTime = t);
934
981
  }
935
982
  startHls() {
936
983
  let { video: t, descriptor: n, credential: r, deviceId: i, userId: a, lowLatency: o, debug: s, hlsConfig: c, emit: l } = this.deps;
@@ -954,8 +1001,9 @@ var V = class {
954
1001
  videoEncryptionScheme: "cbcs",
955
1002
  audioEncryptionScheme: "cbcs"
956
1003
  },
1004
+ ...P() ? { requestMediaKeySystemAccessFunc: De(t) } : {},
957
1005
  licenseXhrSetup: (e, t) => {
958
- 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);
1006
+ 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;
959
1007
  let o = performance.now();
960
1008
  e.addEventListener("loadend", () => {
961
1009
  this.licenseTimeMs = Math.max(1, Math.round(performance.now() - o));
@@ -969,19 +1017,28 @@ var V = class {
969
1017
  } } : {},
970
1018
  ...c
971
1019
  };
972
- We(d), this.sizeCapManaged = d.capLevelToPlayerSize !== !0, this.loadStarted = d.autoStartLoad !== !1;
1020
+ Xe(d), this.sizeCapManaged = d.capLevelToPlayerSize !== !0, this.loadStarted = d.autoStartLoad !== !1;
973
1021
  let f = new e(d);
974
1022
  this.hls = f, f.on(e.Events.MANIFEST_PARSED, () => {
975
- this.mediaRecoveries = 0, this.networkRecoveries = 0, this.applySizeCap(), l({ type: "ready" });
1023
+ this.mediaRecoveries = 0, this.networkRecoveries = 0;
1024
+ let e = f.levels.map((e) => e.height).filter((e) => !!e);
1025
+ this.ladderHeights = e.length ? [...new Set(e)].sort((e, t) => e - t) : void 0, this.ladderTopBitrateKbps = f.levels.length ? Math.round(Math.max(...f.levels.map((e) => e.bitrate)) / 1e3) : 0, this.applySizeCap(), l({ type: "ready" });
976
1026
  }), f.on(e.Events.FRAG_BUFFERED, (e, t) => {
977
1027
  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(() => {
978
1028
  this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.starvationRebuilds = 0, this.recoveryResetTimer = null;
979
- }, je)));
1029
+ }, Fe)));
980
1030
  }), f.on(e.Events.FRAG_LOADING, (e, t) => {
981
1031
  t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
982
1032
  }), f.on(e.Events.FRAG_LOADED, (e, t) => {
983
- let n = t?.frag?.stats?.loaded;
984
- typeof n == "number" && (this.bytesLoaded += n);
1033
+ let n = t?.frag?.stats, r = n?.loaded;
1034
+ typeof r == "number" && (this.bytesLoaded += r);
1035
+ 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());
1037
+ let a = t?.frag?.url;
1038
+ if (a) {
1039
+ let e = Ye(a);
1040
+ e && (this.cdnHost = e);
1041
+ }
985
1042
  }), f.on(e.Events.LEVEL_LOADED, (e, t) => {
986
1043
  let n = t?.stats?.loaded;
987
1044
  if (typeof n == "number" && (this.playlistBytesLoaded += n), this.detectedLowLatency) return;
@@ -992,8 +1049,10 @@ var V = class {
992
1049
  }));
993
1050
  }), f.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.endWatchdog && clearInterval(this.endWatchdog), this.endWatchdog = setInterval(() => {
994
1051
  this.checkEndStale(), this.checkStarvation();
995
- }, Ne), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
996
- this.renditionSwitches += 1, l({
1052
+ }, Le), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1053
+ this.renditionSwitches += 1;
1054
+ let n = this.lastLevelIndex >= 0 ? f.levels[this.lastLevelIndex] : void 0, r = f.levels[t.level];
1055
+ n && r && r.bitrate !== n.bitrate && (r.bitrate > n.bitrate ? this.upshifts += 1 : this.downshifts += 1), this.lastLevelIndex = t.level, l({
997
1056
  type: "qualitychange",
998
1057
  index: t.level
999
1058
  });
@@ -1054,7 +1113,7 @@ var V = class {
1054
1113
  return;
1055
1114
  }
1056
1115
  this.capTimer && clearTimeout(this.capTimer);
1057
- let t = e > this.sizeCapLevel ? ze : Be;
1116
+ let t = e > this.sizeCapLevel ? Ue : We;
1058
1117
  this.capTimer = setTimeout(() => {
1059
1118
  this.capTimer = null, this.computeSizeCapTarget() === e ? this.applySizeCap() : this.onMediaResize();
1060
1119
  }, t);
@@ -1101,20 +1160,28 @@ var V = class {
1101
1160
  e.widevine && (this.licenseUrl = e.widevine);
1102
1161
  }
1103
1162
  getStats() {
1104
- let e = this.hls, t = {
1105
- droppedFrames: I(this.deps.video),
1163
+ let e = this.hls, t = I(this.deps.video), n = {
1164
+ droppedFrames: t.dropped,
1165
+ decodedFrames: t.decoded,
1106
1166
  lowLatency: this.detectedLowLatency,
1107
1167
  bytesLoaded: this.bytesLoaded,
1108
1168
  playlistBytesLoaded: this.playlistBytesLoaded,
1109
1169
  renditionSwitches: this.renditionSwitches,
1110
- licenseTimeMs: this.licenseTimeMs
1170
+ licenseTimeMs: this.licenseTimeMs,
1171
+ upshifts: this.upshifts,
1172
+ downshifts: this.downshifts,
1173
+ requestCount: this.requestCount,
1174
+ requestErrors: this.requestErrors,
1175
+ requestTimesMs: [...this.requestTimesMs],
1176
+ ladderHeights: this.ladderHeights,
1177
+ ladderTopBitrateKbps: this.ladderTopBitrateKbps || void 0
1111
1178
  };
1112
- if (e) {
1113
- typeof e.latency == "number" && (t.latencySeconds = e.latency), typeof e.targetLatency == "number" && e.targetLatency > 0 && (t.targetLatencySeconds = e.targetLatency), typeof e.bandwidthEstimate == "number" && (t.bandwidthKbps = Math.round(e.bandwidthEstimate / 1e3));
1114
- let n = e.currentLevel >= 0 ? e.levels[e.currentLevel] : void 0;
1115
- n && (t.levelHeight = n.height), n && typeof n.bitrate == "number" && (t.renditionBitrateKbps = Math.round(n.bitrate / 1e3)), typeof n?.details?.live == "boolean" && (t.isLive = n.details.live, typeof e.liveSyncPosition == "number" && (t.atLiveEdge = this.deps.video.currentTime >= e.liveSyncPosition - 1.5));
1179
+ if (this.cdnHost && (n.cdnHost = this.cdnHost), this.licenseRequested && (n.keySystem = "widevine", n.drmSecurityLevel = "L3"), e) {
1180
+ 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));
1181
+ let t = e.currentLevel >= 0 ? e.levels[e.currentLevel] : void 0;
1182
+ 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));
1116
1183
  }
1117
- return t;
1184
+ return n;
1118
1185
  }
1119
1186
  trackEnd(e) {
1120
1187
  let t = e.details;
@@ -1135,7 +1202,7 @@ var V = class {
1135
1202
  return;
1136
1203
  }
1137
1204
  let e = Date.now() - this.lastAdvanceMs;
1138
- !this.chaseSuspended && e > B && this.suspendChasing(), e > Me && this.emitEnded("stalled");
1205
+ !this.chaseSuspended && e > B && this.suspendChasing(), e > Ie && this.emitEnded("stalled");
1139
1206
  }
1140
1207
  checkStarvation() {
1141
1208
  let e = this.hls, { video: t } = this.deps;
@@ -1144,17 +1211,17 @@ var V = class {
1144
1211
  this.starvedTicks = 0;
1145
1212
  return;
1146
1213
  }
1147
- let n = this.lastAdvanceMs !== 0 && Date.now() - this.lastAdvanceMs < B, r = this.lastFragLoadingMs !== 0 && Date.now() - this.lastFragLoadingMs > Pe;
1214
+ let n = this.lastAdvanceMs !== 0 && Date.now() - this.lastAdvanceMs < B, r = this.lastFragLoadingMs !== 0 && Date.now() - this.lastFragLoadingMs > Re;
1148
1215
  if (!(t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused && n && r)) {
1149
1216
  this.starvedTicks = 0;
1150
1217
  return;
1151
1218
  }
1152
- if (this.kickPending && (this.kickTicks += 1), !(++this.starvedTicks < Fe)) {
1153
- if (this.starvedTicks = 0, this.kickPending && this.kickTicks >= Le && (this.kickPending = !1, this.kickTicks = 0, this.starvationRebuilds < Re)) {
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)) {
1154
1221
  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");
1155
1222
  return;
1156
1223
  }
1157
- if (!(this.starvationKicks >= Ie)) {
1224
+ if (!(this.starvationKicks >= Be)) {
1158
1225
  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");
1159
1226
  try {
1160
1227
  e.stopLoad(), e.startLoad(-1);
@@ -1204,7 +1271,7 @@ var V = class {
1204
1271
  scheduleLicenseRetry(t) {
1205
1272
  if (t.details !== e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED && t.details !== e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED) return !1;
1206
1273
  let n = t.response?.code ?? 0;
1207
- if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Ae) return !1;
1274
+ if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Pe) return !1;
1208
1275
  this.licenseRecoveries += 1;
1209
1276
  let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
1210
1277
  return this.licenseRetryTimer = setTimeout(() => {
@@ -1222,7 +1289,7 @@ var V = class {
1222
1289
  }
1223
1290
  }
1224
1291
  onError(t) {
1225
- if (!t.fatal) return;
1292
+ if (t.type === e.ErrorTypes.NETWORK_ERROR && String(t.details ?? "").startsWith("frag") && (this.requestErrors += 1), !t.fatal) return;
1226
1293
  let n = this.hls;
1227
1294
  if (n) {
1228
1295
  if (this.clearRecoveryReset(), t.type === e.ErrorTypes.KEY_SYSTEM_ERROR) {
@@ -1230,12 +1297,13 @@ var V = class {
1230
1297
  this.deps.emit({
1231
1298
  type: "error",
1232
1299
  error: this.toError(t, "DRM"),
1233
- fatal: !0
1300
+ fatal: !0,
1301
+ detail: t.details
1234
1302
  });
1235
1303
  return;
1236
1304
  }
1237
1305
  if (t.type === e.ErrorTypes.NETWORK_ERROR) {
1238
- if (this.networkRecoveries < ke) {
1306
+ if (this.networkRecoveries < Ne) {
1239
1307
  this.networkRecoveries += 1;
1240
1308
  let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
1241
1309
  this.networkRetryTimer = setTimeout(() => {
@@ -1246,26 +1314,29 @@ var V = class {
1246
1314
  this.deps.emit({
1247
1315
  type: "error",
1248
1316
  error: this.toError(t, "NETWORK"),
1249
- fatal: !0
1317
+ fatal: !0,
1318
+ detail: t.details
1250
1319
  });
1251
1320
  return;
1252
1321
  }
1253
1322
  if (t.type === e.ErrorTypes.MEDIA_ERROR) {
1254
- if (this.mediaRecoveries < Oe) {
1323
+ if (this.mediaRecoveries < Me) {
1255
1324
  this.mediaRecoveries += 1, n.recoverMediaError();
1256
1325
  return;
1257
1326
  }
1258
1327
  this.deps.emit({
1259
1328
  type: "error",
1260
1329
  error: this.toError(t),
1261
- fatal: !0
1330
+ fatal: !0,
1331
+ detail: t.details
1262
1332
  });
1263
1333
  return;
1264
1334
  }
1265
1335
  this.deps.emit({
1266
1336
  type: "error",
1267
1337
  error: this.toError(t),
1268
- fatal: !0
1338
+ fatal: !0,
1339
+ detail: t.details
1269
1340
  });
1270
1341
  }
1271
1342
  }
@@ -1273,7 +1344,29 @@ var V = class {
1273
1344
  let r = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
1274
1345
  return new n(t === "DRM" ? i(r) : r === 401 ? "UNAUTHORIZED" : r === 403 ? "FORBIDDEN" : "INTERNAL", r, `${t} error: ${a}${o ? ` (${o})` : ""}`);
1275
1346
  }
1276
- }, Ke = 15e3, qe = 100, H = 1, U = {
1347
+ }, Qe = 15e3, $e = 100, H = 1, et = 1e3, U = 3e4, tt = {
1348
+ inline: "PRESENTATION_INLINE",
1349
+ fullscreen: "PRESENTATION_FULLSCREEN",
1350
+ pip: "PRESENTATION_PIP"
1351
+ }, nt = {
1352
+ widevine: "KEY_SYSTEM_WIDEVINE",
1353
+ fairplay: "KEY_SYSTEM_FAIRPLAY",
1354
+ none: "KEY_SYSTEM_NONE"
1355
+ };
1356
+ function W(e, t) {
1357
+ return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
1358
+ }
1359
+ var rt = /* @__PURE__ */ new Set([
1360
+ "INVALID_URN",
1361
+ "NOT_FOUND",
1362
+ "GONE",
1363
+ "TOO_EARLY",
1364
+ "UNAVAILABLE",
1365
+ "UNAUTHORIZED",
1366
+ "FORBIDDEN",
1367
+ "RATE_LIMITED",
1368
+ "TIMEOUT"
1369
+ ]), G = {
1277
1370
  idle: "PLAYER_STATE_IDLE",
1278
1371
  waiting: "PLAYER_STATE_WAITING",
1279
1372
  loading: "PLAYER_STATE_LOADING",
@@ -1283,7 +1376,7 @@ var V = class {
1283
1376
  ended: "PLAYER_STATE_ENDED",
1284
1377
  error: "PLAYER_STATE_ERROR"
1285
1378
  };
1286
- function Je(e) {
1379
+ function it(e) {
1287
1380
  try {
1288
1381
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
1289
1382
  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);
@@ -1291,7 +1384,7 @@ function Je(e) {
1291
1384
  return;
1292
1385
  }
1293
1386
  }
1294
- var Ye = class {
1387
+ var at = class {
1295
1388
  opts;
1296
1389
  url;
1297
1390
  now;
@@ -1315,50 +1408,133 @@ var Ye = class {
1315
1408
  errorCount = 0;
1316
1409
  seekCount = 0;
1317
1410
  pauseCount = 0;
1411
+ isPlaying = !1;
1412
+ isHidden = !1;
1413
+ isMuted = !1;
1414
+ activeSinceMs = null;
1415
+ activeTotalMs = 0;
1416
+ mutedSinceMs = null;
1417
+ mutedTotalMs = 0;
1418
+ lastHbMutedMs = 0;
1419
+ volumeChanges = 0;
1420
+ muteChanges = 0;
1421
+ presentation = "inline";
1422
+ presentationSinceMs;
1423
+ fullscreenTotalMs = 0;
1424
+ pipTotalMs = 0;
1425
+ waitingSinceMs = null;
1426
+ waitingTotalMs = 0;
1427
+ firstAudioMs = 0;
1428
+ timeToFirstStallMs = 0;
1429
+ lastStallMs = 0;
1430
+ lastErrorMs = 0;
1431
+ lastSeekMs = 0;
1432
+ lastErrorStage = null;
1433
+ sawFirstBytes = !1;
1434
+ mediaInfoSignature = "";
1435
+ lastUpshifts = 0;
1436
+ lastDownshifts = 0;
1437
+ lastDecoded = 0;
1438
+ lastRequestCount = 0;
1439
+ lastRequestErrors = 0;
1440
+ pendingStart = null;
1441
+ firstFlushTimer = null;
1318
1442
  bornMs;
1319
1443
  startupTimeMs = 0;
1320
1444
  manifestLoadMs = 0;
1321
1445
  firstFragLoadMs = 0;
1322
1446
  onPagehide = () => this.end("END_REASON_PAGEHIDE");
1323
1447
  onVisibility = () => {
1324
- typeof document > "u" || (document.visibilityState === "hidden" ? this.hiddenSinceMs === null && (this.hiddenSinceMs = this.now()) : this.hiddenSinceMs !== null && (this.hiddenTotalMs += this.now() - this.hiddenSinceMs, this.hiddenSinceMs = null));
1448
+ typeof document > "u" || (document.visibilityState === "hidden" ? (this.hiddenSinceMs === null && (this.hiddenSinceMs = this.now()), this.isHidden = !0) : (this.hiddenSinceMs !== null && (this.hiddenTotalMs += this.now() - this.hiddenSinceMs, this.hiddenSinceMs = null), this.isHidden = !1), this.syncActive());
1325
1449
  };
1326
1450
  constructor(e) {
1327
1451
  this.opts = {
1328
1452
  ...e,
1329
- heartbeatMs: e.heartbeatMs ?? Ke
1330
- }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`, this.now = e.now ?? (() => Date.now()), this.bornMs = this.now();
1453
+ heartbeatMs: e.heartbeatMs ?? Qe
1454
+ }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`, this.now = e.now ?? (() => Date.now()), this.bornMs = this.now(), this.presentationSinceMs = this.bornMs;
1331
1455
  }
1332
1456
  start() {
1333
- this.endedFlag || (this.enqueue({ sessionStart: {
1457
+ if (this.endedFlag) return;
1458
+ let e = {
1334
1459
  player: this.opts.player,
1335
- sdkVersion: this.opts.sdkVersion
1336
- } }), this.flush(), 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()));
1460
+ sdkVersion: this.opts.sdkVersion,
1461
+ ...this.opts.environment
1462
+ };
1463
+ 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());
1337
1464
  }
1338
1465
  noteStateChange(e, t, n, r) {
1339
1466
  if (this.endedFlag || e === t) return;
1340
1467
  let i = this.now();
1341
- t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(H, i - this.bornMs))) : this.playingSinceMs !== null && (this.playingTotalMs += i - this.playingSinceMs, this.playingSinceMs = null), t === "buffering" && e === "playing" ? (this.rebufferCount += 1, this.bufferingSinceMs = i) : e === "buffering" && this.bufferingSinceMs !== null && (this.rebufferTotalMs += i - this.bufferingSinceMs, this.bufferingSinceMs = null), t === "paused" && (this.pauseCount += 1);
1468
+ 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.rebufferCount += 1, this.bufferingSinceMs = i, this.lastStallMs = i, this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(H, Math.round(this.playingTotalMs)))) : e === "buffering" && this.bufferingSinceMs !== null && (this.rebufferTotalMs += i - this.bufferingSinceMs, this.bufferingSinceMs = null), t === "paused" && (this.pauseCount += 1);
1342
1469
  let a = {
1343
- fromState: U[e] ?? "PLAYER_STATE_UNSPECIFIED",
1344
- toState: U[t] ?? "PLAYER_STATE_UNSPECIFIED"
1345
- };
1346
- n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
1470
+ fromState: G[e] ?? "PLAYER_STATE_UNSPECIFIED",
1471
+ toState: G[t] ?? "PLAYER_STATE_UNSPECIFIED",
1472
+ presentation: tt[this.presentation]
1473
+ }, o = this.causeOf(e, t, i);
1474
+ o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
1475
+ }
1476
+ causeOf(e, t, n) {
1477
+ if (t === "error") return "TRANSITION_CAUSE_ERROR";
1478
+ if (t === "buffering") return n - this.lastSeekMs < 1e3 ? "TRANSITION_CAUSE_SEEK" : "TRANSITION_CAUSE_NETWORK_STALL";
1479
+ if (t === "paused" || t === "playing" && e === "paused") return "TRANSITION_CAUSE_USER";
1480
+ }
1481
+ syncActive() {
1482
+ let e = this.isPlaying && !this.isHidden && !this.isMuted;
1483
+ e && this.activeSinceMs === null ? this.activeSinceMs = this.now() : !e && this.activeSinceMs !== null && (this.activeTotalMs += this.now() - this.activeSinceMs, this.activeSinceMs = null);
1484
+ }
1485
+ closePresentation() {
1486
+ let e = this.now(), t = e - this.presentationSinceMs;
1487
+ this.presentation === "fullscreen" ? this.fullscreenTotalMs += t : this.presentation === "pip" && (this.pipTotalMs += t), this.presentationSinceMs = e;
1488
+ }
1489
+ noteAutoplay(e) {
1490
+ this.pendingStart && (this.pendingStart.autoplay = e);
1491
+ }
1492
+ noteEnvironment(e) {
1493
+ this.pendingStart && Object.assign(this.pendingStart, e);
1494
+ }
1495
+ seedVolume(e, t) {
1496
+ this.endedFlag || (this.isMuted = e || t === 0, this.isMuted && this.isPlaying && this.mutedSinceMs === null && (this.mutedSinceMs = this.now()), this.syncActive());
1497
+ }
1498
+ noteVolume(e, t) {
1499
+ if (this.endedFlag) return;
1500
+ let n = e || t === 0;
1501
+ if (n === this.isMuted) {
1502
+ this.volumeChanges += 1;
1503
+ return;
1504
+ }
1505
+ this.muteChanges += 1;
1506
+ let r = this.now();
1507
+ 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();
1508
+ }
1509
+ notePresentation(e) {
1510
+ this.endedFlag || e === this.presentation || (this.closePresentation(), this.presentation = e);
1347
1511
  }
1348
1512
  noteReady() {
1349
1513
  this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(H, this.now() - this.bornMs));
1350
1514
  }
1351
1515
  noteFirstBytes() {
1352
- this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(H, this.now() - this.bornMs));
1516
+ this.sawFirstBytes = !0, this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(H, this.now() - this.bornMs));
1353
1517
  }
1354
1518
  noteSeek() {
1355
- this.endedFlag || (this.seekCount += 1);
1519
+ this.endedFlag || (this.seekCount += 1, this.lastSeekMs = this.now());
1356
1520
  }
1357
- noteError(e, t, n, r, i) {
1521
+ noteError(e, t, n, r, i, a) {
1358
1522
  if (this.endedFlag) return;
1359
- this.errorCount += 1;
1360
- let a = { code: e };
1361
- t && (a.detail = t), n && (a.fatal = !0), r > 0 && (a.httpStatus = r), i > 0 && (a.positionMs = Math.round(i)), this.enqueue({ error: a }), this.flush();
1523
+ this.errorCount += 1, this.lastErrorMs = this.now();
1524
+ let o = { code: e };
1525
+ t && (o.detail = t), n && (o.fatal = !0), r > 0 && (o.httpStatus = r), i > 0 && (o.positionMs = Math.round(i));
1526
+ let s = this.stageOf(e, t, a);
1527
+ o.stage = s, this.lastErrorStage = s;
1528
+ let c = this.safeSample()?.cdnHost;
1529
+ c && (o.cdnHost = c), this.enqueue({ error: o }), this.flush();
1530
+ }
1531
+ stageOf(e, t, n) {
1532
+ let r = n ?? "";
1533
+ 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";
1534
+ if (e === "DRM_CLIENT") return "ERROR_STAGE_DRM";
1535
+ if (rt.has(e)) return "ERROR_STAGE_RESOLVE";
1536
+ let i = t ?? "";
1537
+ 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";
1362
1538
  }
1363
1539
  noteEnded() {
1364
1540
  this.end("END_REASON_ENDED");
@@ -1382,25 +1558,51 @@ var Ye = class {
1382
1558
  let s = e.droppedFrames ?? 0;
1383
1559
  s > this.lastDropped && (i.droppedFramesDelta = s - this.lastDropped), this.lastDropped = s;
1384
1560
  let c = e.renditionSwitches ?? 0;
1385
- 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);
1386
- let l = this.bufferedMsFrom(e);
1387
- l > 0 && (i.bufferedMs = l), i.playerState = U[e.state] ?? "PLAYER_STATE_UNSPECIFIED", this.enqueue({ heartbeat: i }), this.flush(), a > 0 && this.noteFirstBytes();
1561
+ 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";
1562
+ let l = this.mutedTotalMs;
1563
+ this.mutedSinceMs !== null && (l += t - this.mutedSinceMs), l > this.lastHbMutedMs && (i.mutedMsDelta = Math.round(l - this.lastHbMutedMs)), this.lastHbMutedMs = l;
1564
+ let u = e.upshifts ?? 0;
1565
+ u > this.lastUpshifts && (i.upshiftsDelta = u - this.lastUpshifts), this.lastUpshifts = u;
1566
+ let d = e.downshifts ?? 0;
1567
+ d > this.lastDownshifts && (i.downshiftsDelta = d - this.lastDownshifts), this.lastDownshifts = d;
1568
+ let f = e.decodedFrames ?? 0;
1569
+ f > this.lastDecoded && (i.decodedFramesDelta = f - this.lastDecoded), this.lastDecoded = f;
1570
+ let p = e.requestErrors ?? 0;
1571
+ p > this.lastRequestErrors && (i.requestErrorsDelta = p - this.lastRequestErrors), this.lastRequestErrors = p;
1572
+ let m = e.requestCount ?? 0, h = m - this.lastRequestCount;
1573
+ if (this.lastRequestCount = m, h > 0) {
1574
+ i.requestCountDelta = h;
1575
+ let t = e.requestTimesMs ?? [], n = t.slice(Math.max(0, t.length - h)).sort((e, t) => e - t);
1576
+ n.length > 0 && (i.requestTimeP50Ms = W(n, 50), i.requestTimeP95Ms = W(n, 95));
1577
+ }
1578
+ 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();
1388
1579
  }
1389
- bufferedMsFrom(e) {
1390
- let t = e;
1391
- return typeof t.bufferedMs == "number" ? Math.round(t.bufferedMs) : 0;
1580
+ maybeEmitMediaInfo(e) {
1581
+ if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
1582
+ let t = {};
1583
+ 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);
1584
+ let n = JSON.stringify(t);
1585
+ n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
1392
1586
  }
1393
1587
  end(e) {
1394
1588
  if (this.endedFlag) return;
1395
1589
  this.endedFlag = !0;
1396
1590
  let t = this.now();
1397
- this.playingSinceMs !== null && (this.playingTotalMs += t - this.playingSinceMs, this.playingSinceMs = null), this.hiddenSinceMs !== null && (this.hiddenTotalMs += t - this.hiddenSinceMs, this.hiddenSinceMs = null), this.bufferingSinceMs !== null && (this.rebufferTotalMs += t - this.bufferingSinceMs, this.bufferingSinceMs = null), this.timer &&= (clearInterval(this.timer), null), typeof window < "u" && window.removeEventListener("pagehide", this.onPagehide), typeof document < "u" && document.removeEventListener("visibilitychange", this.onVisibility);
1398
- let n = this.safeSample(), r = { reason: e }, i = n?.bytesLoaded ?? this.lastBytes;
1591
+ this.playingSinceMs !== null && (this.playingTotalMs += t - this.playingSinceMs, this.playingSinceMs = null), this.hiddenSinceMs !== null && (this.hiddenTotalMs += t - this.hiddenSinceMs, this.hiddenSinceMs = null), this.bufferingSinceMs !== null && (this.rebufferTotalMs += t - this.bufferingSinceMs, this.bufferingSinceMs = null), this.mutedSinceMs !== null && (this.mutedTotalMs += t - this.mutedSinceMs, this.mutedSinceMs = null), this.waitingSinceMs !== null && (this.waitingTotalMs += t - this.waitingSinceMs, this.waitingSinceMs = null), this.isPlaying = !1, this.syncActive(), this.closePresentation(), this.firstFlushTimer &&= (clearTimeout(this.firstFlushTimer), null), this.timer &&= (clearInterval(this.timer), null), typeof window < "u" && window.removeEventListener("pagehide", this.onPagehide), typeof document < "u" && document.removeEventListener("visibilitychange", this.onVisibility);
1592
+ let n = this.safeSample();
1593
+ n && this.maybeEmitMediaInfo(n);
1594
+ let r = { reason: e }, i = n?.bytesLoaded ?? this.lastBytes;
1399
1595
  i > 0 && (r.bytesDownloadedTotal = i), this.playingTotalMs > 0 && (r.watchTimeMs = Math.round(this.playingTotalMs)), this.rebufferCount > 0 && (r.rebufferCount = this.rebufferCount), this.rebufferTotalMs > 0 && (r.rebufferTimeMs = Math.round(this.rebufferTotalMs)), this.startupTimeMs > 0 && (r.startupTimeMs = Math.round(this.startupTimeMs)), this.manifestLoadMs > 0 && (r.manifestLoadMs = Math.round(this.manifestLoadMs)), this.firstFragLoadMs > 0 && (r.firstFragLoadMs = Math.round(this.firstFragLoadMs));
1400
1596
  let a = n?.licenseTimeMs ?? 0;
1401
1597
  a > 0 && (r.licenseTimeMs = Math.round(a));
1402
1598
  let o = n?.droppedFrames ?? this.lastDropped;
1403
- o > 0 && (r.droppedFramesTotal = o), this.errorCount > 0 && (r.errorCount = this.errorCount), this.hiddenTotalMs > 0 && (r.hiddenTimeMs = Math.round(this.hiddenTotalMs)), this.seekCount > 0 && (r.seekCount = this.seekCount), this.pauseCount > 0 && (r.pauseCount = this.pauseCount), this.pending.push(this.wrap({ sessionEnd: r })), this.flush(e === "END_REASON_PAGEHIDE");
1599
+ o > 0 && (r.droppedFramesTotal = o), this.errorCount > 0 && (r.errorCount = this.errorCount), this.hiddenTotalMs > 0 && (r.hiddenTimeMs = Math.round(this.hiddenTotalMs)), this.seekCount > 0 && (r.seekCount = this.seekCount), this.pauseCount > 0 && (r.pauseCount = this.pauseCount), this.firstAudioMs > 0 && (r.firstAudioMs = Math.round(this.firstAudioMs)), this.waitingTotalMs > 0 && (r.waitingForLiveMs = Math.round(this.waitingTotalMs)), this.timeToFirstStallMs > 0 && (r.timeToFirstStallMs = Math.round(this.timeToFirstStallMs));
1600
+ let s = n?.positionMs ?? 0;
1601
+ 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));
1602
+ let c = e !== "END_REASON_ENDED";
1603
+ 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);
1604
+ let l = n?.decodedFrames ?? this.lastDecoded;
1605
+ 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");
1404
1606
  }
1405
1607
  safeSample() {
1406
1608
  try {
@@ -1418,13 +1620,13 @@ var Ye = class {
1418
1620
  };
1419
1621
  }
1420
1622
  enqueue(e) {
1421
- for (this.pending.push(this.wrap(e)); this.pending.length > qe;) {
1623
+ for (this.pending.push(this.wrap(e)); this.pending.length > $e;) {
1422
1624
  let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
1423
1625
  this.pending.splice(e >= 0 ? e : 0, 1);
1424
1626
  }
1425
1627
  }
1426
1628
  flush(e = !1) {
1427
- if (this.pending.length === 0) return;
1629
+ if (this.firstFlushTimer &&= (clearTimeout(this.firstFlushTimer), null), this.pendingStart = null, this.pending.length === 0) return;
1428
1630
  let t = JSON.stringify({ beacons: this.pending });
1429
1631
  if (this.pending = [], e) {
1430
1632
  let e = this.opts.sendBeacon ?? (typeof navigator < "u" && navigator.sendBeacon ? navigator.sendBeacon.bind(navigator) : void 0);
@@ -1440,7 +1642,74 @@ var Ye = class {
1440
1642
  keepalive: !0
1441
1643
  }).catch(() => {});
1442
1644
  }
1443
- }, Xe = "2.0.4", W = {
1645
+ };
1646
+ //#endregion
1647
+ //#region src/core/environment.ts
1648
+ function ot() {
1649
+ if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
1650
+ let e;
1651
+ try {
1652
+ e = window.top !== window.self;
1653
+ } catch {
1654
+ e = !0;
1655
+ }
1656
+ let t = window.location?.origin;
1657
+ if (!e) return {
1658
+ host: t && t !== "null" ? t : void 0,
1659
+ integration: "INTEGRATION_KIND_SDK"
1660
+ };
1661
+ let n;
1662
+ try {
1663
+ let e = window.location.ancestorOrigins;
1664
+ e && e.length > 0 && (n = e[e.length - 1] ?? void 0), !n && typeof document < "u" && document.referrer && (n = new URL(document.referrer).origin);
1665
+ } catch {
1666
+ n = void 0;
1667
+ }
1668
+ return n === "null" && (n = void 0), {
1669
+ host: n,
1670
+ integration: "INTEGRATION_KIND_IFRAME"
1671
+ };
1672
+ }
1673
+ function st() {
1674
+ if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
1675
+ let e = navigator.userAgentData, t = e?.platform ?? "";
1676
+ if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
1677
+ let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
1678
+ 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";
1679
+ }
1680
+ function ct() {
1681
+ let e = typeof navigator < "u" ? navigator.connection : void 0;
1682
+ if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
1683
+ let t = { connectionType: e.type ? {
1684
+ wifi: "CONNECTION_TYPE_WIFI",
1685
+ cellular: "CONNECTION_TYPE_CELLULAR",
1686
+ ethernet: "CONNECTION_TYPE_ETHERNET"
1687
+ }[e.type] ?? "CONNECTION_TYPE_OTHER" : "CONNECTION_TYPE_UNKNOWN" };
1688
+ return typeof e.downlink == "number" && e.downlink > 0 && (t.downlinkKbps = Math.round(e.downlink * 1e3)), e.saveData && (t.saveData = !0), t;
1689
+ }
1690
+ function lt(e) {
1691
+ let t = ot(), n = {
1692
+ deviceClass: st(),
1693
+ integration: t.integration,
1694
+ ...ct()
1695
+ };
1696
+ 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);
1697
+ let r = e.getBoundingClientRect?.();
1698
+ return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
1699
+ }
1700
+ async function ut() {
1701
+ let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
1702
+ if (!e?.getHighEntropyValues) return {};
1703
+ try {
1704
+ let t = await e.getHighEntropyValues(["model", "platformVersion"]), n = {};
1705
+ return t.model && (n.deviceModel = t.model), t.platformVersion && (n.osVersion = t.platformVersion), n;
1706
+ } catch {
1707
+ return {};
1708
+ }
1709
+ }
1710
+ //#endregion
1711
+ //#region src/core/version.ts
1712
+ var dt = "2.1.1", K = {
1444
1713
  accent: "#E1B600",
1445
1714
  accentText: "#1B1C23",
1446
1715
  background: "#0e0f13",
@@ -1451,7 +1720,7 @@ var Ye = class {
1451
1720
  border: "rgba(255, 255, 255, 0.1)",
1452
1721
  radius: "8px",
1453
1722
  fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
1454
- }, G = {
1723
+ }, q = {
1455
1724
  accent: "--havik-accent",
1456
1725
  accentText: "--havik-accent-text",
1457
1726
  background: "--havik-bg",
@@ -1463,25 +1732,25 @@ var Ye = class {
1463
1732
  radius: "--havik-radius",
1464
1733
  fontFamily: "--havik-font"
1465
1734
  };
1466
- function K(e, t) {
1735
+ function J(e, t) {
1467
1736
  let n = {
1468
- ...W,
1737
+ ...K,
1469
1738
  ...t
1470
1739
  };
1471
- for (let t of Object.keys(G)) e.style.setProperty(G[t], n[t]);
1740
+ for (let t of Object.keys(q)) e.style.setProperty(q[t], n[t]);
1472
1741
  n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
1473
1742
  }
1474
1743
  //#endregion
1475
1744
  //#region src/ui/styles.ts
1476
- var q = "havik-player-styles", Ze = "\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";
1477
- function Qe(e = document) {
1478
- if (e.getElementById(q)) return;
1745
+ 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";
1746
+ function pt(e = document) {
1747
+ if (e.getElementById(Y)) return;
1479
1748
  let t = e.createElement("style");
1480
- t.id = q, t.textContent = Ze, e.head.appendChild(t);
1749
+ t.id = Y, t.textContent = ft, e.head.appendChild(t);
1481
1750
  }
1482
1751
  //#endregion
1483
1752
  //#region src/ui/icons.ts
1484
- var $e = {
1753
+ var mt = {
1485
1754
  play: "<path d=\"M8 5v14l11-7z\"/>",
1486
1755
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
1487
1756
  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\"/>",
@@ -1494,21 +1763,21 @@ var $e = {
1494
1763
  liveDot: "<circle cx=\"12\" cy=\"12\" r=\"6\"/>",
1495
1764
  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\"/>"
1496
1765
  };
1497
- function J(e) {
1498
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${$e[e]}</svg>`;
1766
+ function X(e) {
1767
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${mt[e]}</svg>`;
1499
1768
  }
1500
1769
  //#endregion
1501
1770
  //#region src/ui/controls.ts
1502
- var et = 3e3, tt = /* @__PURE__ */ new Set([
1771
+ var ht = 3e3, gt = /* @__PURE__ */ new Set([
1503
1772
  "GONE",
1504
1773
  "NOT_FOUND",
1505
1774
  "INVALID_URN",
1506
1775
  "UNAUTHORIZED"
1507
1776
  ]);
1508
- function nt(e) {
1509
- return new rt(e);
1777
+ function _t(e) {
1778
+ return new vt(e);
1510
1779
  }
1511
- var rt = class {
1780
+ var vt = class {
1512
1781
  root;
1513
1782
  video;
1514
1783
  player;
@@ -1550,7 +1819,7 @@ var rt = class {
1550
1819
  scrubbing = !1;
1551
1820
  offFns = [];
1552
1821
  constructor(e) {
1553
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? et, 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, Qe(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");
1822
+ 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");
1554
1823
  let t = (e, t, n) => {
1555
1824
  let r = this.doc.createElement(e);
1556
1825
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -1561,12 +1830,12 @@ var rt = class {
1561
1830
  "aria-label": r,
1562
1831
  title: r
1563
1832
  });
1564
- return i.innerHTML = J(n), i;
1833
+ return i.innerHTML = X(n), i;
1565
1834
  };
1566
1835
  this.overlay = t("div", "havik-overlay"), this.center = t("button", "havik-center", {
1567
1836
  type: "button",
1568
1837
  "aria-label": "Play"
1569
- }), this.center.innerHTML = J("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", {
1838
+ }), 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", {
1570
1839
  role: "slider",
1571
1840
  "aria-label": "Seek",
1572
1841
  "aria-valuemin": "0",
@@ -1584,7 +1853,7 @@ var rt = class {
1584
1853
  "aria-label": "Volume"
1585
1854
  });
1586
1855
  let a = t("div", "havik-vol");
1587
- a.append(this.muteBtn, this.volSlider), this.live = t("div", "havik-live"), this.live.innerHTML = J("liveDot");
1856
+ a.append(this.muteBtn, this.volSlider), this.live = t("div", "havik-live"), this.live.innerHTML = X("liveDot");
1588
1857
  let o = this.doc.createElement("span");
1589
1858
  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";
1590
1859
  let s = t("div", "havik-spacer");
@@ -1638,12 +1907,12 @@ var rt = class {
1638
1907
  }
1639
1908
  if (e === "preplay") {
1640
1909
  let e = this.doc.createElement("button");
1641
- e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = J("play"), e.addEventListener("click", () => void this.player.play()), n.append(e);
1910
+ 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);
1642
1911
  } else {
1643
1912
  let r = this.doc.createElement("div");
1644
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !tt.has(this.lastError.code)) {
1913
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !gt.has(this.lastError.code)) {
1645
1914
  let e = this.doc.createElement("button");
1646
- e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${J("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), n.append(e);
1915
+ 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);
1647
1916
  }
1648
1917
  }
1649
1918
  this.root.append(n), this.statusBox = n, this.show();
@@ -1682,7 +1951,7 @@ var rt = class {
1682
1951
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
1683
1952
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
1684
1953
  }
1685
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Y(this.video.currentTime)} / ${Y(this.video.duration)}`);
1954
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Z(this.video.currentTime)} / ${Z(this.video.duration)}`);
1686
1955
  }
1687
1956
  syncLive() {
1688
1957
  let e = this.player.getStats(), t = e.isLive !== !1;
@@ -1732,17 +2001,17 @@ var rt = class {
1732
2001
  renderQualityMenu() {
1733
2002
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
1734
2003
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
1735
- for (let n of e) this.menu.append(this.menuItem(it(n), t === n.index, () => this.player.setQuality(n.index)));
2004
+ for (let n of e) this.menu.append(this.menuItem(yt(n), t === n.index, () => this.player.setQuality(n.index)));
1736
2005
  }
1737
2006
  renderAudioMenu() {
1738
2007
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
1739
2008
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
1740
- for (let t of e) this.menu.append(this.menuItem(at(t), t.default, () => this.player.setAudioTrack(t.id)));
2009
+ for (let t of e) this.menu.append(this.menuItem(bt(t), t.default, () => this.player.setAudioTrack(t.id)));
1741
2010
  }
1742
2011
  renderCcMenu() {
1743
2012
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
1744
2013
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
1745
- for (let t of e) this.menu.append(this.menuItem(ot(t), !1, () => this.player.setTextTrack(t.id)));
2014
+ for (let t of e) this.menu.append(this.menuItem(xt(t), !1, () => this.player.setTextTrack(t.id)));
1746
2015
  }
1747
2016
  onTracksChanged() {
1748
2017
  this.openMenu && this.renderMenu(this.openMenu);
@@ -1762,11 +2031,11 @@ var rt = class {
1762
2031
  }
1763
2032
  async toggleFullscreen() {
1764
2033
  try {
1765
- w(this.video, this.root) ? await E(this.video, this.root) : await T(this.video, this.root);
2034
+ C(this.video, this.root) ? await T(this.video, this.root) : await w(this.video, this.root);
1766
2035
  } catch {}
1767
2036
  }
1768
2037
  syncFullscreen() {
1769
- let e = w(this.video, this.root);
2038
+ let e = C(this.video, this.root);
1770
2039
  this.setIcon(this.fsBtn, e ? "fullscreenExit" : "fullscreen"), this.fsBtn.setAttribute("aria-label", e ? "Exit fullscreen" : "Fullscreen");
1771
2040
  }
1772
2041
  poke() {
@@ -1825,7 +2094,7 @@ var rt = class {
1825
2094
  }
1826
2095
  }
1827
2096
  setIcon(e, t) {
1828
- e.innerHTML = J(t);
2097
+ e.innerHTML = X(t);
1829
2098
  }
1830
2099
  destroy() {
1831
2100
  this.hideTimer && clearTimeout(this.hideTimer);
@@ -1842,32 +2111,32 @@ var rt = class {
1842
2111
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
1843
2112
  }
1844
2113
  };
1845
- function Y(e) {
2114
+ function Z(e) {
1846
2115
  (!Number.isFinite(e) || e < 0) && (e = 0);
1847
2116
  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");
1848
2117
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
1849
2118
  }
1850
- function it(e) {
2119
+ function yt(e) {
1851
2120
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
1852
2121
  }
1853
- function at(e) {
2122
+ function bt(e) {
1854
2123
  return e.name || e.lang || `Track ${e.id}`;
1855
2124
  }
1856
- function ot(e) {
2125
+ function xt(e) {
1857
2126
  return e.name || e.lang || `Track ${e.id}`;
1858
2127
  }
1859
2128
  //#endregion
1860
2129
  //#region src/managed/index.ts
1861
- var st = 6e3, X = 45e3, Z = 2e3, ct = 3, lt = 2e3, ut = 48e4;
1862
- async function Q(e) {
2130
+ var St = 6e3, Q = 45e3, Ct = 2e3, wt = 3, Tt = 2e3, Et = 48e4;
2131
+ async function Dt(e) {
1863
2132
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
1864
2133
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: baseUrl is required");
1865
2134
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
1866
2135
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
1867
- let t = new dt(e);
2136
+ let t = new Ot(e);
1868
2137
  return await t.start(), t;
1869
2138
  }
1870
- var dt = class {
2139
+ var Ot = class {
1871
2140
  opts;
1872
2141
  abort = new AbortController();
1873
2142
  listeners = {};
@@ -1891,7 +2160,7 @@ var dt = class {
1891
2160
  armFallbackCtrl = null;
1892
2161
  _lastError = null;
1893
2162
  analyticsSession = null;
1894
- detachSeekListener;
2163
+ detachMediaListeners;
1895
2164
  playRequested = !1;
1896
2165
  pendingStartLoad = !1;
1897
2166
  constructor(e) {
@@ -1933,7 +2202,7 @@ var dt = class {
1933
2202
  };
1934
2203
  }
1935
2204
  beginArming(e, t, n = !1) {
1936
- let r = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? O(this.opts.baseUrl);
2205
+ let r = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? D(this.opts.baseUrl);
1937
2206
  r ? this.armWithSse(e, r, t, n) : this.beginArmingByPolling(e);
1938
2207
  }
1939
2208
  beginArmingByPolling(e) {
@@ -1954,7 +2223,7 @@ var dt = class {
1954
2223
  setTimeout(() => {
1955
2224
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
1956
2225
  phase: i ? "unavailable" : "tooEarly",
1957
- retryInMs: X,
2226
+ retryInMs: Q,
1958
2227
  attempt: 1,
1959
2228
  elapsedMs: 0,
1960
2229
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -1993,13 +2262,13 @@ var dt = class {
1993
2262
  !a && !p() && h();
1994
2263
  }, e);
1995
2264
  };
1996
- _(st), this.armingSub = D({
2265
+ _(St), this.armingSub = E({
1997
2266
  eventsBaseUrl: t,
1998
2267
  matchUrn: this.opts.matchUrn,
1999
2268
  credential: this.opts.credential,
2000
- onAlive: () => _(X),
2269
+ onAlive: () => _(Q),
2001
2270
  onState: (e) => {
2002
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Z);
2271
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Ct);
2003
2272
  },
2004
2273
  onError: () => h()
2005
2274
  });
@@ -2022,7 +2291,7 @@ var dt = class {
2022
2291
  let n = await s(this.opts.credential);
2023
2292
  if (this.destroyed || t !== this.loadGen) return;
2024
2293
  this.setState("loading");
2025
- let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = we(this.opts.video, (e) => {
2294
+ let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = Oe(this.opts.video, (e) => {
2026
2295
  this._state !== "ended" && this._state !== "error" && this.setState(e);
2027
2296
  });
2028
2297
  if (await i.load(), this.destroyed || t !== this.loadGen) {
@@ -2038,30 +2307,40 @@ var dt = class {
2038
2307
  }, 0));
2039
2308
  }
2040
2309
  startAnalytics(e, t, n) {
2041
- if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachSeekListener?.(), this.opts.analytics === !1) return;
2310
+ if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
2042
2311
  let r = e.analytics?.sid;
2043
2312
  if (!r) return;
2044
- let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? Je(this.opts.baseUrl);
2313
+ let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? it(this.opts.baseUrl);
2045
2314
  if (!i) return;
2046
- let a = this.opts.video, o = new Ye({
2315
+ let a = this.opts.video, o = new at({
2047
2316
  endpoint: i,
2048
2317
  sid: r,
2049
2318
  apiKey: n,
2050
2319
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
2051
- sdkVersion: Xe,
2320
+ sdkVersion: dt,
2321
+ environment: lt(a),
2052
2322
  sample: () => {
2053
2323
  let e = this.engine?.getStats() ?? F, t = a.buffered.length ? Math.max(0, a.buffered.end(a.buffered.length - 1) - a.currentTime) : 0;
2054
2324
  return {
2055
2325
  ...e,
2056
2326
  bufferedMs: Math.round(t * 1e3),
2057
2327
  state: this._state,
2058
- positionMs: Math.round(a.currentTime * 1e3)
2328
+ positionMs: Math.round(a.currentTime * 1e3),
2329
+ playbackRate: a.playbackRate
2059
2330
  };
2060
2331
  }
2061
2332
  });
2062
2333
  this.analyticsSession = o;
2063
- let s = () => o.noteSeek();
2064
- a.addEventListener("seeking", s), this.detachSeekListener = () => a.removeEventListener("seeking", s), o.start();
2334
+ let s = () => o.noteSeek(), c = () => o.noteVolume(a.muted, a.volume);
2335
+ a.addEventListener("seeking", s), a.addEventListener("volumechange", c), this.detachMediaListeners = () => {
2336
+ a.removeEventListener("seeking", s), a.removeEventListener("volumechange", c);
2337
+ }, 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");
2338
+ }
2339
+ currentPresentation() {
2340
+ let e = this.opts.video;
2341
+ if (typeof document < "u" && document.pictureInPictureElement === e) return "pip";
2342
+ let t = typeof document < "u" ? document.fullscreenElement : null;
2343
+ return e.webkitDisplayingFullscreen === !0 || t && (t === e || t.contains(e)) ? "fullscreen" : "inline";
2065
2344
  }
2066
2345
  createEngine(e, t, r = !1) {
2067
2346
  let i = { ...this.opts.hlsConfig };
@@ -2070,7 +2349,7 @@ var dt = class {
2070
2349
  video: this.opts.video,
2071
2350
  descriptor: e,
2072
2351
  credential: t,
2073
- deviceId: j(),
2352
+ deviceId: A(),
2074
2353
  userId: this.opts.userId,
2075
2354
  lowLatency: this.opts.lowLatency === void 0 || this.opts.lowLatency === "auto" || this.opts.lowLatency,
2076
2355
  snapToLiveOnRefocus: this.opts.snapToLiveOnRefocus,
@@ -2082,7 +2361,7 @@ var dt = class {
2082
2361
  if (o === "native") return new z(a);
2083
2362
  if (o === "hls") return new V(a);
2084
2363
  if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && L(this.opts.video) && R()) return new z(a);
2085
- if (De()) return new V(a);
2364
+ if (je()) return new V(a);
2086
2365
  if (L(this.opts.video)) return new z(a);
2087
2366
  throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
2088
2367
  }
@@ -2106,16 +2385,16 @@ var dt = class {
2106
2385
  case "ended":
2107
2386
  this.onStreamEnded();
2108
2387
  break;
2109
- case "error": this.analyticsSession?.noteError(e.error.code, e.error.message, e.fatal, e.error.httpStatus ?? 0, Math.round(this.opts.video.currentTime * 1e3)), e.fatal ? this.fail(e.error) : this.emit("warning", e.error);
2388
+ case "error": this.analyticsSession?.noteError(e.error.code, e.error.message, e.fatal, e.error.httpStatus ?? 0, Math.round(this.opts.video.currentTime * 1e3), e.detail), e.fatal ? this.fail(e.error) : this.emit("warning", e.error);
2110
2389
  }
2111
2390
  }
2112
2391
  startLiveState(e) {
2113
2392
  if (this.opts.liveStateEvents === !1) return;
2114
- let t = this.opts.eventsBaseUrl ?? O(this.opts.baseUrl);
2393
+ let t = this.opts.eventsBaseUrl ?? D(this.opts.baseUrl);
2115
2394
  if (!t) return;
2116
2395
  this.closeLiveState();
2117
2396
  let r = 0, i = !1;
2118
- this.liveStateSub = D({
2397
+ this.liveStateSub = E({
2119
2398
  eventsBaseUrl: t,
2120
2399
  matchUrn: this.opts.matchUrn,
2121
2400
  credential: this.opts.credential,
@@ -2126,7 +2405,7 @@ var dt = class {
2126
2405
  r = 0, i = !1;
2127
2406
  },
2128
2407
  onError: () => {
2129
- this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= ct && (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.`))));
2408
+ 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.`))));
2130
2409
  }
2131
2410
  });
2132
2411
  }
@@ -2148,7 +2427,7 @@ var dt = class {
2148
2427
  scheduleRejoin(e) {
2149
2428
  !this.rejoinEnabled() || this.rejoinTimer || (this.rejoinTimer = setTimeout(() => {
2150
2429
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
2151
- }, Math.random() * Z));
2430
+ }, Math.random() * Ct));
2152
2431
  }
2153
2432
  cancelRejoin() {
2154
2433
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -2172,7 +2451,7 @@ var dt = class {
2172
2451
  }
2173
2452
  t.controls = !1;
2174
2453
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
2175
- r && r.insertBefore(n, t), n.appendChild(t), K(n, this.opts.theme), this.container = n, this.controlBar = nt({
2454
+ r && r.insertBefore(n, t), n.appendChild(t), J(n, this.opts.theme), this.container = n, this.controlBar = _t({
2176
2455
  root: n,
2177
2456
  video: t,
2178
2457
  player: this,
@@ -2189,14 +2468,20 @@ var dt = class {
2189
2468
  e && (e.parentNode ? (e.parentNode.insertBefore(this.opts.video, e), e.remove()) : this.opts.video.parentNode === e && e.removeChild(this.opts.video)), this.container = null;
2190
2469
  }
2191
2470
  attachPipListeners() {
2192
- let e = this.opts.video, t = () => this.emit("pipchange", !0), n = () => this.emit("pipchange", !1);
2193
- e.addEventListener("enterpictureinpicture", t), e.addEventListener("leavepictureinpicture", n), this.detachPip = () => {
2194
- e.removeEventListener("enterpictureinpicture", t), e.removeEventListener("leavepictureinpicture", n);
2471
+ let e = this.opts.video, t = () => this.analyticsSession?.notePresentation(this.currentPresentation()), n = () => {
2472
+ t(), this.emit("pipchange", !0);
2473
+ }, r = () => {
2474
+ t(), this.emit("pipchange", !1);
2475
+ };
2476
+ e.addEventListener("enterpictureinpicture", n), e.addEventListener("leavepictureinpicture", r);
2477
+ let i = typeof document < "u" ? document : void 0;
2478
+ i?.addEventListener("fullscreenchange", t), i?.addEventListener("webkitfullscreenchange", t), this.detachPip = () => {
2479
+ e.removeEventListener("enterpictureinpicture", n), e.removeEventListener("leavepictureinpicture", r), i?.removeEventListener("fullscreenchange", t), i?.removeEventListener("webkitfullscreenchange", t);
2195
2480
  };
2196
2481
  }
2197
2482
  startStats() {
2198
2483
  this.stopStats();
2199
- let e = Math.max(500, this.opts.statsIntervalMs ?? lt);
2484
+ let e = Math.max(500, this.opts.statsIntervalMs ?? Tt);
2200
2485
  this.statsTimer = setInterval(() => {
2201
2486
  if (!this.engine) return;
2202
2487
  let e = this.engine.getStats();
@@ -2208,7 +2493,7 @@ var dt = class {
2208
2493
  }
2209
2494
  scheduleLicenseRefresh() {
2210
2495
  this.stopRefresh();
2211
- let e = this.opts.licenseRefreshMs ?? ut;
2496
+ let e = this.opts.licenseRefreshMs ?? Et;
2212
2497
  !e || !this._descriptor?.drmEnabled || (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
2213
2498
  }
2214
2499
  stopRefresh() {
@@ -2235,9 +2520,9 @@ var dt = class {
2235
2520
  let t = this.pendingStartLoad && this._state !== "ended" && this._state !== "error";
2236
2521
  t && (this.pendingStartLoad = !1, this.setState("loading"), this.engine?.startLoad());
2237
2522
  try {
2238
- await this.opts.video.play();
2523
+ await this.opts.video.play(), e && this.analyticsSession?.noteAutoplay(this.opts.video.muted ? "AUTOPLAY_RESULT_ALLOWED_MUTED" : "AUTOPLAY_RESULT_ALLOWED");
2239
2524
  } catch (n) {
2240
- n instanceof DOMException && n.name === "NotAllowedError" && (e && (this.playRequested = !1), t && !this.destroyed && this._state === "loading" && this.setState("paused"), this.emit("autoplayblocked"));
2525
+ n instanceof DOMException && n.name === "NotAllowedError" && (this.analyticsSession?.noteAutoplay("AUTOPLAY_RESULT_BLOCKED"), e && (this.playRequested = !1), t && !this.destroyed && this._state === "loading" && this.setState("paused"), this.emit("autoplayblocked"));
2241
2526
  }
2242
2527
  }
2243
2528
  pause() {
@@ -2289,10 +2574,10 @@ var dt = class {
2289
2574
  e.pictureInPictureElement === this.opts.video && typeof e.exitPictureInPicture == "function" && await e.exitPictureInPicture();
2290
2575
  }
2291
2576
  async enterFullscreen() {
2292
- return !this.destroyed && T(this.opts.video, this.container ?? void 0);
2577
+ return !this.destroyed && w(this.opts.video, this.container ?? void 0);
2293
2578
  }
2294
2579
  async exitFullscreen() {
2295
- await E(this.opts.video, this.container ?? void 0);
2580
+ await T(this.opts.video, this.container ?? void 0);
2296
2581
  }
2297
2582
  retry() {
2298
2583
  return this.reload();
@@ -2317,7 +2602,7 @@ var dt = class {
2317
2602
  return n.add(t), () => n.delete(t);
2318
2603
  }
2319
2604
  destroy() {
2320
- 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.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachSeekListener?.(), this.engine?.destroy(), this.engine = null, this.setState("idle"), this.clearListeners());
2605
+ 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.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.engine?.destroy(), this.engine = null, this.setState("idle"), this.clearListeners());
2321
2606
  }
2322
2607
  clearListeners() {
2323
2608
  for (let e of Object.keys(this.listeners)) this.listeners[e]?.clear();
@@ -2360,7 +2645,7 @@ function $(e) {
2360
2645
  }
2361
2646
  //#endregion
2362
2647
  //#region src/embed/host.ts
2363
- function ft(e) {
2648
+ function kt(e) {
2364
2649
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
2365
2650
  if (!e.src) throw Error("mountEmbed: opts.src is required");
2366
2651
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -2419,8 +2704,8 @@ function ft(e) {
2419
2704
  }
2420
2705
  //#endregion
2421
2706
  //#region src/embed/page.ts
2422
- var pt = /^[A-Za-z0-9:_.-]{1,128}$/;
2423
- function mt() {
2707
+ var At = /^[A-Za-z0-9:_.-]{1,128}$/;
2708
+ function jt() {
2424
2709
  let e = window.HAVIK_EMBED_CONFIG;
2425
2710
  if (e) return {
2426
2711
  baseUrl: e.baseUrl ?? "",
@@ -2442,9 +2727,9 @@ function mt() {
2442
2727
  waitForLive: !0
2443
2728
  };
2444
2729
  }
2445
- async function ht() {
2446
- let e = mt();
2447
- _t();
2730
+ async function Mt() {
2731
+ let e = jt();
2732
+ Pt();
2448
2733
  let t = document.createElement("video");
2449
2734
  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);
2450
2735
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -2499,12 +2784,12 @@ async function ht() {
2499
2784
  code: e.code,
2500
2785
  message: e.message
2501
2786
  })), e.on("autoplayblocked", () => {
2502
- i({ type: "oddin:autoplayblocked" }), gt(t, e);
2787
+ i({ type: "oddin:autoplayblocked" }), Nt(t, e);
2503
2788
  });
2504
2789
  }, s = async (r) => {
2505
2790
  a?.destroy(), a = null;
2506
2791
  try {
2507
- let n = await Q({
2792
+ let n = await Dt({
2508
2793
  video: t,
2509
2794
  baseUrl: e.baseUrl,
2510
2795
  matchUrn: r,
@@ -2565,7 +2850,7 @@ async function ht() {
2565
2850
  a?.retry();
2566
2851
  break;
2567
2852
  case "oddin:load":
2568
- typeof e.matchUrn == "string" && pt.test(e.matchUrn) && s(e.matchUrn);
2853
+ typeof e.matchUrn == "string" && At.test(e.matchUrn) && s(e.matchUrn);
2569
2854
  break;
2570
2855
  case "oddin:destroy": c();
2571
2856
  }
@@ -2577,18 +2862,18 @@ async function ht() {
2577
2862
  protocol: 1
2578
2863
  });
2579
2864
  }
2580
- function gt(e, t) {
2865
+ function Nt(e, t) {
2581
2866
  if (document.getElementById("havik-tap")) return;
2582
2867
  let n = document.createElement("button");
2583
2868
  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", () => {
2584
2869
  n.remove(), t.play();
2585
2870
  }), document.body.appendChild(n);
2586
2871
  }
2587
- function _t() {
2872
+ function Pt() {
2588
2873
  let e = document.createElement("style");
2589
2874
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
2590
2875
  }
2591
2876
  //#endregion
2592
- export { W as ODDIN_THEME, n as PlaybackError, K as applyTheme, ht as bootEmbed, he as clearDeviceId, Q as createPlayer, O as deriveEventsBaseUrl, xe as detectDrmSupport, T as enterVideoFullscreen, E as exitVideoFullscreen, le as fetchCatalog, de as fetchTournament, pe as flattenMatches, j as getDeviceId, Ce as isLowLatencyManifest, $ as isOddinMessage, _e as licenseRequestHeaders, ft as mountEmbed, v as pollToLive, b as resolveStream, D as subscribeLiveState, w as videoFullscreenActive, me as watchStatus };
2877
+ 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 };
2593
2878
 
2594
2879
  //# sourceMappingURL=index.js.map