@oddin-gg/havik-player 2.15.0 → 2.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -174,7 +174,7 @@ function ue(e, t) {
174
174
  t?.addEventListener("abort", a, { once: !0 });
175
175
  });
176
176
  }
177
- async function v(e, t, r) {
177
+ async function de(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 v(e, t, r) {
200
200
  }
201
201
  //#endregion
202
202
  //#region src/core/resolve.ts
203
- async function y(e) {
203
+ async function v(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 de(t);
211
- return me(await t.json(), e.matchUrn);
210
+ if (!t.ok) throw await fe(t);
211
+ return he(await t.json(), e.matchUrn);
212
212
  }
213
- function b(e) {
214
- let t = () => y(e);
215
- return e.waitForLive ? v(t, e.waitForLive, e.signal) : t();
213
+ function y(e) {
214
+ let t = () => v(e);
215
+ return e.waitForLive ? de(t, e.waitForLive, e.signal) : t();
216
216
  }
217
- async function de(e) {
217
+ async function fe(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 de(e) {
230
230
  liveStartsAtMs: s
231
231
  });
232
232
  }
233
- var fe = /* @__PURE__ */ new Set([
233
+ var pe = /* @__PURE__ */ new Set([
234
234
  "matchUrn",
235
235
  "protocol",
236
236
  "drmEnabled",
@@ -241,7 +241,7 @@ var fe = /* @__PURE__ */ new Set([
241
241
  "expiresAt",
242
242
  "analytics"
243
243
  ]);
244
- function x(e, t) {
244
+ function b(e, t) {
245
245
  let r;
246
246
  try {
247
247
  r = new URL(t);
@@ -251,7 +251,7 @@ function x(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 pe(e) {
254
+ function me(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 pe(e) {
260
260
  g(`server analytics.endpoint ${JSON.stringify(e)} ignored: not a bare https origin.`);
261
261
  }
262
262
  }
263
- function me(e, t) {
263
+ function he(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: x("manifestUrl", e.manifestUrl),
269
+ manifestUrl: b("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 = pe(e.analytics.endpoint);
274
+ let t = me(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: 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)
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)
282
282
  }), (t.widevine || t.fairplay) && (i.drm = t);
283
283
  }
284
284
  let a = {};
285
- for (let [t, n] of Object.entries(e)) fe.has(t) || (a[t] = n);
285
+ for (let [t, n] of Object.entries(e)) pe.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 he(e) {
290
+ function ge(e) {
291
291
  return e.ownerDocument ?? document;
292
292
  }
293
- function ge(e) {
293
+ function x(e) {
294
294
  return e.fullscreenElement ?? e.webkitFullscreenElement ?? null;
295
295
  }
296
296
  function S(e, t) {
297
- let n = ge(he(e));
297
+ let n = x(ge(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 = he(e), i = ge(r);
313
+ let r = ge(e), i = x(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 R = {
674
+ var je = {
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
- }, z = /* @__PURE__ */ new Map(), B = /* @__PURE__ */ new Map();
680
- function V(e) {
681
- let t = L(e), n = z.get(t);
679
+ }, Me = /* @__PURE__ */ new Map(), R = /* @__PURE__ */ new Map();
680
+ function Ne(e) {
681
+ let t = L(e), n = Me.get(t);
682
682
  if (n) return n;
683
683
  let r = {
684
684
  initDataTypes: ["cenc"],
@@ -696,23 +696,23 @@ function V(e) {
696
696
  } catch {
697
697
  return !1;
698
698
  }
699
- })().then((e) => (B.set(t, e), e));
700
- return z.set(t, i), i;
699
+ })().then((e) => (R.set(t, e), e));
700
+ return Me.set(t, i), i;
701
701
  }
702
- function je() {
703
- if (j()) for (let e of Object.values(R)) V(e);
702
+ function Pe() {
703
+ if (j()) for (let e of Object.values(je)) Ne(e);
704
704
  }
705
- function H(e) {
705
+ function Fe(e) {
706
706
  let t = I(e);
707
- if (t) return t === "avc" ? !0 : B.get(L(e)) ?? B.get(L(R[t]));
707
+ if (t) return t === "avc" ? !0 : R.get(L(e)) ?? R.get(L(je[t]));
708
708
  }
709
709
  //#endregion
710
710
  //#region src/core/drm/support.ts
711
- var Me = 5e3, U = "video/mp4; codecs=\"avc1.42E01E\"", Ne = "audio/mp4; codecs=\"mp4a.40.2\"", Pe = {
711
+ var Ie = 5e3, Le = "video/mp4; codecs=\"avc1.42E01E\"", Re = "audio/mp4; codecs=\"mp4a.40.2\"", ze = {
712
712
  widevine: ["com.widevine.alpha"],
713
713
  fairplay: ["com.apple.fps"]
714
714
  };
715
- function Fe(e) {
715
+ function Be(e) {
716
716
  return e === "fairplay" ? [{
717
717
  initDataTypes: [
718
718
  "sinf",
@@ -720,30 +720,30 @@ function Fe(e) {
720
720
  "cenc"
721
721
  ],
722
722
  videoCapabilities: [{
723
- contentType: U,
723
+ contentType: Le,
724
724
  robustness: "",
725
725
  encryptionScheme: "cbcs"
726
726
  }],
727
727
  audioCapabilities: [{
728
- contentType: Ne,
728
+ contentType: Re,
729
729
  robustness: "",
730
730
  encryptionScheme: "cbcs"
731
731
  }]
732
732
  }] : N("com.widevine.alpha", [{
733
733
  initDataTypes: ["cenc"],
734
734
  videoCapabilities: [{
735
- contentType: U,
735
+ contentType: Le,
736
736
  robustness: F.video,
737
737
  encryptionScheme: F.scheme
738
738
  }],
739
739
  audioCapabilities: [{
740
- contentType: Ne,
740
+ contentType: Re,
741
741
  robustness: F.audio,
742
742
  encryptionScheme: F.scheme
743
743
  }]
744
744
  }]);
745
745
  }
746
- function Ie(e, t) {
746
+ function Ve(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 Ie(e, t) {
753
753
  });
754
754
  });
755
755
  }
756
- async function Le(e = {}) {
757
- let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? Me, r = {};
756
+ async function He(e = {}) {
757
+ let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? Ie, 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 Le(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 = Pe[e];
769
+ let t = ze[e];
770
770
  if (!t) {
771
771
  r[e] = "unknown system";
772
772
  continue;
@@ -777,7 +777,7 @@ async function Le(e = {}) {
777
777
  break;
778
778
  }
779
779
  try {
780
- return await Ie((await Ie(i(n, Fe(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
780
+ return await Ve((await Ve(i(n, Be(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 Le(e = {}) {
801
801
  }
802
802
  //#endregion
803
803
  //#region src/core/manifest.ts
804
- var Re = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
805
- function ze(e) {
806
- return Re.test(e);
804
+ var Ue = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
805
+ function We(e) {
806
+ return Ue.test(e);
807
807
  }
808
808
  //#endregion
809
809
  //#region src/core/env.ts
810
- var Be = {
810
+ var z = {
811
811
  integration: "https://feed-dev.oddin-video.gg",
812
812
  production: "https://feed.oddin-video.gg"
813
813
  };
814
- function Ve(e) {
815
- let t = Object.prototype.hasOwnProperty.call(Be, e) ? Be[e] : void 0;
814
+ function B(e) {
815
+ let t = Object.prototype.hasOwnProperty.call(z, e) ? z[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 He = { droppedFrames: 0 };
822
- function Ue(e) {
821
+ var V = { droppedFrames: 0 };
822
+ function H(e) {
823
823
  try {
824
824
  return new URL(e).host;
825
825
  } catch {
826
826
  return "";
827
827
  }
828
828
  }
829
- var We = /\.(m3u8|ts|m4s|mp4|cmfv|cmfa)(\?|$)/i;
830
- function Ge(e) {
829
+ var Ge = /\.(m3u8|ts|m4s|mp4|cmfv|cmfa)(\?|$)/i;
830
+ function Ke(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" || We.test(i.name)) && i.startTime >= n) {
834
- let e = Ue(i.name);
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);
835
835
  e && (t = e, n = i.startTime);
836
836
  }
837
837
  return t;
838
838
  }
839
- function W(e) {
839
+ function U(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 Ke(e, t) {
846
+ function qe(e, t) {
847
847
  let n = [
848
848
  ["playing", () => t("playing")],
849
849
  ["waiting", () => t("buffering")],
@@ -860,7 +860,7 @@ function Ke(e, t) {
860
860
  }
861
861
  //#endregion
862
862
  //#region src/core/engine/native.ts
863
- function qe(e) {
863
+ function Je(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 qe(e) {
868
868
  return;
869
869
  }
870
870
  }
871
- function G(e) {
871
+ function W(e) {
872
872
  return !!(e ?? (typeof document < "u" ? document.createElement("video") : void 0))?.canPlayType("application/vnd.apple.mpegurl");
873
873
  }
874
- function Je() {
874
+ function G() {
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 = qe(i.initData), (async () => {
937
+ i.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = Je(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 = W(e), n = {
1032
+ let e = this.deps.video, t = U(e), n = {
1033
1033
  droppedFrames: t.dropped,
1034
1034
  decodedFrames: t.totalFrames
1035
1035
  };
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;
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;
1037
1037
  }
1038
1038
  stop() {
1039
1039
  try {
@@ -1219,21 +1219,21 @@ function it(e, t) {
1219
1219
  function at() {
1220
1220
  return e.isSupported();
1221
1221
  }
1222
- var ot = 3, st = 5, ct = 3, lt = 3e4, ut = 12e3, dt = 3e3, ft = 6e3, pt = 5e3, mt = 4e3, ht = 2, gt = 5, _t = 2, vt = 2, yt = 4, bt = 4, xt = 2e3, St = 3e4, Ct = 2, wt = 9, Tt = .5, Et = 64;
1223
- function Dt(e) {
1222
+ var ot = 3, st = 5, ct = 3, lt = 3e4, ut = 12e3, dt = 3e3, ft = 6e3, pt = 3, mt = .5, ht = 1.2, gt = .25, _t = 5e3, vt = 4e3, yt = 2, bt = 5, xt = 2, St = 2, Ct = 4, wt = 4, Tt = 2e3, Et = 3e4, Dt = 2, Ot = 9, kt = .5, At = 64;
1223
+ function jt(e) {
1224
1224
  if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
1225
1225
  delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
1226
1226
  return;
1227
1227
  }
1228
- e.liveSyncDuration ??= Ct;
1229
- let t = Math.max(12, e.liveSyncDuration + wt);
1230
- (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Ot(e, e.liveSyncDuration);
1228
+ e.liveSyncDuration ??= Dt;
1229
+ let t = Math.max(12, e.liveSyncDuration + Ot);
1230
+ (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Mt(e, e.liveSyncDuration);
1231
1231
  }
1232
- function Ot(e, t) {
1232
+ function Mt(e, t) {
1233
1233
  let n = (e, t, n) => Math.min(n, Math.max(t, e));
1234
1234
  e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
1235
1235
  }
1236
- var kt = class {
1236
+ var Nt = class {
1237
1237
  name = "hls";
1238
1238
  hls = null;
1239
1239
  deps;
@@ -1289,6 +1289,9 @@ var kt = class {
1289
1289
  chaseSuspended = !1;
1290
1290
  savedLiveMaxLatency;
1291
1291
  savedMaxSyncRate;
1292
+ chaseLatency = -1;
1293
+ chaseTicks = 0;
1294
+ liveRetargets = 0;
1292
1295
  capObserver = null;
1293
1296
  capTimer = null;
1294
1297
  sizeCapLevel = null;
@@ -1301,7 +1304,7 @@ var kt = class {
1301
1304
  this.deps = e, this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl, this.loadStarted = e.deferLoad !== !0;
1302
1305
  }
1303
1306
  async load() {
1304
- let { descriptor: e, snapToLiveOnRefocus: t, video: r, emit: i } = this.deps, a = G(r) && Je(), o = !!e.drm?.widevine?.licenseUrl && !a;
1307
+ let { descriptor: e, snapToLiveOnRefocus: t, video: r, emit: i } = this.deps, a = W(r) && G(), o = !!e.drm?.widevine?.licenseUrl && !a;
1305
1308
  if (e.drmEnabled && !o) {
1306
1309
  i({
1307
1310
  type: "error",
@@ -1310,7 +1313,7 @@ var kt = class {
1310
1313
  });
1311
1314
  return;
1312
1315
  }
1313
- e.drmEnabled && o && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && (Ae(r), je()), this.startHls(), t !== !1 && typeof document < "u" && (this.onVisibility = () => {
1316
+ e.drmEnabled && o && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && (Ae(r), Pe()), this.startHls(), t !== !1 && typeof document < "u" && (this.onVisibility = () => {
1314
1317
  let e = this.hls;
1315
1318
  if (document.visibilityState !== "visible" || !e) return;
1316
1319
  let t = e.liveSyncPosition;
@@ -1333,11 +1336,11 @@ var kt = class {
1333
1336
  }
1334
1337
  this.loadSuspended = !0;
1335
1338
  }
1336
- }, pt));
1339
+ }, _t));
1337
1340
  }
1338
1341
  resumeLoading() {
1339
- if (this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.loadSuspended && (this.loadSuspended = !1, !this.stopped && this.hls)) {
1340
- this.lastAdvanceMs = Date.now(), this.lastFragLoadingMs = 0, this.resetStarvationWindow(), this.clearKick();
1342
+ if (this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.resetChaseWindow(), this.loadSuspended && (this.loadSuspended = !1, !this.stopped && this.hls)) {
1343
+ this.lastAdvanceMs = Date.now(), this.lastFragLoadingMs = 0, this.resetStarvationWindow(), this.resetChaseWindow(), this.clearKick();
1341
1344
  try {
1342
1345
  this.hls.startLoad(-1);
1343
1346
  } catch {}
@@ -1365,11 +1368,11 @@ var kt = class {
1365
1368
  let t = e.liveSyncPosition;
1366
1369
  if (typeof t != "number" || !Number.isFinite(t)) return;
1367
1370
  let { video: n } = this.deps;
1368
- n.currentTime <= t + Tt || (n.currentTime = t);
1371
+ n.currentTime <= t + kt || (n.currentTime = t);
1369
1372
  }
1370
1373
  startHls() {
1371
1374
  let { video: t, descriptor: n, credential: r, deviceId: i, userId: a, lowLatency: o, debug: s, hlsConfig: c, emit: l } = this.deps;
1372
- this.clearKick(), this.resetStarvationWindow(), this.lastFragLoadingMs = 0, this.lastEndSN = -1, this.lastAdvanceMs = 0;
1375
+ this.clearKick(), this.resetStarvationWindow(), this.resetChaseWindow(), this.lastFragLoadingMs = 0, this.lastEndSN = -1, this.lastAdvanceMs = 0;
1373
1376
  let u = n.drmEnabled && !!(n.drm?.widevine?.licenseUrl || n.drm?.fairplay?.licenseUrl), d = {
1374
1377
  debug: s === !0,
1375
1378
  enableWorker: !o,
@@ -1412,7 +1415,7 @@ var kt = class {
1412
1415
  }), t = tt(e);
1413
1416
  t && (this.prefetch = e, d.fLoader = t);
1414
1417
  }
1415
- Dt(d), this.sizeCapManaged = d.capLevelToPlayerSize !== !0, this.loadStarted = d.autoStartLoad !== !1;
1418
+ jt(d), this.sizeCapManaged = d.capLevelToPlayerSize !== !0, this.loadStarted = d.autoStartLoad !== !1;
1416
1419
  let f = new e(d);
1417
1420
  if (this.hls = f, f.on(e.Events.MANIFEST_PARSED, () => {
1418
1421
  this.mediaRecoveries = 0, this.networkRecoveries = 0, u && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && this.dropLevelsWidevineCannotPlay(f), this.measureLadder(f), this.applySizeCap(), this.loadSuspended = !1, t.paused && this.schedulePauseSuspend(), l({ type: "ready" });
@@ -1427,10 +1430,10 @@ var kt = class {
1427
1430
  let n = t?.frag?.stats, r = n?.loaded;
1428
1431
  typeof r == "number" && (this.bytesLoaded += r), !this.reportedFirstBytes && typeof r == "number" && r > 0 && (this.reportedFirstBytes = !0, l({ type: "firstBytes" }));
1429
1432
  let i = n?.loading;
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());
1433
+ 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 > At && this.requestTimesMs.shift());
1431
1434
  let a = t?.frag?.url;
1432
1435
  if (a) {
1433
- let e = Ue(a);
1436
+ let e = H(a);
1434
1437
  e && (this.cdnHost = e);
1435
1438
  }
1436
1439
  }), f.on(e.Events.LEVEL_LOADED, (e, t) => {
@@ -1451,7 +1454,7 @@ var kt = class {
1451
1454
  f.on(e.Events.KEY_LOADED, () => {
1452
1455
  u && !this.reportedDrmReady && (this.reportedDrmReady = !0, l({ type: "drmReady" }));
1453
1456
  }), this.endWatchdog && clearInterval(this.endWatchdog), this.endWatchdog = setInterval(() => {
1454
- this.checkEndStale(), this.checkStarvation();
1457
+ this.checkEndStale(), this.checkStarvation(), this.checkFutileChase();
1455
1458
  }, dt), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1456
1459
  this.renditionSwitches += 1;
1457
1460
  let n = this.lastLevelIndex >= 0 ? f.levels[this.lastLevelIndex] : void 0, r = f.levels[t.level];
@@ -1516,7 +1519,7 @@ var kt = class {
1516
1519
  return;
1517
1520
  }
1518
1521
  this.capTimer && clearTimeout(this.capTimer);
1519
- let t = e > this.sizeCapLevel ? xt : St;
1522
+ let t = e > this.sizeCapLevel ? Tt : Et;
1520
1523
  this.capTimer = setTimeout(() => {
1521
1524
  this.capTimer = null, this.computeSizeCapTarget() === e ? this.applySizeCap() : this.onMediaResize();
1522
1525
  }, t);
@@ -1526,9 +1529,9 @@ var kt = class {
1526
1529
  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
1530
  }
1528
1531
  dropLevelsWidevineCannotPlay(e) {
1529
- this.removeLevelsWhere(e, (e) => H(e) !== !0);
1532
+ this.removeLevelsWhere(e, (e) => Fe(e) !== !0);
1530
1533
  let t = new Set(e.levels.map((e) => e.videoCodec).filter((e) => !!e && I(e) !== "avc" && !!I(e)));
1531
- for (let n of t) V(n).then((t) => {
1534
+ for (let n of t) Ne(n).then((t) => {
1532
1535
  t || this.hls !== e || this.removeLevelsWhere(e, (e) => e === n) && (this.measureLadder(e), this.applySizeCap());
1533
1536
  });
1534
1537
  }
@@ -1539,7 +1542,7 @@ var kt = class {
1539
1542
  a && I(a) && t(a) && (r.push(n), i.add(a));
1540
1543
  }), !r.length) return !1;
1541
1544
  let a = [...i].join(", ");
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;
1545
+ 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;
1543
1546
  for (let t = r.length - 1; t >= 0; --t) e.removeLevel(r[t]);
1544
1547
  return console.info(`havik-player: skipped ${r.length} rendition(s) Widevine cannot play on this browser (${a})`), !0;
1545
1548
  }
@@ -1585,13 +1588,14 @@ var kt = class {
1585
1588
  e.widevine && (this.licenseUrl = e.widevine);
1586
1589
  }
1587
1590
  getStats() {
1588
- let e = this.hls, t = W(this.deps.video), n = {
1591
+ let e = this.hls, t = U(this.deps.video), n = {
1589
1592
  droppedFrames: t.dropped,
1590
1593
  decodedFrames: t.totalFrames,
1591
1594
  lowLatency: this.detectedLowLatency,
1592
1595
  bytesLoaded: this.bytesLoaded,
1593
1596
  playlistBytesLoaded: this.playlistBytesLoaded,
1594
1597
  renditionSwitches: this.renditionSwitches,
1598
+ liveRetargets: this.liveRetargets,
1595
1599
  licenseTimeMs: this.licenseTimeMs,
1596
1600
  upshifts: this.upshifts,
1597
1601
  downshifts: this.downshifts,
@@ -1646,7 +1650,7 @@ var kt = class {
1646
1650
  this.resetStarvationWindow();
1647
1651
  return;
1648
1652
  }
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;
1653
+ let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < ft, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > vt, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = U(t), c = o - s, l = c !== this.starvedFrames;
1650
1654
  if (this.starvedFrames = c, !a || l) {
1651
1655
  (t.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA || l) && this.clearKick(), this.resetStarvationWindow();
1652
1656
  return;
@@ -1655,12 +1659,12 @@ var kt = class {
1655
1659
  this.resetStarvationWindow();
1656
1660
  return;
1657
1661
  }
1658
- if (this.kickPending && (this.kickKind === "dead" || i) && (this.kickTicks += 1), this.starvedTicks = i ? this.starvedTicks + 1 : 0, this.deadTicks += 1, this.recentMainFrags.push(this.mainFragsSinceTick), this.mainFragsSinceTick = 0, this.recentMainFrags.length > yt && this.recentMainFrags.shift(), this.clearRecoveryReset(), this.kickPending && this.kickTicks >= _t && (this.clearKick(), this.starvationRebuilds < vt)) {
1662
+ if (this.kickPending && (this.kickKind === "dead" || i) && (this.kickTicks += 1), this.starvedTicks = i ? this.starvedTicks + 1 : 0, this.deadTicks += 1, this.recentMainFrags.push(this.mainFragsSinceTick), this.mainFragsSinceTick = 0, this.recentMainFrags.length > Ct && this.recentMainFrags.shift(), this.clearRecoveryReset(), this.kickPending && this.kickTicks >= xt && (this.clearKick(), this.starvationRebuilds < St)) {
1659
1663
  this.starvationRebuilds += 1, this.resetStarvationWindow(), this.clearRecoveryReset(), console.warn("havik-player: restarting the load did not resume video (the wedge is in hls.js state that only a rebuild clears) — rebuilding the player"), this.rebuildInstance("starvation rebuild");
1660
1664
  return;
1661
1665
  }
1662
- let u = i && this.starvedTicks >= ht, d = !i && this.deadTicks >= yt && this.recentMainFrags.reduce((e, t) => e + t, 0) >= bt;
1663
- if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >= gt))) {
1666
+ let u = i && this.starvedTicks >= yt, d = !i && this.deadTicks >= Ct && this.recentMainFrags.reduce((e, t) => e + t, 0) >= wt;
1667
+ if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >= bt))) {
1664
1668
  this.starvationKicks += 1, this.kickPending = !0, this.kickTicks = 0, this.kickKind = d ? "dead" : "quiet", this.clearRecoveryReset(), console.warn(d ? "havik-player: video starved while hls.js keeps loading media it never plays (dead pipeline at the live edge) — restarting the load" : "havik-player: video starved while the live playlist advances (wedged hls.js fragment pipeline) — restarting the load");
1665
1669
  try {
1666
1670
  e.stopLoad(), e.startLoad(-1);
@@ -1669,6 +1673,31 @@ var kt = class {
1669
1673
  }
1670
1674
  }
1671
1675
  }
1676
+ resetChaseWindow() {
1677
+ this.chaseLatency = -1, this.chaseTicks = 0;
1678
+ }
1679
+ checkFutileChase() {
1680
+ let e = this.hls, { video: t } = this.deps;
1681
+ if (!e || this.endedEmitted || !this.sawLive || this.chaseSuspended || this.loadSuspended || typeof document < "u" && document.hidden) {
1682
+ this.resetChaseWindow();
1683
+ return;
1684
+ }
1685
+ let n = e.latency, r = t.playbackRate, i = e.config.maxLiveSyncPlaybackRate;
1686
+ if (!(!t.paused && r >= ht && typeof i == "number" && i > 1 && r <= i && typeof n == "number" && Number.isFinite(n))) {
1687
+ this.resetChaseWindow();
1688
+ return;
1689
+ }
1690
+ if (this.chaseLatency < 0 || this.chaseLatency - n >= mt) {
1691
+ this.chaseLatency = n, this.chaseTicks = 0;
1692
+ return;
1693
+ }
1694
+ if (this.chaseTicks += 1, this.chaseTicks < pt) return;
1695
+ this.resetChaseWindow();
1696
+ let a = typeof e.config.liveMaxLatencyDuration == "number" ? e.config.liveMaxLatencyDuration : e.maxLatency;
1697
+ if (!(a > 1)) return;
1698
+ let o = typeof e.targetLatency == "number" ? e.targetLatency : 0, s = Math.min(n, a - 1);
1699
+ s < o + gt || (console.warn(`havik-player: catch-up at ${r}x not gaining on the live edge (latency ${n.toFixed(1)}s); holding at ${s.toFixed(1)}s`), this.liveRetargets += 1, e.targetLatency = s);
1700
+ }
1672
1701
  suspendChasing() {
1673
1702
  let e = this.hls;
1674
1703
  e && (this.chaseSuspended = !0, this.savedLiveMaxLatency = e.config.liveMaxLatencyDuration, this.savedMaxSyncRate = e.config.maxLiveSyncPlaybackRate, e.config.liveMaxLatencyDuration = 2 ** 53 - 1, e.config.maxLiveSyncPlaybackRate = 1);
@@ -1782,19 +1811,19 @@ var kt = class {
1782
1811
  let r = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
1783
1812
  return new n(t === "DRM" ? i(r) : r === 401 ? "UNAUTHORIZED" : r === 403 ? "FORBIDDEN" : "INTERNAL", r, `${t} error: ${a}${o ? ` (${o})` : ""}`);
1784
1813
  }
1785
- }, At = 15e3, jt = 6e5, Mt = 3e5, Nt = 100, q = 1, Pt = 1e3, Ft = 3e4, It = 1e3, Lt = {
1814
+ }, Pt = 15e3, Ft = 6e5, It = 3e5, Lt = 100, q = 1, Rt = 1e3, zt = 3e4, Bt = 1e3, Vt = {
1786
1815
  inline: "PRESENTATION_INLINE",
1787
1816
  fullscreen: "PRESENTATION_FULLSCREEN",
1788
1817
  pip: "PRESENTATION_PIP"
1789
- }, Rt = {
1818
+ }, Ht = {
1790
1819
  widevine: "KEY_SYSTEM_WIDEVINE",
1791
1820
  fairplay: "KEY_SYSTEM_FAIRPLAY",
1792
1821
  none: "KEY_SYSTEM_NONE"
1793
1822
  };
1794
- function zt(e, t) {
1823
+ function Ut(e, t) {
1795
1824
  return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
1796
1825
  }
1797
- var Bt = /* @__PURE__ */ new Set([
1826
+ var Wt = /* @__PURE__ */ new Set([
1798
1827
  "INVALID_URN",
1799
1828
  "NOT_FOUND",
1800
1829
  "GONE",
@@ -1814,7 +1843,7 @@ var Bt = /* @__PURE__ */ new Set([
1814
1843
  ended: "PLAYER_STATE_ENDED",
1815
1844
  error: "PLAYER_STATE_ERROR"
1816
1845
  };
1817
- function Vt(e) {
1846
+ function Gt(e) {
1818
1847
  try {
1819
1848
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
1820
1849
  return !n || r.length === 0 || n !== "feed" && !n.startsWith("feed-") ? void 0 : (t.hostname = [n.replace(/^feed/, "beacons"), ...r].join("."), t.pathname = "", t.search = "", t.origin);
@@ -1822,8 +1851,8 @@ function Vt(e) {
1822
1851
  return;
1823
1852
  }
1824
1853
  }
1825
- var Ht = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), Ut = /* @__PURE__ */ new Set(), Y = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), Wt = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
1826
- function Gt(e) {
1854
+ var Kt = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), qt = /* @__PURE__ */ new Set(), Y = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), Jt = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
1855
+ function Yt(e) {
1827
1856
  if (e.disposition !== "enforce" || e.effectiveDirective !== "connect-src") return;
1828
1857
  let t;
1829
1858
  try {
@@ -1832,19 +1861,19 @@ function Gt(e) {
1832
1861
  } catch {
1833
1862
  return;
1834
1863
  }
1835
- if (Ut.has(t) && !Y.has(t)) {
1836
- Y.add(t), g(Wt(new URL(t).origin));
1864
+ if (qt.has(t) && !Y.has(t)) {
1865
+ Y.add(t), g(Jt(new URL(t).origin));
1837
1866
  for (let e of X) e.noteCspBlocked();
1838
1867
  }
1839
1868
  }
1840
- function Kt(e, t) {
1869
+ function Xt(e, t) {
1841
1870
  return g(t, e), { disabledReason: t };
1842
1871
  }
1843
- function qt(e, t, n) {
1844
- if (n) return t && !Ht(t, n) && g(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
1872
+ function Zt(e, t, n) {
1873
+ if (n) return t && !Kt(t, n) && g(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
1845
1874
  if (t) return { endpoint: t };
1846
- if (t !== void 0) return Kt("empty-endpoint", "analytics is disabled: no QoE beacons will be sent. analytics.endpoint was supplied but empty. Pass a real beacons origin, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, or omit the option to derive it from baseUrl (or analytics: false to opt out).");
1847
- let r = Vt(e);
1875
+ if (t !== void 0) return Xt("empty-endpoint", "analytics is disabled: no QoE beacons will be sent. analytics.endpoint was supplied but empty. Pass a real beacons origin, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, or omit the option to derive it from baseUrl (or analytics: false to opt out).");
1876
+ let r = Gt(e);
1848
1877
  if (r) return { endpoint: r };
1849
1878
  let i;
1850
1879
  try {
@@ -1852,9 +1881,9 @@ function qt(e, t, n) {
1852
1881
  } catch {
1853
1882
  i = e;
1854
1883
  }
1855
- return Kt(i, `analytics is disabled: no QoE beacons will be sent. The beacons endpoint cannot be derived from the baseUrl host "${i}" — the convention is feed[-dev].<domain> → beacons[-dev].<domain>. Pass an explicit endpoint, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, to createPlayer (or analytics: false to opt out deliberately).`);
1884
+ return Xt(i, `analytics is disabled: no QoE beacons will be sent. The beacons endpoint cannot be derived from the baseUrl host "${i}" — the convention is feed[-dev].<domain> → beacons[-dev].<domain>. Pass an explicit endpoint, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, to createPlayer (or analytics: false to opt out deliberately).`);
1856
1885
  }
1857
- var Jt = class {
1886
+ var Qt = class {
1858
1887
  opts;
1859
1888
  url;
1860
1889
  cspKey;
@@ -1936,8 +1965,8 @@ var Jt = class {
1936
1965
  constructor(e) {
1937
1966
  this.opts = {
1938
1967
  ...e,
1939
- heartbeatMs: e.heartbeatMs ?? At,
1940
- idleMs: e.idleMs ?? jt
1968
+ heartbeatMs: e.heartbeatMs ?? Pt,
1969
+ idleMs: e.idleMs ?? Ft
1941
1970
  }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`;
1942
1971
  try {
1943
1972
  let e = new URL(this.url);
@@ -1954,11 +1983,11 @@ var Jt = class {
1954
1983
  sdkVersion: this.opts.sdkVersion,
1955
1984
  ...this.opts.environment
1956
1985
  };
1957
- this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), Pt), this.lastTickMs = this.now(), this.timer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs), typeof window < "u" && window.addEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.addEventListener("visibilitychange", this.onVisibility), document.addEventListener("freeze", this.onFreeze), document.addEventListener("resume", this.onResume), this.cspKey && Ut.add(this.cspKey), document.addEventListener("securitypolicyviolation", Gt), X.add(this), this.noteCspBlocked(), this.onVisibility());
1986
+ this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), Rt), this.lastTickMs = this.now(), this.timer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs), typeof window < "u" && window.addEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.addEventListener("visibilitychange", this.onVisibility), document.addEventListener("freeze", this.onFreeze), document.addEventListener("resume", this.onResume), this.cspKey && qt.add(this.cspKey), document.addEventListener("securitypolicyviolation", Yt), X.add(this), this.noteCspBlocked(), this.onVisibility());
1958
1987
  }
1959
1988
  checkSuspension(e) {
1960
1989
  if (this.endedFlag || !this.opts.idleMs) return !1;
1961
- let t = Math.max(this.opts.idleMs, Mt);
1990
+ let t = Math.max(this.opts.idleMs, It);
1962
1991
  return this.lastTickMs > 0 && e - this.lastTickMs >= t ? (this.endIdle(this.lastTickMs), !0) : (this.lastTickMs = e, !1);
1963
1992
  }
1964
1993
  syncIdle(e) {
@@ -1981,12 +2010,12 @@ var Jt = class {
1981
2010
  let a = {
1982
2011
  fromState: J[e] ?? "PLAYER_STATE_UNSPECIFIED",
1983
2012
  toState: J[t] ?? "PLAYER_STATE_UNSPECIFIED",
1984
- presentation: Lt[this.presentation]
2013
+ presentation: Vt[this.presentation]
1985
2014
  }, o = this.causeOf(e, t, i);
1986
2015
  o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
1987
2016
  }
1988
2017
  seekInduced(e) {
1989
- return this.lastSeekMs >= 0 && e - this.lastSeekMs < It;
2018
+ return this.lastSeekMs >= 0 && e - this.lastSeekMs < Bt;
1990
2019
  }
1991
2020
  causeOf(e, t, n) {
1992
2021
  if (t === "error") return "TRANSITION_CAUSE_ERROR";
@@ -2060,7 +2089,7 @@ var Jt = class {
2060
2089
  let r = n ?? "";
2061
2090
  if (r !== "") return /^fragLoad/i.test(r) ? this.sawFirstBytes ? "ERROR_STAGE_SEGMENT" : "ERROR_STAGE_FIRST_SEGMENT" : /^(manifest|level|audioTrack|subtitleTrack)/i.test(r) ? "ERROR_STAGE_MANIFEST" : /key|licen[cs]e/i.test(r) ? "ERROR_STAGE_DRM" : /parsing|append|buffer|remux|decrypt|media/i.test(r) ? "ERROR_STAGE_DECODE" : "ERROR_STAGE_OTHER";
2062
2091
  if (e === "DRM_CLIENT") return "ERROR_STAGE_DRM";
2063
- if (Bt.has(e)) return "ERROR_STAGE_RESOLVE";
2092
+ if (Wt.has(e)) return "ERROR_STAGE_RESOLVE";
2064
2093
  let i = t ?? "";
2065
2094
  return /frag|segment/i.test(i) ? this.sawFirstBytes ? "ERROR_STAGE_SEGMENT" : "ERROR_STAGE_FIRST_SEGMENT" : /manifest|level|playlist/i.test(i) ? "ERROR_STAGE_MANIFEST" : /key|licen[cs]e/i.test(i) ? "ERROR_STAGE_DRM" : /buffer|decode|media|append/i.test(i) ? "ERROR_STAGE_DECODE" : "ERROR_STAGE_OTHER";
2066
2095
  }
@@ -2107,14 +2136,14 @@ var Jt = class {
2107
2136
  if (this.lastRequestCount = g, _ > 0) {
2108
2137
  o.requestCountDelta = _;
2109
2138
  let e = t.requestTimesMs ?? [], n = e.slice(Math.max(0, e.length - _)).sort((e, t) => e - t);
2110
- n.length > 0 && (o.requestTimeP50Ms = zt(n, 50), o.requestTimeP95Ms = zt(n, 95));
2139
+ n.length > 0 && (o.requestTimeP50Ms = Ut(n, 50), o.requestTimeP95Ms = Ut(n, 95));
2111
2140
  }
2112
2141
  typeof t.latencySeconds == "number" && typeof t.targetLatencySeconds == "number" && (o.driftMs = Math.round((t.latencySeconds - t.targetLatencySeconds) * 1e3)), typeof t.playbackRate == "number" && t.playbackRate > 0 && (o.playbackRate = t.playbackRate), t.qualityPinned === !0 && (o.qualityPinned = !0), t.cdnHost && (o.cdnHost = t.cdnHost), this.maybeEmitMediaInfo(t), this.enqueue({ heartbeat: o }), this.flush(), s > 0 && this.noteFirstBytes();
2113
2142
  }
2114
2143
  maybeEmitMediaInfo(e) {
2115
2144
  if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
2116
2145
  let t = {};
2117
- e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = Rt[e.keySystem]), e.drmSecurityLevel && (t.drmSecurityLevel = e.drmSecurityLevel), e.ladderHeights?.length && (t.ladderHeights = e.ladderHeights), e.ladderTopBitrateKbps && (t.ladderTopBitrateKbps = e.ladderTopBitrateKbps), e.lowLatency && (t.lowLatency = !0);
2146
+ e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = Ht[e.keySystem]), e.drmSecurityLevel && (t.drmSecurityLevel = e.drmSecurityLevel), e.ladderHeights?.length && (t.ladderHeights = e.ladderHeights), e.ladderTopBitrateKbps && (t.ladderTopBitrateKbps = e.ladderTopBitrateKbps), e.lowLatency && (t.lowLatency = !0);
2118
2147
  let n = JSON.stringify(t);
2119
2148
  n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
2120
2149
  }
@@ -2134,7 +2163,7 @@ var Jt = class {
2134
2163
  let c = r?.positionMs ?? 0;
2135
2164
  c > 0 && (i.exitPositionMs = Math.round(c)), this.activeTotalMs > 0 && (i.activeWatchTimeMs = Math.round(this.activeTotalMs)), this.mutedTotalMs > 0 && (i.mutedTimeMs = Math.round(this.mutedTotalMs));
2136
2165
  let l = e !== "END_REASON_ENDED";
2137
- l && this.lastStallMs > 0 && n - this.lastStallMs <= Ft && (i.abandonAfterStallMs = Math.max(q, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= Ft && (i.abandonAfterErrorMs = Math.max(q, Math.round(n - this.lastErrorMs))), this.fullscreenTotalMs > 0 && (i.fullscreenTimeMs = Math.round(this.fullscreenTotalMs)), this.pipTotalMs > 0 && (i.pipTimeMs = Math.round(this.pipTotalMs)), this.volumeChanges > 0 && (i.volumeChanges = this.volumeChanges), this.muteChanges > 0 && (i.muteChanges = this.muteChanges);
2166
+ l && this.lastStallMs > 0 && n - this.lastStallMs <= zt && (i.abandonAfterStallMs = Math.max(q, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= zt && (i.abandonAfterErrorMs = Math.max(q, Math.round(n - this.lastErrorMs))), this.fullscreenTotalMs > 0 && (i.fullscreenTimeMs = Math.round(this.fullscreenTotalMs)), this.pipTotalMs > 0 && (i.pipTimeMs = Math.round(this.pipTotalMs)), this.volumeChanges > 0 && (i.volumeChanges = this.volumeChanges), this.muteChanges > 0 && (i.muteChanges = this.muteChanges);
2138
2167
  let u = r?.decodedFrames ?? this.lastDecoded;
2139
2168
  u > 0 && (i.decodedFramesTotal = u), e === "END_REASON_STOPPED" && this.startupTimeMs === 0 && this.lastErrorStage === null && (e = "END_REASON_ABORTED", i.reason = e), this.startupTimeMs === 0 && e !== "END_REASON_ABORTED" && (i.failedStage = this.lastErrorStage ?? (this.manifestLoadMs === 0 ? "ERROR_STAGE_MANIFEST" : this.firstFragLoadMs === 0 ? "ERROR_STAGE_FIRST_SEGMENT" : "ERROR_STAGE_OTHER")), this.pending.push(this.wrap({ sessionEnd: i })), this.flush(e === "END_REASON_PAGEHIDE");
2140
2169
  }
@@ -2154,13 +2183,13 @@ var Jt = class {
2154
2183
  };
2155
2184
  }
2156
2185
  noteCspBlocked() {
2157
- this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(Wt(new URL(this.url).origin)));
2186
+ this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(Jt(new URL(this.url).origin)));
2158
2187
  }
2159
2188
  cspBlocked() {
2160
2189
  return !!this.cspKey && Y.has(this.cspKey);
2161
2190
  }
2162
2191
  enqueue(e) {
2163
- if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > Nt;) {
2192
+ if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > Lt;) {
2164
2193
  let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
2165
2194
  this.pending.splice(e >= 0 ? e : 0, 1);
2166
2195
  }
@@ -2187,7 +2216,7 @@ var Jt = class {
2187
2216
  };
2188
2217
  //#endregion
2189
2218
  //#region src/core/environment.ts
2190
- function Yt() {
2219
+ function $t() {
2191
2220
  if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
2192
2221
  let e;
2193
2222
  try {
@@ -2212,14 +2241,14 @@ function Yt() {
2212
2241
  integration: "INTEGRATION_KIND_IFRAME"
2213
2242
  };
2214
2243
  }
2215
- function Xt() {
2244
+ function en() {
2216
2245
  if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
2217
2246
  let e = navigator.userAgentData, t = e?.platform ?? "";
2218
2247
  if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
2219
2248
  let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
2220
2249
  return e?.mobile ?? (typeof navigator.maxTouchPoints == "number" && navigator.maxTouchPoints > 1 && n > 0 && n < 900) ? n >= 600 ? "DEVICE_CLASS_TABLET" : "DEVICE_CLASS_PHONE" : e || n >= 900 || /mac|win|linux|chrome os/i.test(t) ? "DEVICE_CLASS_DESKTOP" : "DEVICE_CLASS_OTHER";
2221
2250
  }
2222
- function Zt() {
2251
+ function tn() {
2223
2252
  let e = typeof navigator < "u" ? navigator.connection : void 0;
2224
2253
  if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
2225
2254
  let t = {
@@ -2238,17 +2267,17 @@ function Zt() {
2238
2267
  }
2239
2268
  return typeof e.downlink == "number" && e.downlink > 0 && (i.downlinkKbps = Math.round(e.downlink * 1e3)), typeof e.rtt == "number" && e.rtt > 0 && (i.rttMs = Math.round(e.rtt)), e.saveData && (i.saveData = !0), i;
2240
2269
  }
2241
- function Qt(e) {
2242
- let t = Yt(), n = {
2243
- deviceClass: Xt(),
2270
+ function nn(e) {
2271
+ let t = $t(), n = {
2272
+ deviceClass: en(),
2244
2273
  integration: t.integration,
2245
- ...Zt()
2274
+ ...tn()
2246
2275
  };
2247
2276
  t.host && (n.host = t.host), typeof screen < "u" && screen.width > 0 && (n.screenWidth = screen.width, n.screenHeight = screen.height), typeof window < "u" && window.devicePixelRatio > 0 && (n.devicePixelRatio = Math.round(window.devicePixelRatio * 100) / 100);
2248
2277
  let r = e.getBoundingClientRect?.();
2249
2278
  return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
2250
2279
  }
2251
- async function $t() {
2280
+ async function rn() {
2252
2281
  let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
2253
2282
  if (!e?.getHighEntropyValues) return {};
2254
2283
  try {
@@ -2260,7 +2289,7 @@ async function $t() {
2260
2289
  }
2261
2290
  //#endregion
2262
2291
  //#region src/core/version.ts
2263
- var en = "2.15.0", tn = {
2292
+ var an = "2.15.1", on = {
2264
2293
  accent: "#E1B600",
2265
2294
  accentText: "#1B1C23",
2266
2295
  background: "#0e0f13",
@@ -2271,7 +2300,7 @@ var en = "2.15.0", tn = {
2271
2300
  border: "rgba(255, 255, 255, 0.1)",
2272
2301
  radius: "8px",
2273
2302
  fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
2274
- }, nn = {
2303
+ }, sn = {
2275
2304
  accent: "--havik-accent",
2276
2305
  accentText: "--havik-accent-text",
2277
2306
  background: "--havik-bg",
@@ -2283,26 +2312,26 @@ var en = "2.15.0", tn = {
2283
2312
  radius: "--havik-radius",
2284
2313
  fontFamily: "--havik-font"
2285
2314
  };
2286
- function rn(e, t) {
2315
+ function cn(e, t) {
2287
2316
  let n = {
2288
- ...tn,
2317
+ ...on,
2289
2318
  ...t
2290
2319
  };
2291
- for (let t of Object.keys(nn)) e.style.setProperty(nn[t], n[t]);
2320
+ for (let t of Object.keys(sn)) e.style.setProperty(sn[t], n[t]);
2292
2321
  n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
2293
2322
  }
2294
2323
  //#endregion
2295
2324
  //#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 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;
2325
+ var ln = "havik-player-styles", un = "\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", dn = /* @__PURE__ */ new WeakMap();
2326
+ function fn(e = document, t) {
2327
+ let n = e.querySelector(`style#${ln}`);
2328
+ if (n && (!t || dn.get(n) === t)) return;
2300
2329
  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);
2330
+ r.id = ln, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = un, dn.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
2302
2331
  }
2303
2332
  //#endregion
2304
2333
  //#region src/ui/icons.ts
2305
- var ln = {
2334
+ var pn = {
2306
2335
  play: "<path d=\"M8 5v14l11-7z\"/>",
2307
2336
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
2308
2337
  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\"/>",
@@ -2316,20 +2345,20 @@ var ln = {
2316
2345
  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\"/>"
2317
2346
  };
2318
2347
  function Z(e) {
2319
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${ln[e]}</svg>`;
2348
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${pn[e]}</svg>`;
2320
2349
  }
2321
2350
  //#endregion
2322
2351
  //#region src/ui/controls.ts
2323
- var un = 3e3, dn = 250, fn = /* @__PURE__ */ new Set([
2352
+ var mn = 3e3, hn = 250, gn = /* @__PURE__ */ new Set([
2324
2353
  "GONE",
2325
2354
  "NOT_FOUND",
2326
2355
  "INVALID_URN",
2327
2356
  "UNAUTHORIZED"
2328
2357
  ]);
2329
- function pn(e) {
2330
- return new mn(e);
2358
+ function _n(e) {
2359
+ return new vn(e);
2331
2360
  }
2332
- var mn = class {
2361
+ var vn = class {
2333
2362
  root;
2334
2363
  video;
2335
2364
  player;
@@ -2375,7 +2404,7 @@ var mn = class {
2375
2404
  scrubbing = !1;
2376
2405
  offFns = [];
2377
2406
  constructor(e) {
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");
2407
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? mn, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, fn(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");
2379
2408
  let t = (e, t, n) => {
2380
2409
  let r = this.doc.createElement(e);
2381
2410
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -2461,7 +2490,7 @@ var mn = class {
2461
2490
  let t = this.surfaceWasHidden;
2462
2491
  this.tapTimer = setTimeout(() => {
2463
2492
  this.tapTimer = void 0, this.toggleBar(t);
2464
- }, dn);
2493
+ }, hn);
2465
2494
  }
2466
2495
  toggleBar(e) {
2467
2496
  e ? this.poke() : this.hideIfIdle();
@@ -2496,7 +2525,7 @@ var mn = class {
2496
2525
  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);
2497
2526
  } else {
2498
2527
  let r = this.doc.createElement("div");
2499
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !fn.has(this.lastError.code)) {
2528
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !gn.has(this.lastError.code)) {
2500
2529
  let e = this.doc.createElement("button");
2501
2530
  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);
2502
2531
  }
@@ -2537,7 +2566,7 @@ var mn = class {
2537
2566
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
2538
2567
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
2539
2568
  }
2540
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${hn(this.video.currentTime)} / ${hn(this.video.duration)}`);
2569
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${yn(this.video.currentTime)} / ${yn(this.video.duration)}`);
2541
2570
  }
2542
2571
  syncLive() {
2543
2572
  let e = this.player.getStats(), t = e.isLive !== !1;
@@ -2587,17 +2616,17 @@ var mn = class {
2587
2616
  renderQualityMenu() {
2588
2617
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
2589
2618
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
2590
- for (let n of e) this.menu.append(this.menuItem(gn(n), t === n.index, () => this.player.setQuality(n.index)));
2619
+ for (let n of e) this.menu.append(this.menuItem(bn(n), t === n.index, () => this.player.setQuality(n.index)));
2591
2620
  }
2592
2621
  renderAudioMenu() {
2593
2622
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
2594
2623
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
2595
- for (let t of e) this.menu.append(this.menuItem(_n(t), t.default, () => this.player.setAudioTrack(t.id)));
2624
+ for (let t of e) this.menu.append(this.menuItem(xn(t), t.default, () => this.player.setAudioTrack(t.id)));
2596
2625
  }
2597
2626
  renderCcMenu() {
2598
2627
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
2599
2628
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
2600
- for (let t of e) this.menu.append(this.menuItem(vn(t), !1, () => this.player.setTextTrack(t.id)));
2629
+ for (let t of e) this.menu.append(this.menuItem(Sn(t), !1, () => this.player.setTextTrack(t.id)));
2601
2630
  }
2602
2631
  onTracksChanged() {
2603
2632
  this.openMenu && this.renderMenu(this.openMenu);
@@ -2698,41 +2727,41 @@ var mn = class {
2698
2727
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
2699
2728
  }
2700
2729
  };
2701
- function hn(e) {
2730
+ function yn(e) {
2702
2731
  (!Number.isFinite(e) || e < 0) && (e = 0);
2703
2732
  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");
2704
2733
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
2705
2734
  }
2706
- function gn(e) {
2735
+ function bn(e) {
2707
2736
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
2708
2737
  }
2709
- function _n(e) {
2738
+ function xn(e) {
2710
2739
  return e.name || e.lang || `Track ${e.id}`;
2711
2740
  }
2712
- function vn(e) {
2741
+ function Sn(e) {
2713
2742
  return e.name || e.lang || `Track ${e.id}`;
2714
2743
  }
2715
2744
  //#endregion
2716
2745
  //#region src/managed/index.ts
2717
- var yn = 6e3, bn = 45e3, Q = 2e3, xn = 3, Sn = 2e3, Cn = 48e4;
2718
- function wn(e) {
2746
+ var Cn = 6e3, Q = 45e3, wn = 2e3, Tn = 3, En = 2e3, Dn = 48e4;
2747
+ function On(e) {
2719
2748
  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)})`);
2720
- if (e.env !== void 0) return Ve(e.env);
2749
+ if (e.env !== void 0) return B(e.env);
2721
2750
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
2722
2751
  return e.baseUrl;
2723
2752
  }
2724
- async function Tn(e) {
2753
+ async function kn(e) {
2725
2754
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
2726
- let t = wn(e);
2755
+ let t = On(e);
2727
2756
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
2728
2757
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
2729
- let r = new En({
2758
+ let r = new An({
2730
2759
  ...e,
2731
2760
  baseUrl: t
2732
2761
  });
2733
2762
  return await r.start(), r;
2734
2763
  }
2735
- var En = class {
2764
+ var An = class {
2736
2765
  opts;
2737
2766
  abort = new AbortController();
2738
2767
  listeners = {};
@@ -2776,7 +2805,7 @@ var En = class {
2776
2805
  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");
2777
2806
  let e = ++this.loadGen, t = this.resolveOptions();
2778
2807
  try {
2779
- let n = await y(t);
2808
+ let n = await v(t);
2780
2809
  if (await this.onResolved(n, e), e === this.loadGen && this._state === "error" && this._lastError) {
2781
2810
  let e = this._lastError;
2782
2811
  throw this.destroy(), e;
@@ -2803,7 +2832,7 @@ var En = class {
2803
2832
  r ? this.armWithSse(e, r, t, n) : this.beginArmingByPolling(e);
2804
2833
  }
2805
2834
  beginArmingByPolling(e) {
2806
- b({
2835
+ y({
2807
2836
  ...this.resolveOptions(),
2808
2837
  signal: this.abort.signal,
2809
2838
  waitForLive: this.buildWaitConfig(e)
@@ -2820,7 +2849,7 @@ var En = class {
2820
2849
  setTimeout(() => {
2821
2850
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
2822
2851
  phase: i ? "unavailable" : "tooEarly",
2823
- retryInMs: bn,
2852
+ retryInMs: Q,
2824
2853
  attempt: 1,
2825
2854
  elapsedMs: 0,
2826
2855
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -2837,7 +2866,7 @@ var En = class {
2837
2866
  }
2838
2867
  }
2839
2868
  }, h = () => {
2840
- a || s || p() || (s = !0, b({
2869
+ a || s || p() || (s = !0, y({
2841
2870
  ...this.resolveOptions(),
2842
2871
  signal: c.signal,
2843
2872
  waitForLive: this.buildWaitConfig(e)
@@ -2845,7 +2874,7 @@ var En = class {
2845
2874
  a || p() || c.signal.aborted || (a = !0, this.closeArming(), this.fail(e instanceof n ? e : new n("INTERNAL", 0, String(e))));
2846
2875
  }));
2847
2876
  }, g = () => {
2848
- a || p() || y(this.resolveOptions()).then((e) => m(e)).catch((e) => {
2877
+ a || p() || v(this.resolveOptions()).then((e) => m(e)).catch((e) => {
2849
2878
  if (!(a || p())) {
2850
2879
  if (e instanceof n && o(e.code)) {
2851
2880
  h();
@@ -2859,13 +2888,13 @@ var En = class {
2859
2888
  !a && !p() && h();
2860
2889
  }, e);
2861
2890
  };
2862
- _(yn), this.armingSub = T({
2891
+ _(Cn), this.armingSub = T({
2863
2892
  eventsBaseUrl: t,
2864
2893
  matchUrn: this.opts.matchUrn,
2865
2894
  credential: this.opts.credential,
2866
- onAlive: () => _(bn),
2895
+ onAlive: () => _(Q),
2867
2896
  onState: (e) => {
2868
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Q);
2897
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * wn);
2869
2898
  },
2870
2899
  onError: () => h()
2871
2900
  });
@@ -2888,7 +2917,7 @@ var En = class {
2888
2917
  let n = await s(this.opts.credential);
2889
2918
  if (this.destroyed || t !== this.loadGen) return;
2890
2919
  this.setState("loading");
2891
- let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = Ke(this.opts.video, (e) => {
2920
+ let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = qe(this.opts.video, (e) => {
2892
2921
  this._state !== "ended" && this._state !== "error" && this.setState(e);
2893
2922
  });
2894
2923
  if (await i.load(), this.destroyed || t !== this.loadGen) {
@@ -2910,26 +2939,26 @@ var En = class {
2910
2939
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
2911
2940
  let i = e.analytics?.sid;
2912
2941
  if (!i) return;
2913
- let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = qt(this.opts.baseUrl, a, e.analytics?.endpoint);
2942
+ let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = Zt(this.opts.baseUrl, a, e.analytics?.endpoint);
2914
2943
  if (!o) {
2915
2944
  s && setTimeout(() => {
2916
2945
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, s));
2917
2946
  }, 0);
2918
2947
  return;
2919
2948
  }
2920
- let c = this.opts.video, l = new Jt({
2949
+ let c = this.opts.video, l = new Qt({
2921
2950
  endpoint: o,
2922
2951
  sid: i,
2923
2952
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
2924
- sdkVersion: en,
2925
- environment: Qt(c),
2953
+ sdkVersion: an,
2954
+ environment: nn(c),
2926
2955
  idleMs: typeof this.opts.analytics == "object" ? this.opts.analytics.idleMs : void 0,
2927
2956
  onIdle: () => this.onAnalyticsIdle(t),
2928
2957
  onBlocked: (e) => setTimeout(() => {
2929
2958
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, e));
2930
2959
  }, 0),
2931
2960
  sample: () => {
2932
- let e = this.engine?.getStats() ?? He, t = c.buffered.length ? Math.max(0, c.buffered.end(c.buffered.length - 1) - c.currentTime) : 0;
2961
+ let e = this.engine?.getStats() ?? V, t = c.buffered.length ? Math.max(0, c.buffered.end(c.buffered.length - 1) - c.currentTime) : 0;
2933
2962
  return {
2934
2963
  ...e,
2935
2964
  bufferedMs: Math.round(t * 1e3),
@@ -2943,7 +2972,7 @@ var En = class {
2943
2972
  let u = () => l.noteSeek(), d = () => l.noteVolume(c.muted, c.volume);
2944
2973
  c.addEventListener("seeking", u), c.addEventListener("volumechange", d), this.detachMediaListeners = () => {
2945
2974
  c.removeEventListener("seeking", u), c.removeEventListener("volumechange", d);
2946
- }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), $t().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
2975
+ }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), rn().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
2947
2976
  }
2948
2977
  currentPresentation() {
2949
2978
  let e = this.opts.video;
@@ -2969,10 +2998,10 @@ var En = class {
2969
2998
  emit: (e) => this.onEngineEvent(e)
2970
2999
  }, o = this.opts.engine ?? "auto";
2971
3000
  if (o === "native") return new K(a);
2972
- if (o === "hls") return new kt(a);
2973
- if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && G(this.opts.video) && Je()) return new K(a);
2974
- if (at()) return new kt(a);
2975
- if (G(this.opts.video)) return new K(a);
3001
+ if (o === "hls") return new Nt(a);
3002
+ if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && W(this.opts.video) && G()) return new K(a);
3003
+ if (at()) return new Nt(a);
3004
+ if (W(this.opts.video)) return new K(a);
2976
3005
  throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
2977
3006
  }
2978
3007
  onEngineEvent(e) {
@@ -3024,7 +3053,7 @@ var En = class {
3024
3053
  r = 0, i = !1;
3025
3054
  },
3026
3055
  onError: () => {
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.`))));
3056
+ this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= Tn && (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.`))));
3028
3057
  }
3029
3058
  });
3030
3059
  }
@@ -3046,7 +3075,7 @@ var En = class {
3046
3075
  scheduleRejoin(e) {
3047
3076
  this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
3048
3077
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
3049
- }, Math.random() * Q));
3078
+ }, Math.random() * wn));
3050
3079
  }
3051
3080
  cancelRejoin() {
3052
3081
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -3070,7 +3099,7 @@ var En = class {
3070
3099
  }
3071
3100
  t.controls = !1;
3072
3101
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
3073
- r && r.insertBefore(n, t), n.appendChild(t), rn(n, this.opts.theme), this.container = n, this.controlBar = pn({
3102
+ r && r.insertBefore(n, t), n.appendChild(t), cn(n, this.opts.theme), this.container = n, this.controlBar = _n({
3074
3103
  root: n,
3075
3104
  video: t,
3076
3105
  player: this,
@@ -3101,7 +3130,7 @@ var En = class {
3101
3130
  }
3102
3131
  startStats() {
3103
3132
  this.stopStats();
3104
- let e = Math.max(500, this.opts.statsIntervalMs ?? Sn);
3133
+ let e = Math.max(500, this.opts.statsIntervalMs ?? En);
3105
3134
  this.statsTimer = setInterval(() => {
3106
3135
  if (!this.engine) return;
3107
3136
  let e = this.engine.getStats();
@@ -3113,7 +3142,7 @@ var En = class {
3113
3142
  }
3114
3143
  scheduleLicenseRefresh() {
3115
3144
  this.stopRefresh();
3116
- let e = this.opts.licenseRefreshMs ?? Cn;
3145
+ let e = this.opts.licenseRefreshMs ?? Dn;
3117
3146
  e && this._descriptor?.drmEnabled && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
3118
3147
  }
3119
3148
  stopRefresh() {
@@ -3122,7 +3151,7 @@ var En = class {
3122
3151
  async refreshLicense() {
3123
3152
  let e = this.engine;
3124
3153
  if (!this.destroyed && e) try {
3125
- let t = await y(this.resolveOptions());
3154
+ let t = await v(this.resolveOptions());
3126
3155
  if (this.destroyed || this.engine !== e) return;
3127
3156
  let n = t.drm?.widevine?.licenseUrl, r = t.drm?.fairplay?.licenseUrl;
3128
3157
  (n || r) && (e.setLicenseUrls({
@@ -3158,7 +3187,7 @@ var En = class {
3158
3187
  this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
3159
3188
  }
3160
3189
  getStats() {
3161
- return this.engine?.getStats() ?? He;
3190
+ return this.engine?.getStats() ?? V;
3162
3191
  }
3163
3192
  getQualityLevels() {
3164
3193
  return this.engine?.getQualityLevels() ?? [];
@@ -3210,7 +3239,7 @@ var En = class {
3210
3239
  let e = ++this.loadGen;
3211
3240
  this.teardownPlayback();
3212
3241
  try {
3213
- let t = await b({
3242
+ let t = await y({
3214
3243
  ...this.resolveOptions(),
3215
3244
  waitForLive: this.opts.waitForLive ? this.buildWaitConfig(e) : void 0
3216
3245
  });
@@ -3268,7 +3297,7 @@ function $(e) {
3268
3297
  }
3269
3298
  //#endregion
3270
3299
  //#region src/embed/host.ts
3271
- function Dn(e) {
3300
+ function jn(e) {
3272
3301
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
3273
3302
  if (!e.src) throw Error("mountEmbed: opts.src is required");
3274
3303
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -3327,15 +3356,15 @@ function Dn(e) {
3327
3356
  }
3328
3357
  //#endregion
3329
3358
  //#region src/embed/page.ts
3330
- var On = /^[A-Za-z0-9:_.-]{1,128}$/;
3331
- function kn(e) {
3359
+ var Mn = /^[A-Za-z0-9:_.-]{1,128}$/;
3360
+ function Nn(e) {
3332
3361
  if (typeof e == "boolean") return e;
3333
3362
  if (typeof e == "object" && e) {
3334
3363
  let t = e.endpoint;
3335
3364
  return typeof t == "string" ? { endpoint: t } : {};
3336
3365
  }
3337
3366
  }
3338
- function An() {
3367
+ function Pn() {
3339
3368
  let e = window.HAVIK_EMBED_CONFIG;
3340
3369
  if (e) return {
3341
3370
  baseUrl: e.baseUrl ?? "",
@@ -3345,7 +3374,7 @@ function An() {
3345
3374
  autoplay: e.autoplay ?? !0,
3346
3375
  muted: e.muted ?? !0,
3347
3376
  waitForLive: e.waitForLive ?? !0,
3348
- analytics: kn(e.analytics)
3377
+ analytics: Nn(e.analytics)
3349
3378
  };
3350
3379
  let t = new URLSearchParams(location.search);
3351
3380
  return {
@@ -3358,9 +3387,9 @@ function An() {
3358
3387
  waitForLive: !0
3359
3388
  };
3360
3389
  }
3361
- async function jn() {
3362
- let e = An();
3363
- Nn();
3390
+ async function Fn() {
3391
+ let e = Pn();
3392
+ Ln();
3364
3393
  let t = document.createElement("video");
3365
3394
  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);
3366
3395
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -3415,12 +3444,12 @@ async function jn() {
3415
3444
  code: e.code,
3416
3445
  message: e.message
3417
3446
  })), e.on("autoplayblocked", () => {
3418
- i({ type: "oddin:autoplayblocked" }), Mn(t, e);
3447
+ i({ type: "oddin:autoplayblocked" }), In(t, e);
3419
3448
  });
3420
3449
  }, s = async (r) => {
3421
3450
  a?.destroy(), a = null;
3422
3451
  try {
3423
- let n = await Tn({
3452
+ let n = await kn({
3424
3453
  video: t,
3425
3454
  baseUrl: e.baseUrl,
3426
3455
  matchUrn: r,
@@ -3482,7 +3511,7 @@ async function jn() {
3482
3511
  a?.retry();
3483
3512
  break;
3484
3513
  case "oddin:load":
3485
- typeof e.matchUrn == "string" && On.test(e.matchUrn) && s(e.matchUrn);
3514
+ typeof e.matchUrn == "string" && Mn.test(e.matchUrn) && s(e.matchUrn);
3486
3515
  break;
3487
3516
  case "oddin:destroy": c();
3488
3517
  }
@@ -3494,18 +3523,18 @@ async function jn() {
3494
3523
  protocol: 1
3495
3524
  });
3496
3525
  }
3497
- function Mn(e, t) {
3526
+ function In(e, t) {
3498
3527
  if (document.getElementById("havik-tap")) return;
3499
3528
  let n = document.createElement("button");
3500
3529
  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", () => {
3501
3530
  n.remove(), t.play();
3502
3531
  }), document.body.appendChild(n);
3503
3532
  }
3504
- function Nn() {
3533
+ function Ln() {
3505
3534
  let e = document.createElement("style");
3506
3535
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
3507
3536
  }
3508
3537
  //#endregion
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 };
3538
+ export { on as ODDIN_THEME, n as PlaybackError, cn as applyTheme, B as baseUrlForEnv, Fn as bootEmbed, Te as clearDeviceId, kn 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, jn as mountEmbed, de as pollToLive, y as resolveStream, T as subscribeLiveState, S as videoFullscreenActive, Ce as watchStatus };
3510
3539
 
3511
3540
  //# sourceMappingURL=index.js.map