@oddin-gg/havik-player 2.13.1 → 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -174,7 +174,7 @@ function ue(e, t) {
174
174
  t?.addEventListener("abort", a, { once: !0 });
175
175
  });
176
176
  }
177
- async function de(e, t, r) {
177
+ async function v(e, t, r) {
178
178
  let i = ae(t), a = Date.now(), s = i.timeoutMs == null ? Infinity : a + i.timeoutMs, c = 0, l;
179
179
  for (;;) {
180
180
  if (r?.aborted) throw new n("ABORTED", 0, "wait-for-live aborted");
@@ -200,21 +200,21 @@ async function de(e, t, r) {
200
200
  }
201
201
  //#endregion
202
202
  //#region src/core/resolve.ts
203
- async function v(e) {
203
+ async function y(e) {
204
204
  if (!e.matchUrn || typeof e.matchUrn != "string") throw new n("INVALID_URN", 0, "resolveStream: a non-empty matchUrn is required");
205
205
  let t = await p(d(e.baseUrl, `/v1/playback/${encodeURIComponent(e.matchUrn)}`), {
206
206
  credential: e.credential,
207
207
  signal: e.signal,
208
208
  apiHeaders: e.apiHeaders
209
209
  });
210
- if (!t.ok) throw await fe(t);
211
- return he(await t.json(), e.matchUrn);
210
+ if (!t.ok) throw await de(t);
211
+ return me(await t.json(), e.matchUrn);
212
212
  }
213
- function y(e) {
214
- let t = () => v(e);
215
- return e.waitForLive ? de(t, e.waitForLive, e.signal) : t();
213
+ function b(e) {
214
+ let t = () => y(e);
215
+ return e.waitForLive ? v(t, e.waitForLive, e.signal) : t();
216
216
  }
217
- async function fe(e) {
217
+ async function de(e) {
218
218
  let t = f(e.headers.get("retry-after")), i = e.headers.get("x-request-id") ?? e.headers.get("x-amzn-requestid") ?? void 0, a = e.statusText || `HTTP ${e.status}`, o, s;
219
219
  try {
220
220
  let t = await e.json();
@@ -230,7 +230,7 @@ async function fe(e) {
230
230
  liveStartsAtMs: s
231
231
  });
232
232
  }
233
- var pe = /* @__PURE__ */ new Set([
233
+ var fe = /* @__PURE__ */ new Set([
234
234
  "matchUrn",
235
235
  "protocol",
236
236
  "drmEnabled",
@@ -241,7 +241,7 @@ var pe = /* @__PURE__ */ new Set([
241
241
  "expiresAt",
242
242
  "analytics"
243
243
  ]);
244
- function b(e, t) {
244
+ function x(e, t) {
245
245
  let r;
246
246
  try {
247
247
  r = new URL(t);
@@ -251,7 +251,7 @@ function b(e, t) {
251
251
  if (r.protocol !== "https:" && r.protocol !== "http:") throw new n("INTERNAL", 0, `playback ${e} has a disallowed scheme: ${r.protocol}`);
252
252
  return t;
253
253
  }
254
- function me(e) {
254
+ function pe(e) {
255
255
  if (e !== void 0) {
256
256
  if (typeof e == "string" && !e.endsWith("/")) try {
257
257
  let t = new URL(e);
@@ -260,41 +260,41 @@ function me(e) {
260
260
  g(`server analytics.endpoint ${JSON.stringify(e)} ignored: not a bare https origin.`);
261
261
  }
262
262
  }
263
- function he(e, t) {
263
+ function me(e, t) {
264
264
  if (!e.manifestUrl) throw new n("INTERNAL", 0, "playback response is missing manifestUrl");
265
265
  let r = e.drmEnabled ?? !!e.drm, i = {
266
266
  matchUrn: e.matchUrn ?? t,
267
267
  protocol: "hls",
268
268
  drmEnabled: r,
269
- manifestUrl: b("manifestUrl", e.manifestUrl),
269
+ manifestUrl: x("manifestUrl", e.manifestUrl),
270
270
  serverTime: e.serverTime ?? ""
271
271
  };
272
272
  if (e.liveStartsAt && (i.liveStartsAt = e.liveStartsAt), typeof e.analytics?.sid == "string" && e.analytics.sid) {
273
273
  i.analytics = { sid: e.analytics.sid };
274
- let t = me(e.analytics.endpoint);
274
+ let t = pe(e.analytics.endpoint);
275
275
  t && (i.analytics.endpoint = t);
276
276
  }
277
277
  if (r && e.drm) {
278
278
  let t = {};
279
- e.drm.widevine?.licenseUrl && (t.widevine = { licenseUrl: b("widevine licenseUrl", e.drm.widevine.licenseUrl) }), e.drm.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && (t.fairplay = {
280
- licenseUrl: b("fairplay licenseUrl", e.drm.fairplay.licenseUrl),
281
- certificateUrl: b("fairplay certificateUrl", e.drm.fairplay.certificateUrl)
279
+ e.drm.widevine?.licenseUrl && (t.widevine = { licenseUrl: x("widevine licenseUrl", e.drm.widevine.licenseUrl) }), e.drm.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && (t.fairplay = {
280
+ licenseUrl: x("fairplay licenseUrl", e.drm.fairplay.licenseUrl),
281
+ certificateUrl: x("fairplay certificateUrl", e.drm.fairplay.certificateUrl)
282
282
  }), (t.widevine || t.fairplay) && (i.drm = t);
283
283
  }
284
284
  let a = {};
285
- for (let [t, n] of Object.entries(e)) pe.has(t) || (a[t] = n);
285
+ for (let [t, n] of Object.entries(e)) fe.has(t) || (a[t] = n);
286
286
  return Object.keys(a).length > 0 && (i.extensions = a), i;
287
287
  }
288
288
  //#endregion
289
289
  //#region src/core/fullscreen.ts
290
- function ge(e) {
290
+ function he(e) {
291
291
  return e.ownerDocument ?? document;
292
292
  }
293
- function x(e) {
293
+ function ge(e) {
294
294
  return e.fullscreenElement ?? e.webkitFullscreenElement ?? null;
295
295
  }
296
296
  function S(e, t) {
297
- let n = x(ge(e));
297
+ let n = ge(he(e));
298
298
  return n != null && (n === e || t != null && n === t) || e.webkitDisplayingFullscreen === !0;
299
299
  }
300
300
  async function C(e, t) {
@@ -310,7 +310,7 @@ async function w(e, t) {
310
310
  n.webkitExitFullscreen?.();
311
311
  return;
312
312
  }
313
- let r = ge(e), i = x(r);
313
+ let r = he(e), i = ge(r);
314
314
  i != null && (i === e || t != null && i === t) && (typeof r.exitFullscreen == "function" ? await r.exitFullscreen() : r.webkitExitFullscreen?.());
315
315
  }
316
316
  //#endregion
@@ -671,14 +671,14 @@ function I(e) {
671
671
  function L(e) {
672
672
  return (e ?? "").trim().toLowerCase();
673
673
  }
674
- var je = {
674
+ var R = {
675
675
  hevc: "hvc1.1.6.L93.90",
676
676
  dv: "dvh1.05.06",
677
677
  av1: "av01.0.05M.08",
678
678
  vp9: "vp09.00.10.08"
679
- }, Me = /* @__PURE__ */ new Map(), R = /* @__PURE__ */ new Map();
680
- function Ne(e) {
681
- let t = L(e), n = Me.get(t);
679
+ }, z = /* @__PURE__ */ new Map(), B = /* @__PURE__ */ new Map();
680
+ function V(e) {
681
+ let t = L(e), n = z.get(t);
682
682
  if (n) return n;
683
683
  let r = {
684
684
  initDataTypes: ["cenc"],
@@ -696,23 +696,23 @@ function Ne(e) {
696
696
  } catch {
697
697
  return !1;
698
698
  }
699
- })().then((e) => (R.set(t, e), e));
700
- return Me.set(t, i), i;
699
+ })().then((e) => (B.set(t, e), e));
700
+ return z.set(t, i), i;
701
701
  }
702
- function Pe() {
703
- if (j()) for (let e of Object.values(je)) Ne(e);
702
+ function je() {
703
+ if (j()) for (let e of Object.values(R)) V(e);
704
704
  }
705
- function Fe(e) {
705
+ function H(e) {
706
706
  let t = I(e);
707
- if (t) return t === "avc" ? !0 : R.get(L(e)) ?? R.get(L(je[t]));
707
+ if (t) return t === "avc" ? !0 : B.get(L(e)) ?? B.get(L(R[t]));
708
708
  }
709
709
  //#endregion
710
710
  //#region src/core/drm/support.ts
711
- var Ie = 5e3, Le = "video/mp4; codecs=\"avc1.42E01E\"", Re = "audio/mp4; codecs=\"mp4a.40.2\"", ze = {
711
+ var Me = 5e3, U = "video/mp4; codecs=\"avc1.42E01E\"", Ne = "audio/mp4; codecs=\"mp4a.40.2\"", Pe = {
712
712
  widevine: ["com.widevine.alpha"],
713
713
  fairplay: ["com.apple.fps"]
714
714
  };
715
- function Be(e) {
715
+ function Fe(e) {
716
716
  return e === "fairplay" ? [{
717
717
  initDataTypes: [
718
718
  "sinf",
@@ -720,30 +720,30 @@ function Be(e) {
720
720
  "cenc"
721
721
  ],
722
722
  videoCapabilities: [{
723
- contentType: Le,
723
+ contentType: U,
724
724
  robustness: "",
725
725
  encryptionScheme: "cbcs"
726
726
  }],
727
727
  audioCapabilities: [{
728
- contentType: Re,
728
+ contentType: Ne,
729
729
  robustness: "",
730
730
  encryptionScheme: "cbcs"
731
731
  }]
732
732
  }] : N("com.widevine.alpha", [{
733
733
  initDataTypes: ["cenc"],
734
734
  videoCapabilities: [{
735
- contentType: Le,
735
+ contentType: U,
736
736
  robustness: F.video,
737
737
  encryptionScheme: F.scheme
738
738
  }],
739
739
  audioCapabilities: [{
740
- contentType: Re,
740
+ contentType: Ne,
741
741
  robustness: F.audio,
742
742
  encryptionScheme: F.scheme
743
743
  }]
744
744
  }]);
745
745
  }
746
- function Ve(e, t) {
746
+ function Ie(e, t) {
747
747
  return new Promise((n, r) => {
748
748
  let i = setTimeout(() => r(/* @__PURE__ */ Error("probe-timeout")), t);
749
749
  e.then((e) => {
@@ -753,8 +753,8 @@ function Ve(e, t) {
753
753
  });
754
754
  });
755
755
  }
756
- async function He(e = {}) {
757
- let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? Ie, r = {};
756
+ async function Le(e = {}) {
757
+ let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? Me, r = {};
758
758
  if (typeof window < "u" && window.isSecureContext === !1) return {
759
759
  verdict: "insecure-context",
760
760
  detail: r
@@ -766,7 +766,7 @@ async function He(e = {}) {
766
766
  };
767
767
  let a = !1, o = !1, s = Date.now() + n, c = () => Math.max(1, s - Date.now());
768
768
  for (let e of t) {
769
- let t = ze[e];
769
+ let t = Pe[e];
770
770
  if (!t) {
771
771
  r[e] = "unknown system";
772
772
  continue;
@@ -777,7 +777,7 @@ async function He(e = {}) {
777
777
  break;
778
778
  }
779
779
  try {
780
- return await Ve((await Ve(i(n, Be(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
780
+ return await Ie((await Ie(i(n, Fe(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
781
781
  verdict: "supported",
782
782
  system: e,
783
783
  detail: r
@@ -801,49 +801,49 @@ async function He(e = {}) {
801
801
  }
802
802
  //#endregion
803
803
  //#region src/core/manifest.ts
804
- var Ue = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
805
- function We(e) {
806
- return Ue.test(e);
804
+ var Re = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
805
+ function ze(e) {
806
+ return Re.test(e);
807
807
  }
808
808
  //#endregion
809
809
  //#region src/core/env.ts
810
- var z = {
810
+ var Be = {
811
811
  integration: "https://feed-dev.oddin-video.gg",
812
812
  production: "https://feed.oddin-video.gg"
813
813
  };
814
- function B(e) {
815
- let t = Object.prototype.hasOwnProperty.call(z, e) ? z[e] : void 0;
814
+ function Ve(e) {
815
+ let t = Object.prototype.hasOwnProperty.call(Be, e) ? Be[e] : void 0;
816
816
  if (!t) throw new n("INTERNAL", 0, `unknown env ${JSON.stringify(e)} — expected 'integration' or 'production'`);
817
817
  return t;
818
818
  }
819
819
  //#endregion
820
820
  //#region src/core/stats.ts
821
- var V = { droppedFrames: 0 };
822
- function H(e) {
821
+ var He = { droppedFrames: 0 };
822
+ function Ue(e) {
823
823
  try {
824
824
  return new URL(e).host;
825
825
  } catch {
826
826
  return "";
827
827
  }
828
828
  }
829
- var Ge = /\.(m3u8|ts|m4s|mp4|cmfv|cmfa)(\?|$)/i;
830
- function Ke(e) {
829
+ var We = /\.(m3u8|ts|m4s|mp4|cmfv|cmfa)(\?|$)/i;
830
+ function Ge(e) {
831
831
  if (typeof performance > "u" || typeof performance.getEntriesByType != "function") return "";
832
832
  let t = "", n = -1, r = performance.getEntriesByType("resource");
833
- for (let i of r) if (!(i.startTime < e) && (i.initiatorType === "video" || i.initiatorType === "audio" || Ge.test(i.name)) && i.startTime >= n) {
834
- let e = H(i.name);
833
+ for (let i of r) if (!(i.startTime < e) && (i.initiatorType === "video" || i.initiatorType === "audio" || We.test(i.name)) && i.startTime >= n) {
834
+ let e = Ue(i.name);
835
835
  e && (t = e, n = i.startTime);
836
836
  }
837
837
  return t;
838
838
  }
839
- function U(e) {
839
+ function W(e) {
840
840
  let t = e.getVideoPlaybackQuality?.();
841
841
  return {
842
842
  dropped: t?.droppedVideoFrames ?? 0,
843
843
  totalFrames: t?.totalVideoFrames ?? 0
844
844
  };
845
845
  }
846
- function qe(e, t) {
846
+ function Ke(e, t) {
847
847
  let n = [
848
848
  ["playing", () => t("playing")],
849
849
  ["waiting", () => t("buffering")],
@@ -860,7 +860,7 @@ function qe(e, t) {
860
860
  }
861
861
  //#endregion
862
862
  //#region src/core/engine/native.ts
863
- function Je(e) {
863
+ function qe(e) {
864
864
  try {
865
865
  let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
866
866
  return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
@@ -868,10 +868,10 @@ function Je(e) {
868
868
  return;
869
869
  }
870
870
  }
871
- function W(e) {
871
+ function G(e) {
872
872
  return !!(e ?? (typeof document < "u" ? document.createElement("video") : void 0))?.canPlayType("application/vnd.apple.mpegurl");
873
873
  }
874
- function G() {
874
+ function Je() {
875
875
  if (typeof window > "u") return !1;
876
876
  let e = "WebKitMediaKeys" in window || typeof HTMLMediaElement < "u" && "webkitSetMediaKeys" in HTMLMediaElement.prototype, t = typeof navigator < "u" && /Apple/i.test(navigator.vendor ?? "");
877
877
  return e || t;
@@ -934,7 +934,7 @@ var K = class {
934
934
  });
935
935
  this.onEncrypted = (n) => {
936
936
  let i = n;
937
- i.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = Je(i.initData), (async () => {
937
+ i.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = qe(i.initData), (async () => {
938
938
  try {
939
939
  let n = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
940
940
  initDataTypes: [i.initDataType],
@@ -1029,11 +1029,11 @@ var K = class {
1029
1029
  e.seekable.length > 0 && (e.currentTime = e.seekable.end(e.seekable.length - 1));
1030
1030
  }
1031
1031
  getStats() {
1032
- let e = this.deps.video, t = U(e), n = {
1032
+ let e = this.deps.video, t = W(e), n = {
1033
1033
  droppedFrames: t.dropped,
1034
1034
  decodedFrames: t.totalFrames
1035
1035
  };
1036
- return this.cdnHost ||= Ke(this.builtAt), this.cdnHost && (n.cdnHost = this.cdnHost), 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)) : Number.isFinite(e.duration) && e.duration > 0 && (n.isLive = !1), n;
1036
+ return this.cdnHost ||= Ge(this.builtAt), this.cdnHost && (n.cdnHost = this.cdnHost), 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)) : Number.isFinite(e.duration) && e.duration > 0 && (n.isLive = !1), n;
1037
1037
  }
1038
1038
  stop() {
1039
1039
  try {
@@ -1301,7 +1301,7 @@ var kt = class {
1301
1301
  this.deps = e, this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl, this.loadStarted = e.deferLoad !== !0;
1302
1302
  }
1303
1303
  async load() {
1304
- let { descriptor: e, snapToLiveOnRefocus: t, video: r, emit: i } = this.deps, a = W(r) && G(), o = !!e.drm?.widevine?.licenseUrl && !a;
1304
+ let { descriptor: e, snapToLiveOnRefocus: t, video: r, emit: i } = this.deps, a = G(r) && Je(), o = !!e.drm?.widevine?.licenseUrl && !a;
1305
1305
  if (e.drmEnabled && !o) {
1306
1306
  i({
1307
1307
  type: "error",
@@ -1310,7 +1310,7 @@ var kt = class {
1310
1310
  });
1311
1311
  return;
1312
1312
  }
1313
- e.drmEnabled && o && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && (Ae(r), Pe()), this.startHls(), t !== !1 && typeof document < "u" && (this.onVisibility = () => {
1313
+ e.drmEnabled && o && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && (Ae(r), je()), this.startHls(), t !== !1 && typeof document < "u" && (this.onVisibility = () => {
1314
1314
  let e = this.hls;
1315
1315
  if (document.visibilityState !== "visible" || !e) return;
1316
1316
  let t = e.liveSyncPosition;
@@ -1430,7 +1430,7 @@ var kt = class {
1430
1430
  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 > Et && this.requestTimesMs.shift());
1431
1431
  let a = t?.frag?.url;
1432
1432
  if (a) {
1433
- let e = H(a);
1433
+ let e = Ue(a);
1434
1434
  e && (this.cdnHost = e);
1435
1435
  }
1436
1436
  }), f.on(e.Events.LEVEL_LOADED, (e, t) => {
@@ -1526,9 +1526,9 @@ var kt = class {
1526
1526
  this.ladderHeights = t.length ? [...new Set(t)].sort((e, t) => e - t) : void 0, this.ladderTopBitrateKbps = e.levels.length ? Math.round(Math.max(...e.levels.map((e) => e.bitrate)) / 1e3) : 0;
1527
1527
  }
1528
1528
  dropLevelsWidevineCannotPlay(e) {
1529
- this.removeLevelsWhere(e, (e) => Fe(e) !== !0);
1529
+ this.removeLevelsWhere(e, (e) => H(e) !== !0);
1530
1530
  let t = new Set(e.levels.map((e) => e.videoCodec).filter((e) => !!e && I(e) !== "avc" && !!I(e)));
1531
- for (let n of t) Ne(n).then((t) => {
1531
+ for (let n of t) V(n).then((t) => {
1532
1532
  t || this.hls !== e || this.removeLevelsWhere(e, (e) => e === n) && (this.measureLadder(e), this.applySizeCap());
1533
1533
  });
1534
1534
  }
@@ -1539,7 +1539,7 @@ var kt = class {
1539
1539
  a && I(a) && t(a) && (r.push(n), i.add(a));
1540
1540
  }), !r.length) return !1;
1541
1541
  let a = [...i].join(", ");
1542
- if (!n.some((e, t) => r.includes(t) ? !1 : !I(e.videoCodec) || Fe(e.videoCodec) === !0)) return console.warn(`havik-player: every rendition is ${a}, which Widevine may not play on this browser; keeping them`), !1;
1542
+ if (!n.some((e, t) => r.includes(t) ? !1 : !I(e.videoCodec) || H(e.videoCodec) === !0)) return console.warn(`havik-player: every rendition is ${a}, which Widevine may not play on this browser; keeping them`), !1;
1543
1543
  for (let t = r.length - 1; t >= 0; --t) e.removeLevel(r[t]);
1544
1544
  return console.info(`havik-player: skipped ${r.length} rendition(s) Widevine cannot play on this browser (${a})`), !0;
1545
1545
  }
@@ -1585,7 +1585,7 @@ var kt = class {
1585
1585
  e.widevine && (this.licenseUrl = e.widevine);
1586
1586
  }
1587
1587
  getStats() {
1588
- let e = this.hls, t = U(this.deps.video), n = {
1588
+ let e = this.hls, t = W(this.deps.video), n = {
1589
1589
  droppedFrames: t.dropped,
1590
1590
  decodedFrames: t.totalFrames,
1591
1591
  lowLatency: this.detectedLowLatency,
@@ -1646,7 +1646,7 @@ var kt = class {
1646
1646
  this.resetStarvationWindow();
1647
1647
  return;
1648
1648
  }
1649
- let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < ft, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > mt, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = U(t), c = o - s, l = c !== this.starvedFrames;
1649
+ let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < ft, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > mt, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = W(t), c = o - s, l = c !== this.starvedFrames;
1650
1650
  if (this.starvedFrames = c, !a || l) {
1651
1651
  (t.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA || l) && this.clearKick(), this.resetStarvationWindow();
1652
1652
  return;
@@ -2260,7 +2260,7 @@ async function $t() {
2260
2260
  }
2261
2261
  //#endregion
2262
2262
  //#region src/core/version.ts
2263
- var en = "2.13.1", tn = {
2263
+ var en = "2.15.0", tn = {
2264
2264
  accent: "#E1B600",
2265
2265
  accentText: "#1B1C23",
2266
2266
  background: "#0e0f13",
@@ -2293,15 +2293,16 @@ function rn(e, t) {
2293
2293
  }
2294
2294
  //#endregion
2295
2295
  //#region src/ui/styles.ts
2296
- var an = "havik-player-styles", on = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n/* Absolutely centred, NOT a flex sibling of .havik-center: the hidden center\n button (.havik-gone) still occupies its 74px in the row, which pushed an\n in-flow spinner 37px right of centre. */\n.havik-spinner{position:absolute;inset:0;margin:auto;width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n";
2297
- function sn(e = document) {
2298
- if (e.getElementById(an)) return;
2299
- let t = e.createElement("style");
2300
- t.id = an, t.textContent = on, e.head.appendChild(t);
2296
+ var an = "havik-player-styles", on = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer;\n touch-action:manipulation}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n/* Absolutely centred, NOT a flex sibling of .havik-center: the hidden center\n button (.havik-gone) still occupies its 74px in the row, which pushed an\n in-flow spinner 37px right of centre. */\n.havik-spinner{position:absolute;inset:0;margin:auto;width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n", sn = /* @__PURE__ */ new WeakMap();
2297
+ function cn(e = document, t) {
2298
+ let n = e.querySelector(`style#${an}`);
2299
+ if (n && (!t || sn.get(n) === t)) return;
2300
+ let r = e.createElement("style");
2301
+ r.id = an, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = on, sn.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
2301
2302
  }
2302
2303
  //#endregion
2303
2304
  //#region src/ui/icons.ts
2304
- var cn = {
2305
+ var ln = {
2305
2306
  play: "<path d=\"M8 5v14l11-7z\"/>",
2306
2307
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
2307
2308
  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\"/>",
@@ -2315,20 +2316,20 @@ var cn = {
2315
2316
  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\"/>"
2316
2317
  };
2317
2318
  function Z(e) {
2318
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${cn[e]}</svg>`;
2319
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${ln[e]}</svg>`;
2319
2320
  }
2320
2321
  //#endregion
2321
2322
  //#region src/ui/controls.ts
2322
- var ln = 3e3, un = /* @__PURE__ */ new Set([
2323
+ var un = 3e3, dn = 250, fn = /* @__PURE__ */ new Set([
2323
2324
  "GONE",
2324
2325
  "NOT_FOUND",
2325
2326
  "INVALID_URN",
2326
2327
  "UNAUTHORIZED"
2327
2328
  ]);
2328
- function dn(e) {
2329
- return new fn(e);
2329
+ function pn(e) {
2330
+ return new mn(e);
2330
2331
  }
2331
- var fn = class {
2332
+ var mn = class {
2332
2333
  root;
2333
2334
  video;
2334
2335
  player;
@@ -2366,11 +2367,15 @@ var fn = class {
2366
2367
  interruptedMessage;
2367
2368
  errorMessage;
2368
2369
  hideTimer;
2370
+ tapTimer;
2371
+ surfacePointerType = "";
2372
+ surfaceWasHidden = !1;
2373
+ pointerClosedMenu = !1;
2369
2374
  openMenu = null;
2370
2375
  scrubbing = !1;
2371
2376
  offFns = [];
2372
2377
  constructor(e) {
2373
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? ln, 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, sn(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");
2378
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? un, 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, cn(this.doc, e.cspNonce), this.root.classList.add("havik-player"), this.root.hasAttribute("tabindex") || (this.root.tabIndex = 0), this.root.setAttribute("role", "region"), this.root.setAttribute("aria-label", "Video player");
2374
2379
  let t = (e, t, n) => {
2375
2380
  let r = this.doc.createElement(e);
2376
2381
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -2419,18 +2424,48 @@ var fn = class {
2419
2424
  }
2420
2425
  wire() {
2421
2426
  let e = this.video;
2422
- this.on(this.playBtn, "click", () => this.togglePlay()), this.on(this.center, "click", () => this.togglePlay()), this.on(this.overlay, "click", (e) => {
2423
- e.target === this.overlay && this.togglePlay();
2427
+ this.on(this.playBtn, "click", () => this.togglePlay()), this.on(this.center, "click", () => this.togglePlay()), this.on(this.overlay, "pointerdown", (e) => {
2428
+ this.surfacePointerType = e.pointerType ?? "", this.surfaceWasHidden = this.root.classList.contains("havik-hide");
2429
+ }), this.on(this.overlay, "click", (e) => {
2430
+ e.target === this.overlay && this.onSurfaceClick();
2424
2431
  }), this.on(this.muteBtn, "click", () => this.player.setMuted(!e.muted)), this.on(this.volSlider, "input", () => {
2425
2432
  let t = Number(this.volSlider.value);
2426
2433
  this.setVolumeFill(t), this.player.setVolume(t), t > 0 && e.muted && this.player.setMuted(!1);
2427
2434
  }), this.on(this.liveGo, "click", () => this.player.seekToLive()), this.on(this.pipBtn, "click", () => void this.togglePip()), this.on(this.fsBtn, "click", () => void this.toggleFullscreen()), this.on(this.ccBtn, "click", () => this.toggleMenu("cc")), this.on(this.audioBtn, "click", () => this.toggleMenu("audio")), this.on(this.qualityBtn, "click", () => this.toggleMenu("quality")), this.on(this.seek, "pointerdown", (e) => this.onSeekDown(e)), this.on(this.seek, "keydown", (e) => this.onSeekKey(e)), this.on(e, "play", () => this.syncPlayPause()), this.on(e, "pause", () => this.syncPlayPause()), this.on(e, "volumechange", () => this.syncVolume()), this.on(e, "timeupdate", () => this.syncProgress()), this.on(e, "progress", () => this.syncProgress()), this.on(e, "durationchange", () => this.syncProgress()), this.offFns.push(this.player.on("statechange", () => this.syncState())), this.offFns.push(this.player.on("ended", () => this.syncState())), this.offFns.push(this.player.on("ready", () => this.refreshTracks())), this.offFns.push(this.player.on("qualitychange", () => this.onTracksChanged())), this.offFns.push(this.player.on("audiotrackchange", () => this.onTracksChanged())), this.offFns.push(this.player.on("texttrackchange", () => this.onTracksChanged())), this.offFns.push(this.player.on("pipchange", (e) => this.syncPip(e))), this.offFns.push(this.player.on("stats", () => this.syncLive())), this.offFns.push(this.player.on("error", (e) => {
2428
2435
  this.lastError = e, this.statusKind = null, this.syncState();
2429
- })), this.on(this.root, "pointermove", () => this.poke()), this.on(this.root, "pointerleave", () => this.scheduleHide(0)), this.on(this.root, "focusin", () => this.poke()), this.on(this.root, "keydown", (e) => this.onKey(e)), this.on(this.doc, "pointerdown", (e) => this.onDocPointer(e), { capture: !0 }), this.on(this.doc, "fullscreenchange", () => this.syncFullscreen()), this.on(this.doc, "webkitfullscreenchange", () => this.syncFullscreen()), this.on(e, "webkitbeginfullscreen", () => this.syncFullscreen()), this.on(e, "webkitendfullscreen", () => this.syncFullscreen()), this.syncState();
2436
+ }));
2437
+ let t = (e) => e.pointerType !== "touch";
2438
+ this.on(this.root, "pointermove", (e) => {
2439
+ t(e) && this.poke();
2440
+ }), this.on(this.root, "pointerleave", (e) => {
2441
+ t(e) && this.scheduleHide(0);
2442
+ }), this.on(this.root, "focusin", () => this.poke()), this.on(this.root, "keydown", (e) => this.onKey(e)), this.on(this.doc, "pointerdown", (e) => this.onDocPointer(e), { capture: !0 }), this.on(this.doc, "fullscreenchange", () => this.syncFullscreen()), this.on(this.doc, "webkitfullscreenchange", () => this.syncFullscreen()), this.on(e, "webkitbeginfullscreen", () => this.syncFullscreen()), this.on(e, "webkitendfullscreen", () => this.syncFullscreen()), this.syncState();
2430
2443
  }
2431
2444
  togglePlay() {
2432
2445
  this.video.paused || this.video.ended ? this.player.play() : this.player.pause();
2433
2446
  }
2447
+ onSurfaceClick() {
2448
+ let e = this.surfacePointerType === "touch";
2449
+ if (this.surfacePointerType = "", !e) {
2450
+ this.tapTimer && clearTimeout(this.tapTimer), this.tapTimer = void 0, this.togglePlay();
2451
+ return;
2452
+ }
2453
+ if (this.pointerClosedMenu) {
2454
+ this.tapTimer && clearTimeout(this.tapTimer), this.tapTimer = void 0, this.poke();
2455
+ return;
2456
+ }
2457
+ if (this.tapTimer) {
2458
+ clearTimeout(this.tapTimer), this.tapTimer = void 0, this.togglePlay();
2459
+ return;
2460
+ }
2461
+ let t = this.surfaceWasHidden;
2462
+ this.tapTimer = setTimeout(() => {
2463
+ this.tapTimer = void 0, this.toggleBar(t);
2464
+ }, dn);
2465
+ }
2466
+ toggleBar(e) {
2467
+ e ? this.poke() : this.hideIfIdle();
2468
+ }
2434
2469
  syncPlayPause() {
2435
2470
  let e = !this.video.paused && !this.video.ended;
2436
2471
  this.setIcon(this.playBtn, e ? "pause" : "play"), this.playBtn.setAttribute("aria-label", e ? "Pause" : "Play"), this.setIcon(this.center, e ? "pause" : "play"), this.center.classList.toggle("havik-gone", e || !!this.statusBox), e ? this.scheduleHide() : this.show();
@@ -2461,7 +2496,7 @@ var fn = class {
2461
2496
  e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = Z("play"), e.addEventListener("click", () => void this.player.play()), n.append(e);
2462
2497
  } else {
2463
2498
  let r = this.doc.createElement("div");
2464
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !un.has(this.lastError.code)) {
2499
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !fn.has(this.lastError.code)) {
2465
2500
  let e = this.doc.createElement("button");
2466
2501
  e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${Z("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), n.append(e);
2467
2502
  }
@@ -2502,7 +2537,7 @@ var fn = class {
2502
2537
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
2503
2538
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
2504
2539
  }
2505
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Q(this.video.currentTime)} / ${Q(this.video.duration)}`);
2540
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${hn(this.video.currentTime)} / ${hn(this.video.duration)}`);
2506
2541
  }
2507
2542
  syncLive() {
2508
2543
  let e = this.player.getStats(), t = e.isLive !== !1;
@@ -2552,17 +2587,17 @@ var fn = class {
2552
2587
  renderQualityMenu() {
2553
2588
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
2554
2589
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
2555
- for (let n of e) this.menu.append(this.menuItem(pn(n), t === n.index, () => this.player.setQuality(n.index)));
2590
+ for (let n of e) this.menu.append(this.menuItem(gn(n), t === n.index, () => this.player.setQuality(n.index)));
2556
2591
  }
2557
2592
  renderAudioMenu() {
2558
2593
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
2559
2594
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
2560
- for (let t of e) this.menu.append(this.menuItem(mn(t), t.default, () => this.player.setAudioTrack(t.id)));
2595
+ for (let t of e) this.menu.append(this.menuItem(_n(t), t.default, () => this.player.setAudioTrack(t.id)));
2561
2596
  }
2562
2597
  renderCcMenu() {
2563
2598
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
2564
2599
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
2565
- for (let t of e) this.menu.append(this.menuItem(hn(t), !1, () => this.player.setTextTrack(t.id)));
2600
+ for (let t of e) this.menu.append(this.menuItem(vn(t), !1, () => this.player.setTextTrack(t.id)));
2566
2601
  }
2567
2602
  onTracksChanged() {
2568
2603
  this.openMenu && this.renderMenu(this.openMenu);
@@ -2596,16 +2631,17 @@ var fn = class {
2596
2631
  this.root.classList.remove("havik-hide");
2597
2632
  }
2598
2633
  scheduleHide(e = this.autoHideMs) {
2599
- this.hideTimer && clearTimeout(this.hideTimer), this.hideTimer = setTimeout(() => {
2600
- !this.video.paused && !this.video.ended && !this.openMenu && !this.statusBox && this.root.classList.add("havik-hide");
2601
- }, e);
2634
+ this.hideTimer && clearTimeout(this.hideTimer), this.hideTimer = setTimeout(() => this.hideIfIdle(), e);
2635
+ }
2636
+ hideIfIdle() {
2637
+ !this.video.paused && !this.video.ended && !this.openMenu && !this.statusBox && this.root.classList.add("havik-hide");
2602
2638
  }
2603
2639
  onDocPointer(e) {
2604
- this.openMenu && !this.menu.contains(e.target) && ([
2640
+ this.pointerClosedMenu = !1, this.openMenu && !this.menu.contains(e.target) && ([
2605
2641
  this.qualityBtn,
2606
2642
  this.audioBtn,
2607
2643
  this.ccBtn
2608
- ].some((t) => t.contains(e.target)) || this.closeMenu());
2644
+ ].some((t) => t.contains(e.target)) || (this.closeMenu(), this.pointerClosedMenu = !0));
2609
2645
  }
2610
2646
  onKey(e) {
2611
2647
  let t = e.target;
@@ -2648,7 +2684,7 @@ var fn = class {
2648
2684
  e.innerHTML = Z(t);
2649
2685
  }
2650
2686
  destroy() {
2651
- this.hideTimer && clearTimeout(this.hideTimer);
2687
+ this.hideTimer && clearTimeout(this.hideTimer), this.tapTimer && clearTimeout(this.tapTimer);
2652
2688
  for (let e of this.offFns) try {
2653
2689
  e();
2654
2690
  } catch {}
@@ -2662,41 +2698,41 @@ var fn = class {
2662
2698
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
2663
2699
  }
2664
2700
  };
2665
- function Q(e) {
2701
+ function hn(e) {
2666
2702
  (!Number.isFinite(e) || e < 0) && (e = 0);
2667
2703
  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");
2668
2704
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
2669
2705
  }
2670
- function pn(e) {
2706
+ function gn(e) {
2671
2707
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
2672
2708
  }
2673
- function mn(e) {
2709
+ function _n(e) {
2674
2710
  return e.name || e.lang || `Track ${e.id}`;
2675
2711
  }
2676
- function hn(e) {
2712
+ function vn(e) {
2677
2713
  return e.name || e.lang || `Track ${e.id}`;
2678
2714
  }
2679
2715
  //#endregion
2680
2716
  //#region src/managed/index.ts
2681
- var gn = 6e3, _n = 45e3, vn = 2e3, yn = 3, bn = 2e3, xn = 48e4;
2682
- function Sn(e) {
2717
+ var yn = 6e3, bn = 45e3, Q = 2e3, xn = 3, Sn = 2e3, Cn = 48e4;
2718
+ function wn(e) {
2683
2719
  if (e.env !== void 0 && e.baseUrl !== void 0) throw new n("INTERNAL", 0, `createPlayer: pass env OR baseUrl, not both (got env=${JSON.stringify(e.env)} and baseUrl=${JSON.stringify(e.baseUrl)})`);
2684
- if (e.env !== void 0) return B(e.env);
2720
+ if (e.env !== void 0) return Ve(e.env);
2685
2721
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
2686
2722
  return e.baseUrl;
2687
2723
  }
2688
- async function Cn(e) {
2724
+ async function Tn(e) {
2689
2725
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
2690
- let t = Sn(e);
2726
+ let t = wn(e);
2691
2727
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
2692
2728
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
2693
- let r = new wn({
2729
+ let r = new En({
2694
2730
  ...e,
2695
2731
  baseUrl: t
2696
2732
  });
2697
2733
  return await r.start(), r;
2698
2734
  }
2699
- var wn = class {
2735
+ var En = class {
2700
2736
  opts;
2701
2737
  abort = new AbortController();
2702
2738
  listeners = {};
@@ -2740,7 +2776,7 @@ var wn = class {
2740
2776
  this.opts.muted && (this.opts.video.muted = !0), this.opts.poster && (this.opts.video.poster = this.opts.poster), this.mountControls(), this.attachPipListeners(), this.setState("loading");
2741
2777
  let e = ++this.loadGen, t = this.resolveOptions();
2742
2778
  try {
2743
- let n = await v(t);
2779
+ let n = await y(t);
2744
2780
  if (await this.onResolved(n, e), e === this.loadGen && this._state === "error" && this._lastError) {
2745
2781
  let e = this._lastError;
2746
2782
  throw this.destroy(), e;
@@ -2767,7 +2803,7 @@ var wn = class {
2767
2803
  r ? this.armWithSse(e, r, t, n) : this.beginArmingByPolling(e);
2768
2804
  }
2769
2805
  beginArmingByPolling(e) {
2770
- y({
2806
+ b({
2771
2807
  ...this.resolveOptions(),
2772
2808
  signal: this.abort.signal,
2773
2809
  waitForLive: this.buildWaitConfig(e)
@@ -2784,7 +2820,7 @@ var wn = class {
2784
2820
  setTimeout(() => {
2785
2821
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
2786
2822
  phase: i ? "unavailable" : "tooEarly",
2787
- retryInMs: _n,
2823
+ retryInMs: bn,
2788
2824
  attempt: 1,
2789
2825
  elapsedMs: 0,
2790
2826
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -2801,7 +2837,7 @@ var wn = class {
2801
2837
  }
2802
2838
  }
2803
2839
  }, h = () => {
2804
- a || s || p() || (s = !0, y({
2840
+ a || s || p() || (s = !0, b({
2805
2841
  ...this.resolveOptions(),
2806
2842
  signal: c.signal,
2807
2843
  waitForLive: this.buildWaitConfig(e)
@@ -2809,7 +2845,7 @@ var wn = class {
2809
2845
  a || p() || c.signal.aborted || (a = !0, this.closeArming(), this.fail(e instanceof n ? e : new n("INTERNAL", 0, String(e))));
2810
2846
  }));
2811
2847
  }, g = () => {
2812
- a || p() || v(this.resolveOptions()).then((e) => m(e)).catch((e) => {
2848
+ a || p() || y(this.resolveOptions()).then((e) => m(e)).catch((e) => {
2813
2849
  if (!(a || p())) {
2814
2850
  if (e instanceof n && o(e.code)) {
2815
2851
  h();
@@ -2823,13 +2859,13 @@ var wn = class {
2823
2859
  !a && !p() && h();
2824
2860
  }, e);
2825
2861
  };
2826
- _(gn), this.armingSub = T({
2862
+ _(yn), this.armingSub = T({
2827
2863
  eventsBaseUrl: t,
2828
2864
  matchUrn: this.opts.matchUrn,
2829
2865
  credential: this.opts.credential,
2830
- onAlive: () => _(_n),
2866
+ onAlive: () => _(bn),
2831
2867
  onState: (e) => {
2832
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * vn);
2868
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Q);
2833
2869
  },
2834
2870
  onError: () => h()
2835
2871
  });
@@ -2852,7 +2888,7 @@ var wn = class {
2852
2888
  let n = await s(this.opts.credential);
2853
2889
  if (this.destroyed || t !== this.loadGen) return;
2854
2890
  this.setState("loading");
2855
- let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = qe(this.opts.video, (e) => {
2891
+ let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = Ke(this.opts.video, (e) => {
2856
2892
  this._state !== "ended" && this._state !== "error" && this.setState(e);
2857
2893
  });
2858
2894
  if (await i.load(), this.destroyed || t !== this.loadGen) {
@@ -2893,7 +2929,7 @@ var wn = class {
2893
2929
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, e));
2894
2930
  }, 0),
2895
2931
  sample: () => {
2896
- let e = this.engine?.getStats() ?? V, t = c.buffered.length ? Math.max(0, c.buffered.end(c.buffered.length - 1) - c.currentTime) : 0;
2932
+ let e = this.engine?.getStats() ?? He, t = c.buffered.length ? Math.max(0, c.buffered.end(c.buffered.length - 1) - c.currentTime) : 0;
2897
2933
  return {
2898
2934
  ...e,
2899
2935
  bufferedMs: Math.round(t * 1e3),
@@ -2934,9 +2970,9 @@ var wn = class {
2934
2970
  }, o = this.opts.engine ?? "auto";
2935
2971
  if (o === "native") return new K(a);
2936
2972
  if (o === "hls") return new kt(a);
2937
- if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && W(this.opts.video) && G()) return new K(a);
2973
+ if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && G(this.opts.video) && Je()) return new K(a);
2938
2974
  if (at()) return new kt(a);
2939
- if (W(this.opts.video)) return new K(a);
2975
+ if (G(this.opts.video)) return new K(a);
2940
2976
  throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
2941
2977
  }
2942
2978
  onEngineEvent(e) {
@@ -2988,7 +3024,7 @@ var wn = class {
2988
3024
  r = 0, i = !1;
2989
3025
  },
2990
3026
  onError: () => {
2991
- this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= yn && (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.`))));
3027
+ this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= xn && (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.`))));
2992
3028
  }
2993
3029
  });
2994
3030
  }
@@ -3010,7 +3046,7 @@ var wn = class {
3010
3046
  scheduleRejoin(e) {
3011
3047
  this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
3012
3048
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
3013
- }, Math.random() * vn));
3049
+ }, Math.random() * Q));
3014
3050
  }
3015
3051
  cancelRejoin() {
3016
3052
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -3034,7 +3070,7 @@ var wn = class {
3034
3070
  }
3035
3071
  t.controls = !1;
3036
3072
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
3037
- r && r.insertBefore(n, t), n.appendChild(t), rn(n, this.opts.theme), this.container = n, this.controlBar = dn({
3073
+ r && r.insertBefore(n, t), n.appendChild(t), rn(n, this.opts.theme), this.container = n, this.controlBar = pn({
3038
3074
  root: n,
3039
3075
  video: t,
3040
3076
  player: this,
@@ -3042,7 +3078,8 @@ var wn = class {
3042
3078
  statusOverlays: this.opts.statusOverlays,
3043
3079
  endedMessage: this.opts.endedMessage,
3044
3080
  interruptedMessage: this.opts.interruptedMessage,
3045
- errorMessage: this.opts.errorMessage
3081
+ errorMessage: this.opts.errorMessage,
3082
+ cspNonce: this.opts.cspNonce
3046
3083
  });
3047
3084
  }
3048
3085
  unmountControls() {
@@ -3064,7 +3101,7 @@ var wn = class {
3064
3101
  }
3065
3102
  startStats() {
3066
3103
  this.stopStats();
3067
- let e = Math.max(500, this.opts.statsIntervalMs ?? bn);
3104
+ let e = Math.max(500, this.opts.statsIntervalMs ?? Sn);
3068
3105
  this.statsTimer = setInterval(() => {
3069
3106
  if (!this.engine) return;
3070
3107
  let e = this.engine.getStats();
@@ -3076,7 +3113,7 @@ var wn = class {
3076
3113
  }
3077
3114
  scheduleLicenseRefresh() {
3078
3115
  this.stopRefresh();
3079
- let e = this.opts.licenseRefreshMs ?? xn;
3116
+ let e = this.opts.licenseRefreshMs ?? Cn;
3080
3117
  e && this._descriptor?.drmEnabled && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
3081
3118
  }
3082
3119
  stopRefresh() {
@@ -3085,7 +3122,7 @@ var wn = class {
3085
3122
  async refreshLicense() {
3086
3123
  let e = this.engine;
3087
3124
  if (!this.destroyed && e) try {
3088
- let t = await v(this.resolveOptions());
3125
+ let t = await y(this.resolveOptions());
3089
3126
  if (this.destroyed || this.engine !== e) return;
3090
3127
  let n = t.drm?.widevine?.licenseUrl, r = t.drm?.fairplay?.licenseUrl;
3091
3128
  (n || r) && (e.setLicenseUrls({
@@ -3121,7 +3158,7 @@ var wn = class {
3121
3158
  this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
3122
3159
  }
3123
3160
  getStats() {
3124
- return this.engine?.getStats() ?? V;
3161
+ return this.engine?.getStats() ?? He;
3125
3162
  }
3126
3163
  getQualityLevels() {
3127
3164
  return this.engine?.getQualityLevels() ?? [];
@@ -3173,7 +3210,7 @@ var wn = class {
3173
3210
  let e = ++this.loadGen;
3174
3211
  this.teardownPlayback();
3175
3212
  try {
3176
- let t = await y({
3213
+ let t = await b({
3177
3214
  ...this.resolveOptions(),
3178
3215
  waitForLive: this.opts.waitForLive ? this.buildWaitConfig(e) : void 0
3179
3216
  });
@@ -3231,7 +3268,7 @@ function $(e) {
3231
3268
  }
3232
3269
  //#endregion
3233
3270
  //#region src/embed/host.ts
3234
- function Tn(e) {
3271
+ function Dn(e) {
3235
3272
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
3236
3273
  if (!e.src) throw Error("mountEmbed: opts.src is required");
3237
3274
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -3290,15 +3327,15 @@ function Tn(e) {
3290
3327
  }
3291
3328
  //#endregion
3292
3329
  //#region src/embed/page.ts
3293
- var En = /^[A-Za-z0-9:_.-]{1,128}$/;
3294
- function Dn(e) {
3330
+ var On = /^[A-Za-z0-9:_.-]{1,128}$/;
3331
+ function kn(e) {
3295
3332
  if (typeof e == "boolean") return e;
3296
3333
  if (typeof e == "object" && e) {
3297
3334
  let t = e.endpoint;
3298
3335
  return typeof t == "string" ? { endpoint: t } : {};
3299
3336
  }
3300
3337
  }
3301
- function On() {
3338
+ function An() {
3302
3339
  let e = window.HAVIK_EMBED_CONFIG;
3303
3340
  if (e) return {
3304
3341
  baseUrl: e.baseUrl ?? "",
@@ -3308,7 +3345,7 @@ function On() {
3308
3345
  autoplay: e.autoplay ?? !0,
3309
3346
  muted: e.muted ?? !0,
3310
3347
  waitForLive: e.waitForLive ?? !0,
3311
- analytics: Dn(e.analytics)
3348
+ analytics: kn(e.analytics)
3312
3349
  };
3313
3350
  let t = new URLSearchParams(location.search);
3314
3351
  return {
@@ -3321,9 +3358,9 @@ function On() {
3321
3358
  waitForLive: !0
3322
3359
  };
3323
3360
  }
3324
- async function kn() {
3325
- let e = On();
3326
- jn();
3361
+ async function jn() {
3362
+ let e = An();
3363
+ Nn();
3327
3364
  let t = document.createElement("video");
3328
3365
  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);
3329
3366
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -3378,12 +3415,12 @@ async function kn() {
3378
3415
  code: e.code,
3379
3416
  message: e.message
3380
3417
  })), e.on("autoplayblocked", () => {
3381
- i({ type: "oddin:autoplayblocked" }), An(t, e);
3418
+ i({ type: "oddin:autoplayblocked" }), Mn(t, e);
3382
3419
  });
3383
3420
  }, s = async (r) => {
3384
3421
  a?.destroy(), a = null;
3385
3422
  try {
3386
- let n = await Cn({
3423
+ let n = await Tn({
3387
3424
  video: t,
3388
3425
  baseUrl: e.baseUrl,
3389
3426
  matchUrn: r,
@@ -3445,7 +3482,7 @@ async function kn() {
3445
3482
  a?.retry();
3446
3483
  break;
3447
3484
  case "oddin:load":
3448
- typeof e.matchUrn == "string" && En.test(e.matchUrn) && s(e.matchUrn);
3485
+ typeof e.matchUrn == "string" && On.test(e.matchUrn) && s(e.matchUrn);
3449
3486
  break;
3450
3487
  case "oddin:destroy": c();
3451
3488
  }
@@ -3457,18 +3494,18 @@ async function kn() {
3457
3494
  protocol: 1
3458
3495
  });
3459
3496
  }
3460
- function An(e, t) {
3497
+ function Mn(e, t) {
3461
3498
  if (document.getElementById("havik-tap")) return;
3462
3499
  let n = document.createElement("button");
3463
3500
  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", () => {
3464
3501
  n.remove(), t.play();
3465
3502
  }), document.body.appendChild(n);
3466
3503
  }
3467
- function jn() {
3504
+ function Nn() {
3468
3505
  let e = document.createElement("style");
3469
3506
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
3470
3507
  }
3471
3508
  //#endregion
3472
- export { tn as ODDIN_THEME, n as PlaybackError, rn as applyTheme, B as baseUrlForEnv, kn as bootEmbed, Te as clearDeviceId, Cn as createPlayer, E as deriveEventsBaseUrl, He as detectDrmSupport, C as enterVideoFullscreen, w as exitVideoFullscreen, _e as fetchCatalog, ye as fetchTournament, xe as flattenMatches, k as getDeviceId, We as isLowLatencyManifest, $ as isOddinMessage, De as licenseRequestHeaders, Tn as mountEmbed, de as pollToLive, y as resolveStream, T as subscribeLiveState, S as videoFullscreenActive, Ce as watchStatus };
3509
+ export { tn as ODDIN_THEME, n as PlaybackError, rn as applyTheme, Ve as baseUrlForEnv, jn as bootEmbed, Te as clearDeviceId, Tn as createPlayer, E as deriveEventsBaseUrl, Le as detectDrmSupport, C as enterVideoFullscreen, w as exitVideoFullscreen, _e as fetchCatalog, ye as fetchTournament, xe as flattenMatches, k as getDeviceId, ze as isLowLatencyManifest, $ as isOddinMessage, De as licenseRequestHeaders, Dn as mountEmbed, v as pollToLive, b as resolveStream, T as subscribeLiveState, S as videoFullscreenActive, Ce as watchStatus };
3473
3510
 
3474
3511
  //# sourceMappingURL=index.js.map