@oddin-gg/havik-player 2.14.0 → 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.14.0", tn = {
2263
+ var en = "2.15.0", tn = {
2264
2264
  accent: "#E1B600",
2265
2265
  accentText: "#1B1C23",
2266
2266
  background: "#0e0f13",
@@ -2293,7 +2293,7 @@ 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", sn = /* @__PURE__ */ new WeakMap();
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
2297
  function cn(e = document, t) {
2298
2298
  let n = e.querySelector(`style#${an}`);
2299
2299
  if (n && (!t || sn.get(n) === t)) return;
@@ -2320,16 +2320,16 @@ function Z(e) {
2320
2320
  }
2321
2321
  //#endregion
2322
2322
  //#region src/ui/controls.ts
2323
- var un = 3e3, dn = /* @__PURE__ */ new Set([
2323
+ var un = 3e3, dn = 250, fn = /* @__PURE__ */ new Set([
2324
2324
  "GONE",
2325
2325
  "NOT_FOUND",
2326
2326
  "INVALID_URN",
2327
2327
  "UNAUTHORIZED"
2328
2328
  ]);
2329
- function fn(e) {
2330
- return new pn(e);
2329
+ function pn(e) {
2330
+ return new mn(e);
2331
2331
  }
2332
- var pn = class {
2332
+ var mn = class {
2333
2333
  root;
2334
2334
  video;
2335
2335
  player;
@@ -2367,6 +2367,10 @@ var pn = class {
2367
2367
  interruptedMessage;
2368
2368
  errorMessage;
2369
2369
  hideTimer;
2370
+ tapTimer;
2371
+ surfacePointerType = "";
2372
+ surfaceWasHidden = !1;
2373
+ pointerClosedMenu = !1;
2370
2374
  openMenu = null;
2371
2375
  scrubbing = !1;
2372
2376
  offFns = [];
@@ -2420,18 +2424,48 @@ var pn = class {
2420
2424
  }
2421
2425
  wire() {
2422
2426
  let e = this.video;
2423
- this.on(this.playBtn, "click", () => this.togglePlay()), this.on(this.center, "click", () => this.togglePlay()), this.on(this.overlay, "click", (e) => {
2424
- 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();
2425
2431
  }), this.on(this.muteBtn, "click", () => this.player.setMuted(!e.muted)), this.on(this.volSlider, "input", () => {
2426
2432
  let t = Number(this.volSlider.value);
2427
2433
  this.setVolumeFill(t), this.player.setVolume(t), t > 0 && e.muted && this.player.setMuted(!1);
2428
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) => {
2429
2435
  this.lastError = e, this.statusKind = null, this.syncState();
2430
- })), 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();
2431
2443
  }
2432
2444
  togglePlay() {
2433
2445
  this.video.paused || this.video.ended ? this.player.play() : this.player.pause();
2434
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
+ }
2435
2469
  syncPlayPause() {
2436
2470
  let e = !this.video.paused && !this.video.ended;
2437
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();
@@ -2462,7 +2496,7 @@ var pn = class {
2462
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);
2463
2497
  } else {
2464
2498
  let r = this.doc.createElement("div");
2465
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !dn.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)) {
2466
2500
  let e = this.doc.createElement("button");
2467
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);
2468
2502
  }
@@ -2503,7 +2537,7 @@ var pn = class {
2503
2537
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
2504
2538
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
2505
2539
  }
2506
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${mn(this.video.currentTime)} / ${mn(this.video.duration)}`);
2540
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${hn(this.video.currentTime)} / ${hn(this.video.duration)}`);
2507
2541
  }
2508
2542
  syncLive() {
2509
2543
  let e = this.player.getStats(), t = e.isLive !== !1;
@@ -2553,17 +2587,17 @@ var pn = class {
2553
2587
  renderQualityMenu() {
2554
2588
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
2555
2589
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
2556
- for (let n of e) this.menu.append(this.menuItem(hn(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)));
2557
2591
  }
2558
2592
  renderAudioMenu() {
2559
2593
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
2560
2594
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
2561
- for (let t of e) this.menu.append(this.menuItem(gn(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)));
2562
2596
  }
2563
2597
  renderCcMenu() {
2564
2598
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
2565
2599
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
2566
- for (let t of e) this.menu.append(this.menuItem(_n(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)));
2567
2601
  }
2568
2602
  onTracksChanged() {
2569
2603
  this.openMenu && this.renderMenu(this.openMenu);
@@ -2597,16 +2631,17 @@ var pn = class {
2597
2631
  this.root.classList.remove("havik-hide");
2598
2632
  }
2599
2633
  scheduleHide(e = this.autoHideMs) {
2600
- this.hideTimer && clearTimeout(this.hideTimer), this.hideTimer = setTimeout(() => {
2601
- !this.video.paused && !this.video.ended && !this.openMenu && !this.statusBox && this.root.classList.add("havik-hide");
2602
- }, 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");
2603
2638
  }
2604
2639
  onDocPointer(e) {
2605
- this.openMenu && !this.menu.contains(e.target) && ([
2640
+ this.pointerClosedMenu = !1, this.openMenu && !this.menu.contains(e.target) && ([
2606
2641
  this.qualityBtn,
2607
2642
  this.audioBtn,
2608
2643
  this.ccBtn
2609
- ].some((t) => t.contains(e.target)) || this.closeMenu());
2644
+ ].some((t) => t.contains(e.target)) || (this.closeMenu(), this.pointerClosedMenu = !0));
2610
2645
  }
2611
2646
  onKey(e) {
2612
2647
  let t = e.target;
@@ -2649,7 +2684,7 @@ var pn = class {
2649
2684
  e.innerHTML = Z(t);
2650
2685
  }
2651
2686
  destroy() {
2652
- this.hideTimer && clearTimeout(this.hideTimer);
2687
+ this.hideTimer && clearTimeout(this.hideTimer), this.tapTimer && clearTimeout(this.tapTimer);
2653
2688
  for (let e of this.offFns) try {
2654
2689
  e();
2655
2690
  } catch {}
@@ -2663,41 +2698,41 @@ var pn = class {
2663
2698
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
2664
2699
  }
2665
2700
  };
2666
- function mn(e) {
2701
+ function hn(e) {
2667
2702
  (!Number.isFinite(e) || e < 0) && (e = 0);
2668
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");
2669
2704
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
2670
2705
  }
2671
- function hn(e) {
2706
+ function gn(e) {
2672
2707
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
2673
2708
  }
2674
- function gn(e) {
2709
+ function _n(e) {
2675
2710
  return e.name || e.lang || `Track ${e.id}`;
2676
2711
  }
2677
- function _n(e) {
2712
+ function vn(e) {
2678
2713
  return e.name || e.lang || `Track ${e.id}`;
2679
2714
  }
2680
2715
  //#endregion
2681
2716
  //#region src/managed/index.ts
2682
- var vn = 6e3, yn = 45e3, Q = 2e3, bn = 3, xn = 2e3, Sn = 48e4;
2683
- function Cn(e) {
2717
+ var yn = 6e3, bn = 45e3, Q = 2e3, xn = 3, Sn = 2e3, Cn = 48e4;
2718
+ function wn(e) {
2684
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)})`);
2685
- if (e.env !== void 0) return B(e.env);
2720
+ if (e.env !== void 0) return Ve(e.env);
2686
2721
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
2687
2722
  return e.baseUrl;
2688
2723
  }
2689
- async function wn(e) {
2724
+ async function Tn(e) {
2690
2725
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
2691
- let t = Cn(e);
2726
+ let t = wn(e);
2692
2727
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
2693
2728
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
2694
- let r = new Tn({
2729
+ let r = new En({
2695
2730
  ...e,
2696
2731
  baseUrl: t
2697
2732
  });
2698
2733
  return await r.start(), r;
2699
2734
  }
2700
- var Tn = class {
2735
+ var En = class {
2701
2736
  opts;
2702
2737
  abort = new AbortController();
2703
2738
  listeners = {};
@@ -2741,7 +2776,7 @@ var Tn = class {
2741
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");
2742
2777
  let e = ++this.loadGen, t = this.resolveOptions();
2743
2778
  try {
2744
- let n = await v(t);
2779
+ let n = await y(t);
2745
2780
  if (await this.onResolved(n, e), e === this.loadGen && this._state === "error" && this._lastError) {
2746
2781
  let e = this._lastError;
2747
2782
  throw this.destroy(), e;
@@ -2768,7 +2803,7 @@ var Tn = class {
2768
2803
  r ? this.armWithSse(e, r, t, n) : this.beginArmingByPolling(e);
2769
2804
  }
2770
2805
  beginArmingByPolling(e) {
2771
- y({
2806
+ b({
2772
2807
  ...this.resolveOptions(),
2773
2808
  signal: this.abort.signal,
2774
2809
  waitForLive: this.buildWaitConfig(e)
@@ -2785,7 +2820,7 @@ var Tn = class {
2785
2820
  setTimeout(() => {
2786
2821
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
2787
2822
  phase: i ? "unavailable" : "tooEarly",
2788
- retryInMs: yn,
2823
+ retryInMs: bn,
2789
2824
  attempt: 1,
2790
2825
  elapsedMs: 0,
2791
2826
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -2802,7 +2837,7 @@ var Tn = class {
2802
2837
  }
2803
2838
  }
2804
2839
  }, h = () => {
2805
- a || s || p() || (s = !0, y({
2840
+ a || s || p() || (s = !0, b({
2806
2841
  ...this.resolveOptions(),
2807
2842
  signal: c.signal,
2808
2843
  waitForLive: this.buildWaitConfig(e)
@@ -2810,7 +2845,7 @@ var Tn = class {
2810
2845
  a || p() || c.signal.aborted || (a = !0, this.closeArming(), this.fail(e instanceof n ? e : new n("INTERNAL", 0, String(e))));
2811
2846
  }));
2812
2847
  }, g = () => {
2813
- a || p() || v(this.resolveOptions()).then((e) => m(e)).catch((e) => {
2848
+ a || p() || y(this.resolveOptions()).then((e) => m(e)).catch((e) => {
2814
2849
  if (!(a || p())) {
2815
2850
  if (e instanceof n && o(e.code)) {
2816
2851
  h();
@@ -2824,11 +2859,11 @@ var Tn = class {
2824
2859
  !a && !p() && h();
2825
2860
  }, e);
2826
2861
  };
2827
- _(vn), this.armingSub = T({
2862
+ _(yn), this.armingSub = T({
2828
2863
  eventsBaseUrl: t,
2829
2864
  matchUrn: this.opts.matchUrn,
2830
2865
  credential: this.opts.credential,
2831
- onAlive: () => _(yn),
2866
+ onAlive: () => _(bn),
2832
2867
  onState: (e) => {
2833
2868
  a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Q);
2834
2869
  },
@@ -2853,7 +2888,7 @@ var Tn = class {
2853
2888
  let n = await s(this.opts.credential);
2854
2889
  if (this.destroyed || t !== this.loadGen) return;
2855
2890
  this.setState("loading");
2856
- 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) => {
2857
2892
  this._state !== "ended" && this._state !== "error" && this.setState(e);
2858
2893
  });
2859
2894
  if (await i.load(), this.destroyed || t !== this.loadGen) {
@@ -2894,7 +2929,7 @@ var Tn = class {
2894
2929
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, e));
2895
2930
  }, 0),
2896
2931
  sample: () => {
2897
- 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;
2898
2933
  return {
2899
2934
  ...e,
2900
2935
  bufferedMs: Math.round(t * 1e3),
@@ -2935,9 +2970,9 @@ var Tn = class {
2935
2970
  }, o = this.opts.engine ?? "auto";
2936
2971
  if (o === "native") return new K(a);
2937
2972
  if (o === "hls") return new kt(a);
2938
- 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);
2939
2974
  if (at()) return new kt(a);
2940
- if (W(this.opts.video)) return new K(a);
2975
+ if (G(this.opts.video)) return new K(a);
2941
2976
  throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
2942
2977
  }
2943
2978
  onEngineEvent(e) {
@@ -2989,7 +3024,7 @@ var Tn = class {
2989
3024
  r = 0, i = !1;
2990
3025
  },
2991
3026
  onError: () => {
2992
- this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= bn && (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.`))));
2993
3028
  }
2994
3029
  });
2995
3030
  }
@@ -3035,7 +3070,7 @@ var Tn = class {
3035
3070
  }
3036
3071
  t.controls = !1;
3037
3072
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
3038
- r && r.insertBefore(n, t), n.appendChild(t), rn(n, this.opts.theme), this.container = n, this.controlBar = fn({
3073
+ r && r.insertBefore(n, t), n.appendChild(t), rn(n, this.opts.theme), this.container = n, this.controlBar = pn({
3039
3074
  root: n,
3040
3075
  video: t,
3041
3076
  player: this,
@@ -3066,7 +3101,7 @@ var Tn = class {
3066
3101
  }
3067
3102
  startStats() {
3068
3103
  this.stopStats();
3069
- let e = Math.max(500, this.opts.statsIntervalMs ?? xn);
3104
+ let e = Math.max(500, this.opts.statsIntervalMs ?? Sn);
3070
3105
  this.statsTimer = setInterval(() => {
3071
3106
  if (!this.engine) return;
3072
3107
  let e = this.engine.getStats();
@@ -3078,7 +3113,7 @@ var Tn = class {
3078
3113
  }
3079
3114
  scheduleLicenseRefresh() {
3080
3115
  this.stopRefresh();
3081
- let e = this.opts.licenseRefreshMs ?? Sn;
3116
+ let e = this.opts.licenseRefreshMs ?? Cn;
3082
3117
  e && this._descriptor?.drmEnabled && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
3083
3118
  }
3084
3119
  stopRefresh() {
@@ -3087,7 +3122,7 @@ var Tn = class {
3087
3122
  async refreshLicense() {
3088
3123
  let e = this.engine;
3089
3124
  if (!this.destroyed && e) try {
3090
- let t = await v(this.resolveOptions());
3125
+ let t = await y(this.resolveOptions());
3091
3126
  if (this.destroyed || this.engine !== e) return;
3092
3127
  let n = t.drm?.widevine?.licenseUrl, r = t.drm?.fairplay?.licenseUrl;
3093
3128
  (n || r) && (e.setLicenseUrls({
@@ -3123,7 +3158,7 @@ var Tn = class {
3123
3158
  this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
3124
3159
  }
3125
3160
  getStats() {
3126
- return this.engine?.getStats() ?? V;
3161
+ return this.engine?.getStats() ?? He;
3127
3162
  }
3128
3163
  getQualityLevels() {
3129
3164
  return this.engine?.getQualityLevels() ?? [];
@@ -3175,7 +3210,7 @@ var Tn = class {
3175
3210
  let e = ++this.loadGen;
3176
3211
  this.teardownPlayback();
3177
3212
  try {
3178
- let t = await y({
3213
+ let t = await b({
3179
3214
  ...this.resolveOptions(),
3180
3215
  waitForLive: this.opts.waitForLive ? this.buildWaitConfig(e) : void 0
3181
3216
  });
@@ -3233,7 +3268,7 @@ function $(e) {
3233
3268
  }
3234
3269
  //#endregion
3235
3270
  //#region src/embed/host.ts
3236
- function En(e) {
3271
+ function Dn(e) {
3237
3272
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
3238
3273
  if (!e.src) throw Error("mountEmbed: opts.src is required");
3239
3274
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -3292,15 +3327,15 @@ function En(e) {
3292
3327
  }
3293
3328
  //#endregion
3294
3329
  //#region src/embed/page.ts
3295
- var Dn = /^[A-Za-z0-9:_.-]{1,128}$/;
3296
- function On(e) {
3330
+ var On = /^[A-Za-z0-9:_.-]{1,128}$/;
3331
+ function kn(e) {
3297
3332
  if (typeof e == "boolean") return e;
3298
3333
  if (typeof e == "object" && e) {
3299
3334
  let t = e.endpoint;
3300
3335
  return typeof t == "string" ? { endpoint: t } : {};
3301
3336
  }
3302
3337
  }
3303
- function kn() {
3338
+ function An() {
3304
3339
  let e = window.HAVIK_EMBED_CONFIG;
3305
3340
  if (e) return {
3306
3341
  baseUrl: e.baseUrl ?? "",
@@ -3310,7 +3345,7 @@ function kn() {
3310
3345
  autoplay: e.autoplay ?? !0,
3311
3346
  muted: e.muted ?? !0,
3312
3347
  waitForLive: e.waitForLive ?? !0,
3313
- analytics: On(e.analytics)
3348
+ analytics: kn(e.analytics)
3314
3349
  };
3315
3350
  let t = new URLSearchParams(location.search);
3316
3351
  return {
@@ -3323,9 +3358,9 @@ function kn() {
3323
3358
  waitForLive: !0
3324
3359
  };
3325
3360
  }
3326
- async function An() {
3327
- let e = kn();
3328
- Mn();
3361
+ async function jn() {
3362
+ let e = An();
3363
+ Nn();
3329
3364
  let t = document.createElement("video");
3330
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);
3331
3366
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -3380,12 +3415,12 @@ async function An() {
3380
3415
  code: e.code,
3381
3416
  message: e.message
3382
3417
  })), e.on("autoplayblocked", () => {
3383
- i({ type: "oddin:autoplayblocked" }), jn(t, e);
3418
+ i({ type: "oddin:autoplayblocked" }), Mn(t, e);
3384
3419
  });
3385
3420
  }, s = async (r) => {
3386
3421
  a?.destroy(), a = null;
3387
3422
  try {
3388
- let n = await wn({
3423
+ let n = await Tn({
3389
3424
  video: t,
3390
3425
  baseUrl: e.baseUrl,
3391
3426
  matchUrn: r,
@@ -3447,7 +3482,7 @@ async function An() {
3447
3482
  a?.retry();
3448
3483
  break;
3449
3484
  case "oddin:load":
3450
- typeof e.matchUrn == "string" && Dn.test(e.matchUrn) && s(e.matchUrn);
3485
+ typeof e.matchUrn == "string" && On.test(e.matchUrn) && s(e.matchUrn);
3451
3486
  break;
3452
3487
  case "oddin:destroy": c();
3453
3488
  }
@@ -3459,18 +3494,18 @@ async function An() {
3459
3494
  protocol: 1
3460
3495
  });
3461
3496
  }
3462
- function jn(e, t) {
3497
+ function Mn(e, t) {
3463
3498
  if (document.getElementById("havik-tap")) return;
3464
3499
  let n = document.createElement("button");
3465
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", () => {
3466
3501
  n.remove(), t.play();
3467
3502
  }), document.body.appendChild(n);
3468
3503
  }
3469
- function Mn() {
3504
+ function Nn() {
3470
3505
  let e = document.createElement("style");
3471
3506
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
3472
3507
  }
3473
3508
  //#endregion
3474
- export { tn as ODDIN_THEME, n as PlaybackError, rn as applyTheme, B as baseUrlForEnv, An as bootEmbed, Te as clearDeviceId, wn 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, En 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 };
3475
3510
 
3476
3511
  //# sourceMappingURL=index.js.map