@oddin-gg/havik-player 2.16.12 → 2.17.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
@@ -470,6 +470,7 @@ function T(e) {
470
470
  clearTimeout(n), t();
471
471
  }, { once: !0 });
472
472
  }), l = (t) => {
473
+ if (o) return;
473
474
  e.onAlive?.();
474
475
  let n = "message", r = [];
475
476
  for (let e of t.split("\n")) {
@@ -671,14 +672,14 @@ function I(e) {
671
672
  function L(e) {
672
673
  return (e ?? "").trim().toLowerCase();
673
674
  }
674
- var je = {
675
+ var R = {
675
676
  hevc: "hvc1.1.6.L93.90",
676
677
  dv: "dvh1.05.06",
677
678
  av1: "av01.0.05M.08",
678
679
  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);
680
+ }, je = /* @__PURE__ */ new Map(), z = /* @__PURE__ */ new Map();
681
+ function Me(e) {
682
+ let t = L(e), n = je.get(t);
682
683
  if (n) return n;
683
684
  let r = {
684
685
  initDataTypes: ["cenc"],
@@ -696,23 +697,23 @@ function Ne(e) {
696
697
  } catch {
697
698
  return !1;
698
699
  }
699
- })().then((e) => (R.set(t, e), e));
700
- return Me.set(t, i), i;
700
+ })().then((e) => (z.set(t, e), e));
701
+ return je.set(t, i), i;
701
702
  }
702
- function Pe() {
703
- if (j()) for (let e of Object.values(je)) Ne(e);
703
+ function Ne() {
704
+ if (j()) for (let e of Object.values(R)) Me(e);
704
705
  }
705
- function Fe(e) {
706
+ function Pe(e) {
706
707
  let t = I(e);
707
- if (t) return t === "avc" ? !0 : R.get(L(e)) ?? R.get(L(je[t]));
708
+ if (t) return t === "avc" ? !0 : z.get(L(e)) ?? z.get(L(R[t]));
708
709
  }
709
710
  //#endregion
710
711
  //#region src/core/drm/support.ts
711
- var Ie = 5e3, Le = "video/mp4; codecs=\"avc1.42E01E\"", Re = "audio/mp4; codecs=\"mp4a.40.2\"", ze = {
712
+ var Fe = 5e3, Ie = "video/mp4; codecs=\"avc1.42E01E\"", Le = "audio/mp4; codecs=\"mp4a.40.2\"", Re = {
712
713
  widevine: ["com.widevine.alpha"],
713
714
  fairplay: ["com.apple.fps"]
714
715
  };
715
- function Be(e) {
716
+ function ze(e) {
716
717
  return e === "fairplay" ? [{
717
718
  initDataTypes: [
718
719
  "sinf",
@@ -720,30 +721,30 @@ function Be(e) {
720
721
  "cenc"
721
722
  ],
722
723
  videoCapabilities: [{
723
- contentType: Le,
724
+ contentType: Ie,
724
725
  robustness: "",
725
726
  encryptionScheme: "cbcs"
726
727
  }],
727
728
  audioCapabilities: [{
728
- contentType: Re,
729
+ contentType: Le,
729
730
  robustness: "",
730
731
  encryptionScheme: "cbcs"
731
732
  }]
732
733
  }] : N("com.widevine.alpha", [{
733
734
  initDataTypes: ["cenc"],
734
735
  videoCapabilities: [{
735
- contentType: Le,
736
+ contentType: Ie,
736
737
  robustness: F.video,
737
738
  encryptionScheme: F.scheme
738
739
  }],
739
740
  audioCapabilities: [{
740
- contentType: Re,
741
+ contentType: Le,
741
742
  robustness: F.audio,
742
743
  encryptionScheme: F.scheme
743
744
  }]
744
745
  }]);
745
746
  }
746
- function Ve(e, t) {
747
+ function Be(e, t) {
747
748
  return new Promise((n, r) => {
748
749
  let i = setTimeout(() => r(/* @__PURE__ */ Error("probe-timeout")), t);
749
750
  e.then((e) => {
@@ -753,8 +754,8 @@ function Ve(e, t) {
753
754
  });
754
755
  });
755
756
  }
756
- async function He(e = {}) {
757
- let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? Ie, r = {};
757
+ async function Ve(e = {}) {
758
+ let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? Fe, r = {};
758
759
  if (typeof window < "u" && window.isSecureContext === !1) return {
759
760
  verdict: "insecure-context",
760
761
  detail: r
@@ -766,7 +767,7 @@ async function He(e = {}) {
766
767
  };
767
768
  let a = !1, o = !1, s = Date.now() + n, c = () => Math.max(1, s - Date.now());
768
769
  for (let e of t) {
769
- let t = ze[e];
770
+ let t = Re[e];
770
771
  if (!t) {
771
772
  r[e] = "unknown system";
772
773
  continue;
@@ -777,7 +778,7 @@ async function He(e = {}) {
777
778
  break;
778
779
  }
779
780
  try {
780
- return await Ve((await Ve(i(n, Be(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
781
+ return await Be((await Be(i(n, ze(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
781
782
  verdict: "supported",
782
783
  system: e,
783
784
  detail: r
@@ -801,49 +802,49 @@ async function He(e = {}) {
801
802
  }
802
803
  //#endregion
803
804
  //#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);
805
+ var He = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
806
+ function Ue(e) {
807
+ return He.test(e);
807
808
  }
808
809
  //#endregion
809
810
  //#region src/core/env.ts
810
- var Ge = {
811
+ var We = {
811
812
  integration: "https://feed-dev.oddin-video.gg",
812
813
  production: "https://feed.oddin-video.gg"
813
814
  };
814
- function Ke(e) {
815
- let t = Object.prototype.hasOwnProperty.call(Ge, e) ? Ge[e] : void 0;
815
+ function Ge(e) {
816
+ let t = Object.prototype.hasOwnProperty.call(We, e) ? We[e] : void 0;
816
817
  if (!t) throw new n("INTERNAL", 0, `unknown env ${JSON.stringify(e)} — expected 'integration' or 'production'`);
817
818
  return t;
818
819
  }
819
820
  //#endregion
820
821
  //#region src/core/stats.ts
821
- var qe = { droppedFrames: 0 };
822
- function Je(e) {
822
+ var Ke = { droppedFrames: 0 };
823
+ function qe(e) {
823
824
  try {
824
825
  return new URL(e).host;
825
826
  } catch {
826
827
  return "";
827
828
  }
828
829
  }
829
- var Ye = /\.(m3u8|ts|m4s|mp4|cmfv|cmfa)(\?|$)/i;
830
- function Xe(e) {
830
+ var Je = /\.(m3u8|ts|m4s|mp4|cmfv|cmfa)(\?|$)/i;
831
+ function Ye(e) {
831
832
  if (typeof performance > "u" || typeof performance.getEntriesByType != "function") return "";
832
833
  let t = "", n = -1, r = performance.getEntriesByType("resource");
833
- for (let i of r) if (!(i.startTime < e) && (i.initiatorType === "video" || i.initiatorType === "audio" || Ye.test(i.name)) && i.startTime >= n) {
834
- let e = Je(i.name);
834
+ for (let i of r) if (!(i.startTime < e) && (i.initiatorType === "video" || i.initiatorType === "audio" || Je.test(i.name)) && i.startTime >= n) {
835
+ let e = qe(i.name);
835
836
  e && (t = e, n = i.startTime);
836
837
  }
837
838
  return t;
838
839
  }
839
- function z(e) {
840
+ function B(e) {
840
841
  let t = e.getVideoPlaybackQuality?.();
841
842
  return {
842
843
  dropped: t?.droppedVideoFrames ?? 0,
843
844
  totalFrames: t?.totalVideoFrames ?? 0
844
845
  };
845
846
  }
846
- var B = class {
847
+ var Xe = class {
847
848
  video;
848
849
  baseFrames = 0;
849
850
  baseDropped = 0;
@@ -853,7 +854,7 @@ var B = class {
853
854
  this.video = e;
854
855
  }
855
856
  read() {
856
- let e = z(this.video);
857
+ let e = B(this.video);
857
858
  return e.totalFrames < this.lastFrames && (this.baseFrames += this.lastFrames), e.dropped < this.lastDropped && (this.baseDropped += this.lastDropped), this.lastFrames = e.totalFrames, this.lastDropped = e.dropped, {
858
859
  dropped: this.baseDropped + e.dropped,
859
860
  totalFrames: this.baseFrames + e.totalFrames
@@ -883,8 +884,8 @@ function Ze(e, t) {
883
884
  }
884
885
  //#endregion
885
886
  //#region src/core/engine/native.ts
886
- var Qe = 2e3, $e = 3, V = 500, et = 6e3, tt = .25, nt = 250, rt = .05, it = 4;
887
- function at(e) {
887
+ var Qe = 2e3, $e = 3, V = 500, et = 6e3, tt = .25, nt = 250, rt = .05, it = 4, at = 12e3, ot = .25, st = 1e3;
888
+ function ct(e) {
888
889
  try {
889
890
  let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
890
891
  return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
@@ -900,7 +901,7 @@ function U() {
900
901
  let e = "WebKitMediaKeys" in window || typeof HTMLMediaElement < "u" && "webkitSetMediaKeys" in HTMLMediaElement.prototype, t = typeof navigator < "u" && /Apple/i.test(navigator.vendor ?? "");
901
902
  return e || t;
902
903
  }
903
- function ot() {
904
+ function lt() {
904
905
  if (typeof navigator > "u" || !U()) return !1;
905
906
  let e = navigator.userAgent ?? "";
906
907
  return /iPhone|iPad|iPod/.test(e) ? !0 : /Macintosh/.test(e) && typeof navigator.maxTouchPoints == "number" && navigator.maxTouchPoints > 1;
@@ -922,6 +923,10 @@ var W = class {
922
923
  onEdgeReset = null;
923
924
  lastBufferedEnd = null;
924
925
  bufferedAdvancedAt = null;
926
+ lastSeekableEnd = null;
927
+ edgeStampedAt = null;
928
+ lastTickAt = null;
929
+ endedEmitted = !1;
925
930
  fairplayKeyIV;
926
931
  loadStarted;
927
932
  preloadTouched = !1;
@@ -931,12 +936,12 @@ var W = class {
931
936
  frameCounter;
932
937
  detachFrames = null;
933
938
  constructor(e) {
934
- this.deps = e, this.frameCounter = new B(e.video), this.loadStarted = e.deferLoad !== !0, this.builtAt = typeof performance < "u" ? performance.now() : 0;
939
+ this.deps = e, this.frameCounter = new Xe(e.video), this.loadStarted = e.deferLoad !== !0, this.builtAt = typeof performance < "u" ? performance.now() : 0;
935
940
  }
936
941
  async load() {
937
942
  this.detachFrames ??= this.frameCounter.attach();
938
943
  let { video: e, descriptor: t, emit: r } = this.deps;
939
- if (this.watchRefocus(), this.watchBufferedEnd(), this.loadStarted || (this.preloadTouched = !0, this.savedPreload = e.getAttribute("preload"), e.preload = "none"), t.drmEnabled) {
944
+ if (this.endedEmitted = !1, this.watchRefocus(), this.watchBufferedEnd(), this.loadStarted || (this.preloadTouched = !0, this.savedPreload = e.getAttribute("preload"), e.preload = "none"), t.drmEnabled) {
940
945
  if (!t.drm?.fairplay) {
941
946
  r({
942
947
  type: "error",
@@ -952,7 +957,7 @@ var W = class {
952
957
  type: "error",
953
958
  error: new n("INTERNAL", 0, "native HLS playback error"),
954
959
  fatal: !0
955
- }), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = ct(t.manifestUrl, t.analytics?.sid), e.load();
960
+ }), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = dt(t.manifestUrl, t.analytics?.sid), e.load();
956
961
  }
957
962
  startLoad() {
958
963
  this.loadStarted || (this.loadStarted = !0, this.restorePreload());
@@ -974,7 +979,7 @@ var W = class {
974
979
  });
975
980
  this.onEncrypted = (n) => {
976
981
  let i = n;
977
- i.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = at(i.initData), (async () => {
982
+ i.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = ct(i.initData), (async () => {
978
983
  try {
979
984
  let n = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
980
985
  initDataTypes: [i.initDataType],
@@ -1073,7 +1078,7 @@ var W = class {
1073
1078
  droppedFrames: t.dropped,
1074
1079
  decodedFrames: t.totalFrames
1075
1080
  };
1076
- if (this.cdnHost ||= Xe(this.builtAt), this.cdnHost && (n.cdnHost = this.cdnHost), this.deps.descriptor.drmEnabled && (n.keySystem = "fairplay"), e.duration === Infinity) {
1081
+ if (this.cdnHost ||= Ye(this.builtAt), this.cdnHost && (n.cdnHost = this.cdnHost), this.deps.descriptor.drmEnabled && (n.keySystem = "fairplay"), e.duration === Infinity) {
1077
1082
  n.isLive = !0;
1078
1083
  let t = this.liveEdgeEstimate(), r = t == null ? NaN : t - e.currentTime;
1079
1084
  Number.isFinite(r) && (n.atLiveEdge = r <= it, !e.paused && r >= 0 && (n.latencySeconds = r));
@@ -1119,21 +1124,43 @@ var W = class {
1119
1124
  watchBufferedEnd() {
1120
1125
  if (this.edgeTimer != null) return;
1121
1126
  let { video: e } = this.deps;
1122
- this.onEdgeReset = () => {
1123
- this.lastBufferedEnd = null, this.bufferedAdvancedAt = null;
1127
+ this.onEdgeReset = (e) => {
1128
+ this.lastBufferedEnd = null, this.bufferedAdvancedAt = null, e.type === "emptied" && this.resetEndStale();
1124
1129
  }, e.addEventListener("seeking", this.onEdgeReset), e.addEventListener("emptied", this.onEdgeReset), this.edgeTimer = setInterval(() => {
1125
1130
  if (e.duration !== Infinity) return;
1126
- let t = st(e, e.currentTime);
1131
+ this.checkEndStale();
1132
+ let t = ut(e, e.currentTime);
1127
1133
  t != null && ((this.lastBufferedEnd == null || Math.abs(t - this.lastBufferedEnd) >= rt) && (this.bufferedAdvancedAt = G()), this.lastBufferedEnd = t);
1128
1134
  }, nt);
1129
1135
  }
1136
+ checkEndStale() {
1137
+ if (this.endedEmitted) return;
1138
+ let e = G(), t = this.lastTickAt == null ? 0 : e - this.lastTickAt;
1139
+ this.lastTickAt = e;
1140
+ let n = this.liveEnd();
1141
+ if (n == null) return;
1142
+ let r = this.lastSeekableEnd == null || Math.abs(n - this.lastSeekableEnd) >= ot;
1143
+ this.lastSeekableEnd = n;
1144
+ let i = this.deps.video, a = typeof document < "u" && document.visibilityState === "hidden";
1145
+ if (r || t > st || a || i.paused) {
1146
+ this.edgeStampedAt = e;
1147
+ return;
1148
+ }
1149
+ e - (this.edgeStampedAt ?? e) > at && (this.endedEmitted = !0, this.deps.emit({
1150
+ type: "ended",
1151
+ reason: "stalled"
1152
+ }));
1153
+ }
1154
+ resetEndStale() {
1155
+ this.lastSeekableEnd = null, this.edgeStampedAt = null, this.lastTickAt = null;
1156
+ }
1130
1157
  unwatchBufferedEnd() {
1131
- this.edgeTimer != null && clearInterval(this.edgeTimer), this.edgeTimer = null, this.onEdgeReset &&= (this.deps.video.removeEventListener("seeking", this.onEdgeReset), this.deps.video.removeEventListener("emptied", this.onEdgeReset), null), this.lastBufferedEnd = null, this.bufferedAdvancedAt = null;
1158
+ this.edgeTimer != null && clearInterval(this.edgeTimer), this.edgeTimer = null, this.onEdgeReset &&= (this.deps.video.removeEventListener("seeking", this.onEdgeReset), this.deps.video.removeEventListener("emptied", this.onEdgeReset), null), this.resetEndStale(), this.lastBufferedEnd = null, this.bufferedAdvancedAt = null;
1132
1159
  }
1133
1160
  liveEdgeEstimate() {
1134
1161
  let e = this.deps.video;
1135
1162
  if (e.duration !== Infinity) return null;
1136
- let t = this.liveEnd(), n = st(e, e.currentTime) ?? this.lastBufferedEnd;
1163
+ let t = this.liveEnd(), n = ut(e, e.currentTime) ?? this.lastBufferedEnd;
1137
1164
  if (n != null) {
1138
1165
  let e = this.bufferedAdvancedAt, r = n + (e == null ? 0 : Math.max(0, (G() - e) / 1e3));
1139
1166
  (t == null || r > t) && (t = r);
@@ -1170,7 +1197,7 @@ var W = class {
1170
1197
  function G() {
1171
1198
  return typeof performance < "u" ? performance.now() : Date.now();
1172
1199
  }
1173
- function st(e, t) {
1200
+ function ut(e, t) {
1174
1201
  let n = e.buffered;
1175
1202
  if (!n || !Number.isFinite(t)) return null;
1176
1203
  for (let e = 0; e < n.length; e += 1) if (n.start(e) <= t + .1 && n.end(e) > t) {
@@ -1179,7 +1206,7 @@ function st(e, t) {
1179
1206
  }
1180
1207
  return null;
1181
1208
  }
1182
- function ct(e, t) {
1209
+ function dt(e, t) {
1183
1210
  return t ? `${e}${e.includes("?") ? "&" : "?"}CMCD=${encodeURIComponent(`sid="${t}"`)}` : e;
1184
1211
  }
1185
1212
  //#endregion
@@ -1196,8 +1223,8 @@ function K(e, t = Date.now()) {
1196
1223
  }
1197
1224
  //#endregion
1198
1225
  //#region src/core/engine/prefetch.ts
1199
- var lt = 15e3, ut = 5e3, dt = 6;
1200
- function ft(e) {
1226
+ var ft = 15e3, pt = 5e3, mt = 6;
1227
+ function ht(e) {
1201
1228
  try {
1202
1229
  let t = new URL(e);
1203
1230
  return t.searchParams.delete("CMCD"), t.toString();
@@ -1205,7 +1232,7 @@ function ft(e) {
1205
1232
  return e;
1206
1233
  }
1207
1234
  }
1208
- var pt = class {
1235
+ var gt = class {
1209
1236
  cmcd;
1210
1237
  entries = /* @__PURE__ */ new Map();
1211
1238
  keyed = /* @__PURE__ */ new Set();
@@ -1228,10 +1255,10 @@ var pt = class {
1228
1255
  }
1229
1256
  prefetch(e, t, n) {
1230
1257
  if (!this.isActive(t)) return;
1231
- let r = ft(e);
1258
+ let r = ht(e);
1232
1259
  if (this.entries.has(r)) return;
1233
1260
  let i = this.perTrack.get(t) ?? 0;
1234
- if (i >= dt) return;
1261
+ if (i >= mt) return;
1235
1262
  this.perTrack.set(t, i + 1);
1236
1263
  let a = new AbortController(), o = {
1237
1264
  started: performance.now(),
@@ -1245,10 +1272,10 @@ var pt = class {
1245
1272
  signal: a.signal
1246
1273
  }).then(async (e) => (o.first = performance.now(), e.ok ? e.arrayBuffer() : null)).catch(() => null), this.entries.set(r, o), this.stats.started += 1, setTimeout(() => {
1247
1274
  this.entries.get(r) === o && (this.entries.delete(r), a.abort(), this.stats.expired += 1);
1248
- }, lt);
1275
+ }, ft);
1249
1276
  }
1250
1277
  take(e) {
1251
- let t = ft(e), n = this.entries.get(t);
1278
+ let t = ht(e), n = this.entries.get(t);
1252
1279
  return n && this.entries.delete(t), n;
1253
1280
  }
1254
1281
  destroy() {
@@ -1268,13 +1295,13 @@ var pt = class {
1268
1295
  }
1269
1296
  }
1270
1297
  };
1271
- function mt(t) {
1298
+ function _t(t) {
1272
1299
  let n = e.DefaultConfig?.loader;
1273
1300
  if (typeof n != "function") return;
1274
1301
  class r extends n {
1275
1302
  served = !1;
1276
1303
  cancelled = !1;
1277
- stats = ht();
1304
+ stats = vt();
1278
1305
  load(e, n, r) {
1279
1306
  let i = e.rangeEnd ? void 0 : t.take(e.url);
1280
1307
  if (!i) {
@@ -1282,7 +1309,7 @@ function mt(t) {
1282
1309
  return;
1283
1310
  }
1284
1311
  this.served = !0;
1285
- let a = performance.now(), o = n?.loadPolicy?.maxTimeToFirstByteMs || ut, s = setTimeout(() => {
1312
+ let a = performance.now(), o = n?.loadPolicy?.maxTimeToFirstByteMs || pt, s = setTimeout(() => {
1286
1313
  !this.cancelled && this.served && (i.abort.abort(), t.stats.failed += 1, this.served = !1, super.load(e, n, r));
1287
1314
  }, o);
1288
1315
  i.bytes.then((o) => {
@@ -1309,7 +1336,7 @@ function mt(t) {
1309
1336
  }
1310
1337
  return r;
1311
1338
  }
1312
- function ht() {
1339
+ function vt() {
1313
1340
  return {
1314
1341
  aborted: !1,
1315
1342
  loaded: 0,
@@ -1333,10 +1360,10 @@ function ht() {
1333
1360
  }
1334
1361
  };
1335
1362
  }
1336
- function gt(e) {
1363
+ function q(e) {
1337
1364
  return e === "audio" ? "audio" : "main";
1338
1365
  }
1339
- function _t(e, t) {
1366
+ function yt(e, t) {
1340
1367
  let n = [];
1341
1368
  t.initSegment?.url && !t.initSegment.data && n.push({
1342
1369
  url: t.initSegment.url,
@@ -1355,24 +1382,24 @@ function _t(e, t) {
1355
1382
  }
1356
1383
  //#endregion
1357
1384
  //#region src/core/engine/hls.ts
1358
- function vt() {
1385
+ function bt() {
1359
1386
  return e.isSupported();
1360
1387
  }
1361
- var yt = 3, bt = 5, xt = 3, St = 1e4, Ct = 3e4, wt = 12e3, Tt = 3e3, Et = .02, Dt = 6e3, Ot = .5, kt = 3, At = .5, jt = 1.1, Mt = .25, Nt = 5e3, Pt = 4e3, Ft = 2, It = 5, Lt = 2, Rt = 2, zt = 4, Bt = 4, Vt = 2e3, Ht = 3e4, Ut = .2, Wt = 30, Gt = 2, Kt = 2, qt = 9, Jt = .5, Yt = 64;
1362
- function Xt(e) {
1388
+ var xt = 3, St = 5, Ct = 3, wt = 1e4, Tt = 3e4, Et = 12e3, Dt = 3e3, Ot = .02, kt = 6e3, At = .5, jt = 3, Mt = .5, Nt = 1.1, Pt = .25, Ft = 5e3, It = 4e3, Lt = 2, Rt = 5, zt = 2, Bt = 2, Vt = 4, Ht = 4, Ut = 2e3, Wt = 3e4, Gt = .2, Kt = 30, qt = 2, Jt = 2, Yt = 9, Xt = .5, Zt = 64;
1389
+ function Qt(e) {
1363
1390
  if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
1364
1391
  delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
1365
1392
  return;
1366
1393
  }
1367
- e.liveSyncDuration ??= Kt;
1368
- let t = Math.max(12, e.liveSyncDuration + qt);
1369
- (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Zt(e, e.liveSyncDuration);
1394
+ e.liveSyncDuration ??= Jt;
1395
+ let t = Math.max(12, e.liveSyncDuration + Yt);
1396
+ (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), $t(e, e.liveSyncDuration);
1370
1397
  }
1371
- function Zt(e, t) {
1398
+ function $t(e, t) {
1372
1399
  let n = (e, t, n) => Math.min(n, Math.max(t, e));
1373
1400
  e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
1374
1401
  }
1375
- var Qt = class {
1402
+ var en = class {
1376
1403
  name = "hls";
1377
1404
  hls = null;
1378
1405
  deps;
@@ -1438,6 +1465,8 @@ var Qt = class {
1438
1465
  capTimer = null;
1439
1466
  sizeCapLevel = null;
1440
1467
  bitrateCapLevel = -1;
1468
+ pushedCapLevel = -1;
1469
+ pushedCapReason = null;
1441
1470
  fpsCapBitrate = null;
1442
1471
  fpsCapFailedHeight = null;
1443
1472
  fpsCapManaged = !0;
@@ -1452,7 +1481,7 @@ var Qt = class {
1452
1481
  frameCounter;
1453
1482
  detachFrames = null;
1454
1483
  constructor(e) {
1455
- this.deps = e, this.frameCounter = new B(e.video), this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl, this.loadStarted = e.deferLoad !== !0;
1484
+ this.deps = e, this.frameCounter = new Xe(e.video), this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl, this.loadStarted = e.deferLoad !== !0;
1456
1485
  }
1457
1486
  async load() {
1458
1487
  this.detachFrames ??= this.frameCounter.attach();
@@ -1465,7 +1494,7 @@ var Qt = class {
1465
1494
  });
1466
1495
  return;
1467
1496
  }
1468
- e.drmEnabled && o && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && (Ae(r), Pe()), this.startHls(), t !== !1 && typeof document < "u" && (this.onVisibility = () => {
1497
+ e.drmEnabled && o && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && (Ae(r), Ne()), this.startHls(), t !== !1 && typeof document < "u" && (this.onVisibility = () => {
1469
1498
  let e = this.hls;
1470
1499
  if (document.visibilityState !== "visible" || !e) return;
1471
1500
  let t = e.liveSyncPosition;
@@ -1488,7 +1517,7 @@ var Qt = class {
1488
1517
  }
1489
1518
  this.loadSuspended = !0;
1490
1519
  }
1491
- }, Nt));
1520
+ }, Ft));
1492
1521
  }
1493
1522
  resumeLoading() {
1494
1523
  if (this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.resetChaseWindow(), this.resetFrameDropWindow(), this.loadSuspended && (this.loadSuspended = !1, !this.stopped && this.hls)) {
@@ -1520,7 +1549,7 @@ var Qt = class {
1520
1549
  let t = e.liveSyncPosition;
1521
1550
  if (typeof t != "number" || !Number.isFinite(t)) return;
1522
1551
  let { video: n } = this.deps;
1523
- n.currentTime <= t + Jt || (n.currentTime = t);
1552
+ n.currentTime <= t + Xt || (n.currentTime = t);
1524
1553
  }
1525
1554
  reanchorPartTimeline(e, t) {
1526
1555
  let { frag: n, part: r } = t;
@@ -1532,7 +1561,7 @@ var Qt = class {
1532
1561
  let o = n.sn, s = i.fragments.find((e) => e && e.sn === o) ?? (i.fragmentHint?.sn === o ? i.fragmentHint : void 0);
1533
1562
  if (!s) return;
1534
1563
  let c = a.startPTS - s.start;
1535
- if (!(!Number.isFinite(c) || Math.abs(c) < Et)) {
1564
+ if (!(!Number.isFinite(c) || Math.abs(c) < Ot)) {
1536
1565
  for (let e of i.fragments) e && e.setStart(e.start + c);
1537
1566
  i.fragmentHint?.setStart(i.fragmentHint.start + c), this.deps.debug === !0 && console.info(`havik-player: LL-HLS timeline re-anchored by ${c >= 0 ? "+" : ""}${c.toFixed(3)}s at sn ${n.sn} (level ${n.level})`);
1538
1567
  }
@@ -1572,7 +1601,7 @@ var Qt = class {
1572
1601
  return;
1573
1602
  }
1574
1603
  let l = this.hls, u, d = new Promise((e) => {
1575
- u = setTimeout(() => e(void 0), St);
1604
+ u = setTimeout(() => e(void 0), wt);
1576
1605
  });
1577
1606
  return Promise.race([c().catch(() => void 0), d]).finally(() => clearTimeout(u)).then((e) => {
1578
1607
  if (!this.hls || this.hls !== l) throw Error("havik-player: engine torn down during refresh");
@@ -1588,20 +1617,20 @@ var Qt = class {
1588
1617
  ...c
1589
1618
  };
1590
1619
  if (this.prefetch?.destroy(), this.prefetch = null, u && this.deps.mediaDuringKey !== !1 && !f.fLoader) {
1591
- let e = new pt({
1620
+ let e = new gt({
1592
1621
  sessionId: n.analytics?.sid,
1593
1622
  contentId: n.matchUrn
1594
- }), t = mt(e);
1623
+ }), t = _t(e);
1595
1624
  t && (this.prefetch = e, f.fLoader = t);
1596
1625
  }
1597
- Xt(f), this.catchUpRate = typeof f.maxLiveSyncPlaybackRate == "number" ? f.maxLiveSyncPlaybackRate : 1, this.catchUpEngaged = !1, this.catchUpRate > 1 && (f.maxLiveSyncPlaybackRate = 1), this.sizeCapManaged = f.capLevelToPlayerSize !== !0, this.fpsCapManaged = !(c && "capLevelOnFPSDrop" in c), this.loadStarted = f.autoStartLoad !== !1;
1626
+ Qt(f), this.catchUpRate = typeof f.maxLiveSyncPlaybackRate == "number" ? f.maxLiveSyncPlaybackRate : 1, this.catchUpEngaged = !1, this.catchUpRate > 1 && (f.maxLiveSyncPlaybackRate = 1), this.sizeCapManaged = f.capLevelToPlayerSize !== !0, this.fpsCapManaged = !(c && "capLevelOnFPSDrop" in c), this.loadStarted = f.autoStartLoad !== !1;
1598
1627
  let p = new e(f);
1599
1628
  if (this.hls = p, p.on(e.Events.FRAG_PARSED, (e, t) => this.reanchorPartTimeline(p, t)), p.on(e.Events.MANIFEST_PARSED, () => {
1600
1629
  this.mediaRecoveries = 0, this.networkRecoveries = 0, u && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && this.dropLevelsWidevineCannotPlay(p), this.measureLadder(p), this.applySizeCap(), this.loadSuspended = !1, t.paused && this.schedulePauseSuspend(), l({ type: "ready" });
1601
1630
  }), p.on(e.Events.FRAG_BUFFERED, (e, t) => {
1602
1631
  t?.frag?.type === "main" && (this.kickKind !== "dead" && this.clearKick(), (this.mediaRecoveries > 0 || this.networkRecoveries > 0 || this.licenseRecoveries > 0 || this.starvationKicks > 0 || this.starvationRebuilds > 0) && !this.recoveryResetTimer && (this.recoveryResetTimer = setTimeout(() => {
1603
1632
  this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.starvationRebuilds = 0, this.recoveryResetTimer = null;
1604
- }, Ct)));
1633
+ }, Tt)));
1605
1634
  }), p.on(e.Events.FRAG_LOADING, (e, t) => {
1606
1635
  t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
1607
1636
  }), p.on(e.Events.FRAG_LOADED, (e, t) => {
@@ -1609,10 +1638,10 @@ var Qt = class {
1609
1638
  let n = t?.frag?.stats, r = n?.loaded;
1610
1639
  typeof r == "number" && (this.bytesLoaded += r), !this.reportedFirstBytes && typeof r == "number" && r > 0 && (this.reportedFirstBytes = !0, l({ type: "firstBytes" }));
1611
1640
  let i = n?.loading;
1612
- 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 > Yt && this.requestTimesMs.shift());
1641
+ 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 > Zt && this.requestTimesMs.shift());
1613
1642
  let a = t?.frag?.url;
1614
1643
  if (a) {
1615
- let e = Je(a);
1644
+ let e = qe(a);
1616
1645
  e && (this.cdnHost = e);
1617
1646
  }
1618
1647
  }), p.on(e.Events.LEVEL_LOADED, (e, t) => {
@@ -1626,15 +1655,15 @@ var Qt = class {
1626
1655
  }), p.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.prefetch) {
1627
1656
  let t = this.prefetch;
1628
1657
  p.on(e.Events.KEY_LOADING, (e, n) => {
1629
- let r = n?.frag, i = gt(r?.type);
1630
- if (r && t.isActive(i)) for (let e of _t(p, r)) t.prefetch(e.url, i, e.kind);
1631
- }), p.on(e.Events.KEY_LOADED, (e, n) => t.keyUsable(gt(n?.frag?.type)));
1658
+ let r = n?.frag, i = q(r?.type);
1659
+ if (r && t.isActive(i)) for (let e of yt(p, r)) t.prefetch(e.url, i, e.kind);
1660
+ }), p.on(e.Events.KEY_LOADED, (e, n) => t.keyUsable(q(n?.frag?.type)));
1632
1661
  }
1633
1662
  p.on(e.Events.KEY_LOADED, () => {
1634
1663
  u && !this.reportedDrmReady && (this.reportedDrmReady = !0, l({ type: "drmReady" }));
1635
1664
  }), this.endWatchdog && clearInterval(this.endWatchdog), this.endWatchdog = setInterval(() => {
1636
1665
  this.checkEndStale(), this.checkStarvation(), this.updateCatchUpBand(), this.checkFutileChase(), this.checkFrameDrops();
1637
- }, Tt), p.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1666
+ }, Dt), p.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1638
1667
  this.renditionSwitches += 1;
1639
1668
  let n = this.lastLevelIndex >= 0 ? p.levels[this.lastLevelIndex] : void 0, r = p.levels[t.level];
1640
1669
  n && r && r.bitrate !== n.bitrate && (r.bitrate > n.bitrate ? this.upshifts += 1 : this.downshifts += 1), this.lastLevelIndex = t.level, l({
@@ -1698,7 +1727,7 @@ var Qt = class {
1698
1727
  return;
1699
1728
  }
1700
1729
  this.capTimer && clearTimeout(this.capTimer);
1701
- let t = e > this.sizeCapLevel ? Vt : Ht;
1730
+ let t = e > this.sizeCapLevel ? Ut : Wt;
1702
1731
  this.capTimer = setTimeout(() => {
1703
1732
  this.capTimer = null, this.computeSizeCapTarget() === e ? this.applySizeCap() : this.onMediaResize();
1704
1733
  }, t);
@@ -1708,9 +1737,9 @@ var Qt = class {
1708
1737
  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;
1709
1738
  }
1710
1739
  dropLevelsWidevineCannotPlay(e) {
1711
- this.removeLevelsWhere(e, (e) => Fe(e) !== !0);
1740
+ this.removeLevelsWhere(e, (e) => Pe(e) !== !0);
1712
1741
  let t = new Set(e.levels.map((e) => e.videoCodec).filter((e) => !!e && I(e) !== "avc" && !!I(e)));
1713
- for (let n of t) Ne(n).then((t) => {
1742
+ for (let n of t) Me(n).then((t) => {
1714
1743
  t || this.hls !== e || this.removeLevelsWhere(e, (e) => e === n) && (this.measureLadder(e), this.applySizeCap());
1715
1744
  });
1716
1745
  }
@@ -1721,7 +1750,7 @@ var Qt = class {
1721
1750
  a && I(a) && t(a) && (r.push(n), i.add(a));
1722
1751
  }), !r.length) return !1;
1723
1752
  let a = [...i].join(", ");
1724
- 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;
1753
+ if (!n.some((e, t) => r.includes(t) ? !1 : !I(e.videoCodec) || Pe(e.videoCodec) === !0)) return console.warn(`havik-player: every rendition is ${a}, which Widevine may not play on this browser; keeping them`), !1;
1725
1754
  for (let t = r.length - 1; t >= 0; --t) e.removeLevel(r[t]);
1726
1755
  return console.info(`havik-player: skipped ${r.length} rendition(s) Widevine cannot play on this browser (${a})`), !0;
1727
1756
  }
@@ -1735,11 +1764,12 @@ var Qt = class {
1735
1764
  let e = this.hls;
1736
1765
  if (!e) return;
1737
1766
  let t = [
1738
- this.sizeCapLevel ?? -1,
1739
- this.bitrateCapLevel,
1740
- this.fpsCapLevel(e)
1741
- ].filter((e) => e >= 0);
1742
- e.autoLevelCapping = t.length ? Math.min(...t) : -1;
1767
+ ["size", this.sizeCapLevel ?? -1],
1768
+ ["bitrate", this.bitrateCapLevel],
1769
+ ["fps", this.fpsCapLevel(e)]
1770
+ ], n = -1, r = null;
1771
+ for (let [e, i] of t) i >= 0 && (n < 0 || i < n) && (n = i, r = e);
1772
+ e.autoLevelCapping = n, this.pushedCapLevel = n, this.pushedCapReason = r;
1743
1773
  }
1744
1774
  getAudioTracks() {
1745
1775
  return this.hls ? this.hls.audioTracks.map((e) => ({
@@ -1792,7 +1822,14 @@ var Qt = class {
1792
1822
  if (this.cdnHost && (n.cdnHost = this.cdnHost), this.licenseRequested && (n.keySystem = "widevine", n.drmSecurityLevel = "L3"), e) {
1793
1823
  n.qualityPinned = !e.autoLevelEnabled, typeof e.latency == "number" && (n.latencySeconds = e.latency), typeof e.targetLatency == "number" && e.targetLatency > 0 && (n.targetLatencySeconds = e.targetLatency), typeof e.bandwidthEstimate == "number" && (n.bandwidthKbps = Math.round(e.bandwidthEstimate / 1e3));
1794
1824
  let t = e.currentLevel >= 0 ? e.levels[e.currentLevel] : void 0;
1795
- t && (n.levelHeight = t.height), t?.videoCodec && (n.videoCodec = t.videoCodec), t?.audioCodec && (n.audioCodec = t.audioCodec), t && typeof t.bitrate == "number" && (n.renditionBitrateKbps = Math.round(t.bitrate / 1e3)), typeof t?.details?.live == "boolean" && (n.isLive = t.details.live, typeof e.liveSyncPosition == "number" && (n.atLiveEdge = this.deps.video.currentTime >= e.liveSyncPosition - 1.5));
1825
+ t && (n.levelHeight = t.height);
1826
+ let r = e.autoLevelCapping, i = r >= 0 ? e.levels[r] : void 0;
1827
+ if (i && i.height > 0) {
1828
+ n.maxLevelHeight = i.height;
1829
+ let t = e.config ?? {};
1830
+ t.capLevelToPlayerSize !== !0 && t.capLevelOnFPSDrop !== !0 && r === this.pushedCapLevel && this.pushedCapReason && (n.capReason = this.pushedCapReason);
1831
+ }
1832
+ t?.videoCodec && (n.videoCodec = t.videoCodec), t?.audioCodec && (n.audioCodec = t.audioCodec), t && typeof t.bitrate == "number" && (n.renditionBitrateKbps = Math.round(t.bitrate / 1e3)), typeof t?.details?.live == "boolean" && (n.isLive = t.details.live, typeof e.liveSyncPosition == "number" && (n.atLiveEdge = this.deps.video.currentTime >= e.liveSyncPosition - 1.5));
1796
1833
  }
1797
1834
  return n;
1798
1835
  }
@@ -1819,7 +1856,7 @@ var Qt = class {
1819
1856
  return;
1820
1857
  }
1821
1858
  let e = Date.now() - this.lastAdvanceMs;
1822
- !this.chaseSuspended && e > Dt && this.suspendChasing(), e > wt && this.emitEnded("stalled");
1859
+ !this.chaseSuspended && e > kt && this.suspendChasing(), e > Et && this.emitEnded("stalled");
1823
1860
  }
1824
1861
  resetStarvationWindow() {
1825
1862
  this.starvedTicks = 0, this.deadTicks = 0, this.mainFragsSinceTick = 0, this.recentMainFrags = [];
@@ -1834,7 +1871,7 @@ var Qt = class {
1834
1871
  this.resetStarvationWindow();
1835
1872
  return;
1836
1873
  }
1837
- let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < Dt, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > Pt, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = z(t), c = o - s, l = c !== this.starvedFrames;
1874
+ let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < kt, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > It, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = B(t), c = o - s, l = c !== this.starvedFrames;
1838
1875
  if (this.starvedFrames = c, !a || l) {
1839
1876
  (t.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA || l) && this.clearKick(), this.resetStarvationWindow();
1840
1877
  return;
@@ -1843,12 +1880,12 @@ var Qt = class {
1843
1880
  this.resetStarvationWindow();
1844
1881
  return;
1845
1882
  }
1846
- 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 > zt && this.recentMainFrags.shift(), this.clearRecoveryReset(), this.kickPending && this.kickTicks >= Lt && (this.clearKick(), this.starvationRebuilds < Rt)) {
1883
+ 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 > Vt && this.recentMainFrags.shift(), this.clearRecoveryReset(), this.kickPending && this.kickTicks >= zt && (this.clearKick(), this.starvationRebuilds < Bt)) {
1847
1884
  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");
1848
1885
  return;
1849
1886
  }
1850
- let u = i && this.starvedTicks >= Ft, d = !i && this.deadTicks >= zt && this.recentMainFrags.reduce((e, t) => e + t, 0) >= Bt;
1851
- if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >= It))) {
1887
+ let u = i && this.starvedTicks >= Lt, d = !i && this.deadTicks >= Vt && this.recentMainFrags.reduce((e, t) => e + t, 0) >= Ht;
1888
+ if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >= Rt))) {
1852
1889
  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");
1853
1890
  try {
1854
1891
  e.stopLoad(), e.startLoad(-1);
@@ -1863,7 +1900,7 @@ var Qt = class {
1863
1900
  let t = e.latency, n = e.targetLatency;
1864
1901
  if (typeof t != "number" || typeof n != "number" || !Number.isFinite(t)) return;
1865
1902
  let r = t - n;
1866
- !this.catchUpEngaged && r > Ot ? this.catchUpEngaged = !0 : this.catchUpEngaged && r <= Ot && this.deps.video.playbackRate === 1 && (this.catchUpEngaged = !1);
1903
+ !this.catchUpEngaged && r > At ? this.catchUpEngaged = !0 : this.catchUpEngaged && r <= At && this.deps.video.playbackRate === 1 && (this.catchUpEngaged = !1);
1867
1904
  let i = this.catchUpEngaged ? this.catchUpRate : 1;
1868
1905
  e.config.maxLiveSyncPlaybackRate !== i && (e.config.maxLiveSyncPlaybackRate = i);
1869
1906
  }
@@ -1884,16 +1921,16 @@ var Qt = class {
1884
1921
  this.resetFrameDropWindow();
1885
1922
  return;
1886
1923
  }
1887
- let { totalFrames: n, dropped: r } = z(t), i = this.fpsLastFrames < 0, a = n - this.fpsLastFrames, o = r - this.fpsLastDropped;
1888
- if (this.fpsLastFrames = n, this.fpsLastDropped = r, i || a < Wt || o < 0) {
1924
+ let { totalFrames: n, dropped: r } = B(t), i = this.fpsLastFrames < 0, a = n - this.fpsLastFrames, o = r - this.fpsLastDropped;
1925
+ if (this.fpsLastFrames = n, this.fpsLastDropped = r, i || a < Kt || o < 0) {
1889
1926
  this.fpsBadTicks = 0;
1890
1927
  return;
1891
1928
  }
1892
- if (o <= Ut * a) {
1929
+ if (o <= Gt * a) {
1893
1930
  this.fpsBadTicks = 0;
1894
1931
  return;
1895
1932
  }
1896
- if (this.fpsBadTicks += 1, this.fpsBadTicks < Gt) return;
1933
+ if (this.fpsBadTicks += 1, this.fpsBadTicks < qt) return;
1897
1934
  this.fpsBadTicks = 0;
1898
1935
  let s = e.levels, c = s[e.currentLevel];
1899
1936
  if (!c) return;
@@ -1910,20 +1947,20 @@ var Qt = class {
1910
1947
  return;
1911
1948
  }
1912
1949
  let n = e.latency, r = t.playbackRate, i = e.config.maxLiveSyncPlaybackRate;
1913
- if (!(!t.paused && typeof i == "number" && i > 1 && r >= Math.min(jt, i) && r <= i && typeof n == "number" && Number.isFinite(n))) {
1950
+ if (!(!t.paused && typeof i == "number" && i > 1 && r >= Math.min(Nt, i) && r <= i && typeof n == "number" && Number.isFinite(n))) {
1914
1951
  this.resetChaseWindow();
1915
1952
  return;
1916
1953
  }
1917
- if (this.chaseLatency < 0 || this.chaseLatency - n >= At) {
1954
+ if (this.chaseLatency < 0 || this.chaseLatency - n >= Mt) {
1918
1955
  this.chaseLatency = n, this.chaseTicks = 0;
1919
1956
  return;
1920
1957
  }
1921
- if (this.chaseTicks += 1, this.chaseTicks < kt) return;
1958
+ if (this.chaseTicks += 1, this.chaseTicks < jt) return;
1922
1959
  this.resetChaseWindow();
1923
1960
  let a = typeof e.config.liveMaxLatencyDuration == "number" ? e.config.liveMaxLatencyDuration : e.maxLatency;
1924
1961
  if (!(a > 1)) return;
1925
1962
  let o = typeof e.targetLatency == "number" ? e.targetLatency : 0, s = Math.min(n, a - 1);
1926
- s < o + Mt || (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);
1963
+ s < o + Pt || (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);
1927
1964
  }
1928
1965
  suspendChasing() {
1929
1966
  let e = this.hls;
@@ -1968,7 +2005,7 @@ var Qt = class {
1968
2005
  scheduleLicenseRetry(t) {
1969
2006
  if (t.details !== e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED && t.details !== e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED) return !1;
1970
2007
  let n = t.response?.code ?? 0;
1971
- if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= xt) return !1;
2008
+ if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Ct) return !1;
1972
2009
  this.licenseRecoveries += 1;
1973
2010
  let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
1974
2011
  return this.licenseRetryTimer = setTimeout(() => {
@@ -2000,7 +2037,7 @@ var Qt = class {
2000
2037
  return;
2001
2038
  }
2002
2039
  if (t.type === e.ErrorTypes.NETWORK_ERROR) {
2003
- if (this.networkRecoveries < bt) {
2040
+ if (this.networkRecoveries < St) {
2004
2041
  this.networkRecoveries += 1;
2005
2042
  let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
2006
2043
  this.networkRetryTimer = setTimeout(() => {
@@ -2017,7 +2054,7 @@ var Qt = class {
2017
2054
  return;
2018
2055
  }
2019
2056
  if (t.type === e.ErrorTypes.MEDIA_ERROR) {
2020
- if (this.mediaRecoveries < yt) {
2057
+ if (this.mediaRecoveries < xt) {
2021
2058
  this.mediaRecoveries += 1, n.recoverMediaError();
2022
2059
  return;
2023
2060
  }
@@ -2041,19 +2078,19 @@ var Qt = class {
2041
2078
  let r = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
2042
2079
  return new n(t === "DRM" ? i(r) : r === 401 ? "UNAUTHORIZED" : r === 403 ? "FORBIDDEN" : "INTERNAL", r, `${t} error: ${a}${o ? ` (${o})` : ""}`);
2043
2080
  }
2044
- }, $t = 15e3, en = 6e5, tn = 3e5, nn = 100, q = 1, rn = 1e3, an = 3e4, on = 1e3, sn = {
2081
+ }, tn = 15e3, nn = 6e5, rn = 3e5, an = 100, J = 1, on = 1e3, sn = 3e4, cn = 1e3, ln = {
2045
2082
  inline: "PRESENTATION_INLINE",
2046
2083
  fullscreen: "PRESENTATION_FULLSCREEN",
2047
2084
  pip: "PRESENTATION_PIP"
2048
- }, cn = {
2085
+ }, un = {
2049
2086
  widevine: "KEY_SYSTEM_WIDEVINE",
2050
2087
  fairplay: "KEY_SYSTEM_FAIRPLAY",
2051
2088
  none: "KEY_SYSTEM_NONE"
2052
2089
  };
2053
- function ln(e, t) {
2090
+ function dn(e, t) {
2054
2091
  return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
2055
2092
  }
2056
- var un = /* @__PURE__ */ new Set([
2093
+ var fn = /* @__PURE__ */ new Set([
2057
2094
  "INVALID_URN",
2058
2095
  "NOT_FOUND",
2059
2096
  "GONE",
@@ -2063,7 +2100,7 @@ var un = /* @__PURE__ */ new Set([
2063
2100
  "FORBIDDEN",
2064
2101
  "RATE_LIMITED",
2065
2102
  "TIMEOUT"
2066
- ]), J = {
2103
+ ]), Y = {
2067
2104
  idle: "PLAYER_STATE_IDLE",
2068
2105
  waiting: "PLAYER_STATE_WAITING",
2069
2106
  loading: "PLAYER_STATE_LOADING",
@@ -2073,7 +2110,7 @@ var un = /* @__PURE__ */ new Set([
2073
2110
  ended: "PLAYER_STATE_ENDED",
2074
2111
  error: "PLAYER_STATE_ERROR"
2075
2112
  };
2076
- function dn(e) {
2113
+ function pn(e) {
2077
2114
  try {
2078
2115
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
2079
2116
  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);
@@ -2081,8 +2118,8 @@ function dn(e) {
2081
2118
  return;
2082
2119
  }
2083
2120
  }
2084
- var fn = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), pn = /* @__PURE__ */ new Set(), Y = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), mn = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
2085
- function hn(e) {
2121
+ var mn = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), hn = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), Z = /* @__PURE__ */ new Set(), gn = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
2122
+ function _n(e) {
2086
2123
  if (e.disposition !== "enforce" || e.effectiveDirective !== "connect-src") return;
2087
2124
  let t;
2088
2125
  try {
@@ -2091,19 +2128,19 @@ function hn(e) {
2091
2128
  } catch {
2092
2129
  return;
2093
2130
  }
2094
- if (pn.has(t) && !Y.has(t)) {
2095
- Y.add(t), g(mn(new URL(t).origin));
2096
- for (let e of X) e.noteCspBlocked();
2131
+ if (hn.has(t) && !X.has(t)) {
2132
+ X.add(t), g(gn(new URL(t).origin));
2133
+ for (let e of Z) e.noteCspBlocked();
2097
2134
  }
2098
2135
  }
2099
- function Z(e, t) {
2136
+ function vn(e, t) {
2100
2137
  return g(t, e), { disabledReason: t };
2101
2138
  }
2102
- function gn(e, t, n) {
2103
- if (n) return t && !fn(t, n) && g(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
2139
+ function yn(e, t, n) {
2140
+ if (n) return t && !mn(t, n) && g(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
2104
2141
  if (t) return { endpoint: t };
2105
- if (t !== void 0) return Z("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).");
2106
- let r = dn(e);
2142
+ if (t !== void 0) return vn("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).");
2143
+ let r = pn(e);
2107
2144
  if (r) return { endpoint: r };
2108
2145
  let i;
2109
2146
  try {
@@ -2111,9 +2148,9 @@ function gn(e, t, n) {
2111
2148
  } catch {
2112
2149
  i = e;
2113
2150
  }
2114
- return Z(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).`);
2151
+ return vn(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).`);
2115
2152
  }
2116
- var _n = class {
2153
+ var bn = class {
2117
2154
  opts;
2118
2155
  url;
2119
2156
  cspKey;
@@ -2195,8 +2232,8 @@ var _n = class {
2195
2232
  constructor(e) {
2196
2233
  this.opts = {
2197
2234
  ...e,
2198
- heartbeatMs: e.heartbeatMs ?? $t,
2199
- idleMs: e.idleMs ?? en
2235
+ heartbeatMs: e.heartbeatMs ?? tn,
2236
+ idleMs: e.idleMs ?? nn
2200
2237
  }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`;
2201
2238
  try {
2202
2239
  let e = new URL(this.url);
@@ -2213,11 +2250,11 @@ var _n = class {
2213
2250
  sdkVersion: this.opts.sdkVersion,
2214
2251
  ...this.opts.environment
2215
2252
  };
2216
- this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), rn), 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 && pn.add(this.cspKey), document.addEventListener("securitypolicyviolation", hn), X.add(this), this.noteCspBlocked(), this.onVisibility());
2253
+ this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), on), 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 && hn.add(this.cspKey), document.addEventListener("securitypolicyviolation", _n), Z.add(this), this.noteCspBlocked(), this.onVisibility());
2217
2254
  }
2218
2255
  checkSuspension(e) {
2219
2256
  if (this.endedFlag || !this.opts.idleMs) return !1;
2220
- let t = Math.max(this.opts.idleMs, tn);
2257
+ let t = Math.max(this.opts.idleMs, rn);
2221
2258
  return this.lastTickMs > 0 && e - this.lastTickMs >= t ? (this.endIdle(this.lastTickMs), !0) : (this.lastTickMs = e, !1);
2222
2259
  }
2223
2260
  syncIdle(e) {
@@ -2231,21 +2268,21 @@ var _n = class {
2231
2268
  noteStateChange(e, t, n, r) {
2232
2269
  if (this.endedFlag || e === t) return;
2233
2270
  let i = this.now();
2234
- if (t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(q, i - this.startupAnchorMs)), this.firstAudioMs === 0 && !this.isMuted && (this.firstAudioMs = Math.max(q, i - this.startupAnchorMs)), this.isMuted && this.mutedSinceMs === null && (this.mutedSinceMs = i)) : this.playingSinceMs !== null && (this.playingTotalMs += i - this.playingSinceMs, this.playingSinceMs = null, this.mutedSinceMs !== null && (this.mutedTotalMs += i - this.mutedSinceMs, this.mutedSinceMs = null)), this.isPlaying = t === "playing", this.syncActive(), t === "waiting" ? this.waitingSinceMs = i : this.waitingSinceMs !== null && (this.waitingTotalMs += i - this.waitingSinceMs, this.waitingSinceMs = null), t === "buffering" && e === "playing") {
2271
+ if (t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(J, i - this.startupAnchorMs)), this.firstAudioMs === 0 && !this.isMuted && (this.firstAudioMs = Math.max(J, i - this.startupAnchorMs)), this.isMuted && this.mutedSinceMs === null && (this.mutedSinceMs = i)) : this.playingSinceMs !== null && (this.playingTotalMs += i - this.playingSinceMs, this.playingSinceMs = null, this.mutedSinceMs !== null && (this.mutedTotalMs += i - this.mutedSinceMs, this.mutedSinceMs = null)), this.isPlaying = t === "playing", this.syncActive(), t === "waiting" ? this.waitingSinceMs = i : this.waitingSinceMs !== null && (this.waitingTotalMs += i - this.waitingSinceMs, this.waitingSinceMs = null), t === "buffering" && e === "playing") {
2235
2272
  this.stallIsSeekInduced = this.seekInduced(i);
2236
2273
  let e = !this.isHidden && !this.stallIsSeekInduced;
2237
- e && (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, e && this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(q, Math.round(this.playingTotalMs)));
2274
+ e && (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, e && this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(J, Math.round(this.playingTotalMs)));
2238
2275
  } else e === "buffering" && (this.isBuffering = !1, this.stallIsSeekInduced = !1);
2239
2276
  this.syncBuffering(), t === "paused" && (this.pauseCount += 1), this.syncIdle(t);
2240
2277
  let a = {
2241
- fromState: J[e] ?? "PLAYER_STATE_UNSPECIFIED",
2242
- toState: J[t] ?? "PLAYER_STATE_UNSPECIFIED",
2243
- presentation: sn[this.presentation]
2278
+ fromState: Y[e] ?? "PLAYER_STATE_UNSPECIFIED",
2279
+ toState: Y[t] ?? "PLAYER_STATE_UNSPECIFIED",
2280
+ presentation: ln[this.presentation]
2244
2281
  }, o = this.causeOf(e, t, i);
2245
2282
  o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
2246
2283
  }
2247
2284
  seekInduced(e) {
2248
- return this.lastSeekMs >= 0 && e - this.lastSeekMs < on;
2285
+ return this.lastSeekMs >= 0 && e - this.lastSeekMs < cn;
2249
2286
  }
2250
2287
  causeOf(e, t, n) {
2251
2288
  if (t === "error") return "TRANSITION_CAUSE_ERROR";
@@ -2282,25 +2319,25 @@ var _n = class {
2282
2319
  }
2283
2320
  this.muteChanges += 1;
2284
2321
  let r = this.now();
2285
- n ? this.isPlaying && this.mutedSinceMs === null && (this.mutedSinceMs = r) : (this.mutedSinceMs !== null && (this.mutedTotalMs += r - this.mutedSinceMs, this.mutedSinceMs = null), this.firstAudioMs === 0 && this.isPlaying && (this.firstAudioMs = Math.max(q, r - this.startupAnchorMs))), this.isMuted = n, this.syncActive();
2322
+ n ? this.isPlaying && this.mutedSinceMs === null && (this.mutedSinceMs = r) : (this.mutedSinceMs !== null && (this.mutedTotalMs += r - this.mutedSinceMs, this.mutedSinceMs = null), this.firstAudioMs === 0 && this.isPlaying && (this.firstAudioMs = Math.max(J, r - this.startupAnchorMs))), this.isMuted = n, this.syncActive();
2286
2323
  }
2287
2324
  notePresentation(e) {
2288
2325
  this.endedFlag || e === this.presentation || (this.closePresentation(), this.presentation = e);
2289
2326
  }
2290
2327
  notePlayIntent() {
2291
- this.endedFlag || this.startupTimeMs > 0 || (this.startupAnchorMs = this.now(), this.manifestLoadMs > 0 && (this.manifestLoadMs = q), this.firstPlaylistMs > 0 && (this.firstPlaylistMs = q), this.firstFragLoadMs > 0 && (this.firstFragLoadMs = q), this.drmSetupMs > 0 && (this.drmSetupMs = q));
2328
+ this.endedFlag || this.startupTimeMs > 0 || (this.startupAnchorMs = this.now(), this.manifestLoadMs > 0 && (this.manifestLoadMs = J), this.firstPlaylistMs > 0 && (this.firstPlaylistMs = J), this.firstFragLoadMs > 0 && (this.firstFragLoadMs = J), this.drmSetupMs > 0 && (this.drmSetupMs = J));
2292
2329
  }
2293
2330
  noteReady() {
2294
- this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(q, this.now() - this.startupAnchorMs));
2331
+ this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(J, this.now() - this.startupAnchorMs));
2295
2332
  }
2296
2333
  noteFirstPlaylist() {
2297
- this.firstPlaylistMs === 0 && (this.firstPlaylistMs = Math.max(q, this.now() - this.startupAnchorMs));
2334
+ this.firstPlaylistMs === 0 && (this.firstPlaylistMs = Math.max(J, this.now() - this.startupAnchorMs));
2298
2335
  }
2299
2336
  noteDrmReady() {
2300
- this.drmSetupMs === 0 && (this.drmSetupMs = Math.max(q, this.now() - this.startupAnchorMs));
2337
+ this.drmSetupMs === 0 && (this.drmSetupMs = Math.max(J, this.now() - this.startupAnchorMs));
2301
2338
  }
2302
2339
  noteFirstBytes() {
2303
- this.sawFirstBytes = !0, this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(q, this.now() - this.startupAnchorMs));
2340
+ this.sawFirstBytes = !0, this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(J, this.now() - this.startupAnchorMs));
2304
2341
  }
2305
2342
  noteSeek() {
2306
2343
  this.endedFlag || (this.seekCount += 1, this.lastSeekMs = this.now());
@@ -2319,7 +2356,7 @@ var _n = class {
2319
2356
  let r = n ?? "";
2320
2357
  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";
2321
2358
  if (e === "DRM_CLIENT") return "ERROR_STAGE_DRM";
2322
- if (un.has(e)) return "ERROR_STAGE_RESOLVE";
2359
+ if (fn.has(e)) return "ERROR_STAGE_RESOLVE";
2323
2360
  let i = t ?? "";
2324
2361
  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";
2325
2362
  }
@@ -2351,7 +2388,7 @@ var _n = class {
2351
2388
  let l = t.droppedFrames ?? 0;
2352
2389
  l > this.lastDropped && (o.droppedFramesDelta = l - this.lastDropped), this.lastDropped = l;
2353
2390
  let u = t.renditionSwitches ?? 0;
2354
- u > this.lastSwitches && (o.renditionSwitchesDelta = u - this.lastSwitches), this.lastSwitches = u, n > this.lastHbPlayingMs && (o.playingMsDelta = Math.round(n - this.lastHbPlayingMs)), this.lastHbPlayingMs = n, r > this.lastHbHiddenMs && (o.hiddenMsDelta = Math.round(r - this.lastHbHiddenMs)), this.lastHbHiddenMs = r, i > this.lastHbRebufferMs && (o.rebufferMsDelta = Math.round(i - this.lastHbRebufferMs)), this.lastHbRebufferMs = i, this.rebufferCount > this.lastHbRebufferCount && (o.rebufferCountDelta = this.rebufferCount - this.lastHbRebufferCount), this.lastHbRebufferCount = this.rebufferCount, a > this.lastHbActiveMs && (o.activeMsDelta = Math.round(a - this.lastHbActiveMs)), this.lastHbActiveMs = a, typeof t.latencySeconds == "number" && (o.latencyMs = Math.round(t.latencySeconds * 1e3)), typeof t.targetLatencySeconds == "number" && (o.targetLatencyMs = Math.round(t.targetLatencySeconds * 1e3)), typeof t.bandwidthKbps == "number" && (o.bandwidthEstKbps = t.bandwidthKbps), typeof t.levelHeight == "number" && (o.renditionHeight = t.levelHeight), typeof t.renditionBitrateKbps == "number" && (o.renditionBitrateKbps = t.renditionBitrateKbps), t.atLiveEdge === !0 && (o.atLiveEdge = !0), typeof t.bufferedMs == "number" && t.bufferedMs > 0 && (o.bufferedMs = Math.round(t.bufferedMs)), o.playerState = J[t.state] ?? "PLAYER_STATE_UNSPECIFIED";
2391
+ u > this.lastSwitches && (o.renditionSwitchesDelta = u - this.lastSwitches), this.lastSwitches = u, n > this.lastHbPlayingMs && (o.playingMsDelta = Math.round(n - this.lastHbPlayingMs)), this.lastHbPlayingMs = n, r > this.lastHbHiddenMs && (o.hiddenMsDelta = Math.round(r - this.lastHbHiddenMs)), this.lastHbHiddenMs = r, i > this.lastHbRebufferMs && (o.rebufferMsDelta = Math.round(i - this.lastHbRebufferMs)), this.lastHbRebufferMs = i, this.rebufferCount > this.lastHbRebufferCount && (o.rebufferCountDelta = this.rebufferCount - this.lastHbRebufferCount), this.lastHbRebufferCount = this.rebufferCount, a > this.lastHbActiveMs && (o.activeMsDelta = Math.round(a - this.lastHbActiveMs)), this.lastHbActiveMs = a, typeof t.latencySeconds == "number" && (o.latencyMs = Math.round(t.latencySeconds * 1e3)), typeof t.targetLatencySeconds == "number" && (o.targetLatencyMs = Math.round(t.targetLatencySeconds * 1e3)), typeof t.bandwidthKbps == "number" && (o.bandwidthEstKbps = t.bandwidthKbps), typeof t.levelHeight == "number" && (o.renditionHeight = t.levelHeight), typeof t.renditionBitrateKbps == "number" && (o.renditionBitrateKbps = t.renditionBitrateKbps), t.atLiveEdge === !0 && (o.atLiveEdge = !0), typeof t.bufferedMs == "number" && t.bufferedMs > 0 && (o.bufferedMs = Math.round(t.bufferedMs)), o.playerState = Y[t.state] ?? "PLAYER_STATE_UNSPECIFIED";
2355
2392
  let d = this.mutedTotalMs;
2356
2393
  this.mutedSinceMs !== null && (d += e - this.mutedSinceMs), d > this.lastHbMutedMs && (o.mutedMsDelta = Math.round(d - this.lastHbMutedMs)), this.lastHbMutedMs = d;
2357
2394
  let f = t.upshifts ?? 0;
@@ -2366,20 +2403,20 @@ var _n = class {
2366
2403
  if (this.lastRequestCount = g, _ > 0) {
2367
2404
  o.requestCountDelta = _;
2368
2405
  let e = t.requestTimesMs ?? [], n = e.slice(Math.max(0, e.length - _)).sort((e, t) => e - t);
2369
- n.length > 0 && (o.requestTimeP50Ms = ln(n, 50), o.requestTimeP95Ms = ln(n, 95));
2406
+ n.length > 0 && (o.requestTimeP50Ms = dn(n, 50), o.requestTimeP95Ms = dn(n, 95));
2370
2407
  }
2371
2408
  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();
2372
2409
  }
2373
2410
  maybeEmitMediaInfo(e) {
2374
2411
  if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
2375
2412
  let t = {};
2376
- e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = cn[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);
2413
+ e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = un[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);
2377
2414
  let n = JSON.stringify(t);
2378
2415
  n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
2379
2416
  }
2380
2417
  end(e, t) {
2381
2418
  if (this.endedFlag) return;
2382
- this.endedFlag = !0, X.delete(this);
2419
+ this.endedFlag = !0, Z.delete(this);
2383
2420
  let n = t ?? this.now();
2384
2421
  this.playingSinceMs !== null && (this.playingTotalMs += n - this.playingSinceMs, this.playingSinceMs = null), this.hiddenSinceMs !== null && (this.hiddenTotalMs += n - this.hiddenSinceMs, this.hiddenSinceMs = null), this.bufferingSinceMs !== null && (this.rebufferTotalMs += n - this.bufferingSinceMs, this.bufferingSinceMs = null), this.mutedSinceMs !== null && (this.mutedTotalMs += n - this.mutedSinceMs, this.mutedSinceMs = null), this.waitingSinceMs !== null && (this.waitingTotalMs += n - this.waitingSinceMs, this.waitingSinceMs = null), this.isPlaying = !1, this.syncActive(n), this.closePresentation(n), this.firstFlushTimer &&= (clearTimeout(this.firstFlushTimer), null), this.timer &&= (clearInterval(this.timer), null), this.idleTimer &&= (clearTimeout(this.idleTimer), null), typeof window < "u" && window.removeEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.removeEventListener("visibilitychange", this.onVisibility), document.removeEventListener("freeze", this.onFreeze), document.removeEventListener("resume", this.onResume));
2385
2422
  let r = this.safeSample();
@@ -2393,7 +2430,7 @@ var _n = class {
2393
2430
  let c = r?.positionMs ?? 0;
2394
2431
  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));
2395
2432
  let l = e !== "END_REASON_ENDED";
2396
- l && this.lastStallMs > 0 && n - this.lastStallMs <= an && (i.abandonAfterStallMs = Math.max(q, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= an && (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);
2433
+ l && this.lastStallMs > 0 && n - this.lastStallMs <= sn && (i.abandonAfterStallMs = Math.max(J, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= sn && (i.abandonAfterErrorMs = Math.max(J, 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);
2397
2434
  let u = r?.decodedFrames ?? this.lastDecoded;
2398
2435
  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");
2399
2436
  }
@@ -2413,13 +2450,13 @@ var _n = class {
2413
2450
  };
2414
2451
  }
2415
2452
  noteCspBlocked() {
2416
- this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(mn(new URL(this.url).origin)));
2453
+ this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(gn(new URL(this.url).origin)));
2417
2454
  }
2418
2455
  cspBlocked() {
2419
- return !!this.cspKey && Y.has(this.cspKey);
2456
+ return !!this.cspKey && X.has(this.cspKey);
2420
2457
  }
2421
2458
  enqueue(e) {
2422
- if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > nn;) {
2459
+ if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > an;) {
2423
2460
  let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
2424
2461
  this.pending.splice(e >= 0 ? e : 0, 1);
2425
2462
  }
@@ -2446,7 +2483,7 @@ var _n = class {
2446
2483
  };
2447
2484
  //#endregion
2448
2485
  //#region src/core/environment.ts
2449
- function vn() {
2486
+ function xn() {
2450
2487
  if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
2451
2488
  let e;
2452
2489
  try {
@@ -2471,14 +2508,14 @@ function vn() {
2471
2508
  integration: "INTEGRATION_KIND_IFRAME"
2472
2509
  };
2473
2510
  }
2474
- function yn() {
2511
+ function Sn() {
2475
2512
  if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
2476
2513
  let e = navigator.userAgentData, t = e?.platform ?? "";
2477
2514
  if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
2478
2515
  let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
2479
2516
  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";
2480
2517
  }
2481
- function bn() {
2518
+ function Cn() {
2482
2519
  let e = typeof navigator < "u" ? navigator.connection : void 0;
2483
2520
  if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
2484
2521
  let t = {
@@ -2497,17 +2534,17 @@ function bn() {
2497
2534
  }
2498
2535
  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;
2499
2536
  }
2500
- function xn(e) {
2501
- let t = vn(), n = {
2502
- deviceClass: yn(),
2537
+ function wn(e) {
2538
+ let t = xn(), n = {
2539
+ deviceClass: Sn(),
2503
2540
  integration: t.integration,
2504
- ...bn()
2541
+ ...Cn()
2505
2542
  };
2506
2543
  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);
2507
2544
  let r = e.getBoundingClientRect?.();
2508
2545
  return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
2509
2546
  }
2510
- async function Sn() {
2547
+ async function Tn() {
2511
2548
  let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
2512
2549
  if (!e?.getHighEntropyValues) return {};
2513
2550
  try {
@@ -2519,7 +2556,7 @@ async function Sn() {
2519
2556
  }
2520
2557
  //#endregion
2521
2558
  //#region src/core/version.ts
2522
- var Cn = "2.16.12", wn = {
2559
+ var En = "2.17.0", Dn = {
2523
2560
  accent: "#E1B600",
2524
2561
  accentText: "#1B1C23",
2525
2562
  background: "#0e0f13",
@@ -2530,7 +2567,7 @@ var Cn = "2.16.12", wn = {
2530
2567
  border: "rgba(255, 255, 255, 0.1)",
2531
2568
  radius: "8px",
2532
2569
  fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
2533
- }, Tn = {
2570
+ }, On = {
2534
2571
  accent: "--havik-accent",
2535
2572
  accentText: "--havik-accent-text",
2536
2573
  background: "--havik-bg",
@@ -2542,26 +2579,26 @@ var Cn = "2.16.12", wn = {
2542
2579
  radius: "--havik-radius",
2543
2580
  fontFamily: "--havik-font"
2544
2581
  };
2545
- function En(e, t) {
2582
+ function kn(e, t) {
2546
2583
  let n = {
2547
- ...wn,
2584
+ ...Dn,
2548
2585
  ...t
2549
2586
  };
2550
- for (let t of Object.keys(Tn)) e.style.setProperty(Tn[t], n[t]);
2587
+ for (let t of Object.keys(On)) e.style.setProperty(On[t], n[t]);
2551
2588
  n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
2552
2589
  }
2553
2590
  //#endregion
2554
2591
  //#region src/ui/styles.ts
2555
- var Dn = "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", kn = /* @__PURE__ */ new WeakMap();
2556
- function An(e = document, t) {
2557
- let n = e.querySelector(`style#${Dn}`);
2558
- if (n && (!t || kn.get(n) === t)) return;
2592
+ var An = "havik-player-styles", jn = "\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", Mn = /* @__PURE__ */ new WeakMap();
2593
+ function Nn(e = document, t) {
2594
+ let n = e.querySelector(`style#${An}`);
2595
+ if (n && (!t || Mn.get(n) === t)) return;
2559
2596
  let r = e.createElement("style");
2560
- r.id = Dn, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = On, kn.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
2597
+ r.id = An, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = jn, Mn.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
2561
2598
  }
2562
2599
  //#endregion
2563
2600
  //#region src/ui/icons.ts
2564
- var jn = {
2601
+ var Pn = {
2565
2602
  play: "<path d=\"M8 5v14l11-7z\"/>",
2566
2603
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
2567
2604
  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\"/>",
@@ -2575,20 +2612,20 @@ var jn = {
2575
2612
  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\"/>"
2576
2613
  };
2577
2614
  function Q(e) {
2578
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${jn[e]}</svg>`;
2615
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${Pn[e]}</svg>`;
2579
2616
  }
2580
2617
  //#endregion
2581
2618
  //#region src/ui/controls.ts
2582
- var Mn = 3e3, Nn = 250, Pn = /* @__PURE__ */ new Set([
2619
+ var Fn = 3e3, In = 250, Ln = /* @__PURE__ */ new Set([
2583
2620
  "GONE",
2584
2621
  "NOT_FOUND",
2585
2622
  "INVALID_URN",
2586
2623
  "UNAUTHORIZED"
2587
2624
  ]);
2588
- function Fn(e) {
2589
- return new In(e);
2625
+ function Rn(e) {
2626
+ return new zn(e);
2590
2627
  }
2591
- var In = class {
2628
+ var zn = class {
2592
2629
  root;
2593
2630
  video;
2594
2631
  player;
@@ -2622,6 +2659,7 @@ var In = class {
2622
2659
  lastError = null;
2623
2660
  logoUrl;
2624
2661
  statusOverlays;
2662
+ streamEnded;
2625
2663
  endedMessage;
2626
2664
  interruptedMessage;
2627
2665
  errorMessage;
@@ -2634,7 +2672,7 @@ var In = class {
2634
2672
  scrubbing = !1;
2635
2673
  offFns = [];
2636
2674
  constructor(e) {
2637
- 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, An(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");
2675
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Fn, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.streamEnded = e.streamEnded ?? (() => !1), this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, Nn(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");
2638
2676
  let t = (e, t, n) => {
2639
2677
  let r = this.doc.createElement(e);
2640
2678
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -2720,7 +2758,7 @@ var In = class {
2720
2758
  let t = this.surfaceWasHidden;
2721
2759
  this.tapTimer = setTimeout(() => {
2722
2760
  this.tapTimer = void 0, this.toggleBar(t);
2723
- }, Nn);
2761
+ }, In);
2724
2762
  }
2725
2763
  toggleBar(e) {
2726
2764
  e ? this.poke() : this.hideIfIdle();
@@ -2755,7 +2793,7 @@ var In = class {
2755
2793
  e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = Q("play"), e.addEventListener("click", () => void this.player.play()), n.append(e);
2756
2794
  } else {
2757
2795
  let r = this.doc.createElement("div");
2758
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !Pn.has(this.lastError.code)) {
2796
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !Ln.has(this.lastError.code)) {
2759
2797
  let e = this.doc.createElement("button");
2760
2798
  e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${Q("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), n.append(e);
2761
2799
  }
@@ -2796,11 +2834,11 @@ var In = class {
2796
2834
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
2797
2835
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
2798
2836
  }
2799
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Ln(this.video.currentTime)} / ${Ln(this.video.duration)}`);
2837
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Bn(this.video.currentTime)} / ${Bn(this.video.duration)}`);
2800
2838
  }
2801
2839
  syncLive() {
2802
- let e = this.player.getStats(), t = e.isLive !== !1;
2803
- this.live.style.display = t ? "" : "none", this.time.style.display = t ? "none" : "", this.seek.style.display = t ? "none" : "", this.live.classList.toggle("is-live", e.atLiveEdge !== !1 && t);
2840
+ let e = this.player.getStats(), t = e.isLive !== !1, n = this.player.state === "ended", r = !t && !this.streamEnded();
2841
+ this.live.style.display = t && !n ? "" : "none", this.time.style.display = r ? "" : "none", this.seek.style.display = r ? "" : "none", this.live.classList.toggle("is-live", e.atLiveEdge !== !1 && t);
2804
2842
  }
2805
2843
  onSeekDown(e) {
2806
2844
  let t = this.seekableRange();
@@ -2846,17 +2884,17 @@ var In = class {
2846
2884
  renderQualityMenu() {
2847
2885
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
2848
2886
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
2849
- for (let n of e) this.menu.append(this.menuItem(Rn(n), t === n.index, () => this.player.setQuality(n.index)));
2887
+ for (let n of e) this.menu.append(this.menuItem(Vn(n), t === n.index, () => this.player.setQuality(n.index)));
2850
2888
  }
2851
2889
  renderAudioMenu() {
2852
2890
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
2853
2891
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
2854
- for (let t of e) this.menu.append(this.menuItem(zn(t), t.default, () => this.player.setAudioTrack(t.id)));
2892
+ for (let t of e) this.menu.append(this.menuItem(Hn(t), t.default, () => this.player.setAudioTrack(t.id)));
2855
2893
  }
2856
2894
  renderCcMenu() {
2857
2895
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
2858
2896
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
2859
- for (let t of e) this.menu.append(this.menuItem(Bn(t), !1, () => this.player.setTextTrack(t.id)));
2897
+ for (let t of e) this.menu.append(this.menuItem(Un(t), !1, () => this.player.setTextTrack(t.id)));
2860
2898
  }
2861
2899
  onTracksChanged() {
2862
2900
  this.openMenu && this.renderMenu(this.openMenu);
@@ -2957,41 +2995,41 @@ var In = class {
2957
2995
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
2958
2996
  }
2959
2997
  };
2960
- function Ln(e) {
2998
+ function Bn(e) {
2961
2999
  (!Number.isFinite(e) || e < 0) && (e = 0);
2962
3000
  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");
2963
3001
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
2964
3002
  }
2965
- function Rn(e) {
3003
+ function Vn(e) {
2966
3004
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
2967
3005
  }
2968
- function zn(e) {
3006
+ function Hn(e) {
2969
3007
  return e.name || e.lang || `Track ${e.id}`;
2970
3008
  }
2971
- function Bn(e) {
3009
+ function Un(e) {
2972
3010
  return e.name || e.lang || `Track ${e.id}`;
2973
3011
  }
2974
3012
  //#endregion
2975
3013
  //#region src/managed/index.ts
2976
- var Vn = 6e3, Hn = 45e3, Un = 2e3, Wn = 3, Gn = 2e3, Kn = 48e4, qn = "keySystemLicenseRequestFailed";
2977
- function Jn(e) {
3014
+ var Wn = 6e3, Gn = 45e3, Kn = 2e3, qn = 3, Jn = 5e3, Yn = 2e3, Xn = 48e4, Zn = "keySystemLicenseRequestFailed";
3015
+ function Qn(e) {
2978
3016
  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)})`);
2979
- if (e.env !== void 0) return Ke(e.env);
3017
+ if (e.env !== void 0) return Ge(e.env);
2980
3018
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
2981
3019
  return e.baseUrl;
2982
3020
  }
2983
- async function Yn(e) {
3021
+ async function $n(e) {
2984
3022
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
2985
- let t = Jn(e);
3023
+ let t = Qn(e);
2986
3024
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
2987
3025
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
2988
- let r = new Xn({
3026
+ let r = new er({
2989
3027
  ...e,
2990
3028
  baseUrl: t
2991
3029
  });
2992
3030
  return await r.start(), r;
2993
3031
  }
2994
- var Xn = class {
3032
+ var er = class {
2995
3033
  opts;
2996
3034
  abort = new AbortController();
2997
3035
  listeners = {};
@@ -3000,10 +3038,14 @@ var Xn = class {
3000
3038
  container = null;
3001
3039
  detachMedia;
3002
3040
  detachPip;
3041
+ detachEndedPlayGuard;
3003
3042
  statsTimer;
3004
3043
  refreshTimer;
3005
3044
  refreshInFlight;
3006
3045
  onRefocus;
3046
+ onReturnVisible = null;
3047
+ onReturnPageShow = null;
3048
+ hiddenAtMs = null;
3007
3049
  staleLicenseReloaded = !1;
3008
3050
  _state = "idle";
3009
3051
  _descriptor = null;
@@ -3035,10 +3077,10 @@ var Xn = class {
3035
3077
  return this._endedReason;
3036
3078
  }
3037
3079
  async start() {
3038
- 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");
3080
+ this.opts.muted && (this.opts.video.muted = !0), this.opts.poster && (this.opts.video.poster = this.opts.poster), this.mountControls(), this.attachPipListeners(), this.attachEndedPlayGuard(), this.setState("loading");
3039
3081
  let e = ++this.loadGen, t = this.resolveOptions();
3040
3082
  try {
3041
- let n = await v(t);
3083
+ let n = await y(t);
3042
3084
  if (await this.onResolved(n, e), e === this.loadGen && this._state === "error" && this._lastError) {
3043
3085
  let e = this._lastError;
3044
3086
  throw this.destroy(), e;
@@ -3065,7 +3107,7 @@ var Xn = class {
3065
3107
  r ? this.armWithSse(e, r, t, n) : this.beginArmingByPolling(e);
3066
3108
  }
3067
3109
  beginArmingByPolling(e) {
3068
- y({
3110
+ b({
3069
3111
  ...this.resolveOptions(),
3070
3112
  signal: this.abort.signal,
3071
3113
  waitForLive: this.buildWaitConfig(e)
@@ -3082,7 +3124,7 @@ var Xn = class {
3082
3124
  setTimeout(() => {
3083
3125
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
3084
3126
  phase: i ? "unavailable" : "tooEarly",
3085
- retryInMs: Hn,
3127
+ retryInMs: Gn,
3086
3128
  attempt: 1,
3087
3129
  elapsedMs: 0,
3088
3130
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -3099,7 +3141,7 @@ var Xn = class {
3099
3141
  }
3100
3142
  }
3101
3143
  }, h = () => {
3102
- a || s || p() || (s = !0, y({
3144
+ a || s || p() || (s = !0, b({
3103
3145
  ...this.resolveOptions(),
3104
3146
  signal: c.signal,
3105
3147
  waitForLive: this.buildWaitConfig(e)
@@ -3107,7 +3149,7 @@ var Xn = class {
3107
3149
  a || p() || c.signal.aborted || (a = !0, this.closeArming(), this.fail(e instanceof n ? e : new n("INTERNAL", 0, String(e))));
3108
3150
  }));
3109
3151
  }, g = () => {
3110
- a || p() || v(this.resolveOptions()).then((e) => m(e)).catch((e) => {
3152
+ a || p() || y(this.resolveOptions()).then((e) => m(e)).catch((e) => {
3111
3153
  if (!(a || p())) {
3112
3154
  if (e instanceof n && o(e.code)) {
3113
3155
  h();
@@ -3121,13 +3163,13 @@ var Xn = class {
3121
3163
  !a && !p() && h();
3122
3164
  }, e);
3123
3165
  };
3124
- _(Vn), this.armingSub = T({
3166
+ _(Wn), this.armingSub = T({
3125
3167
  eventsBaseUrl: t,
3126
3168
  matchUrn: this.opts.matchUrn,
3127
3169
  credential: this.opts.credential,
3128
- onAlive: () => _(Hn),
3170
+ onAlive: () => _(Gn),
3129
3171
  onState: (e) => {
3130
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Un);
3172
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Kn);
3131
3173
  },
3132
3174
  onError: () => h()
3133
3175
  });
@@ -3161,7 +3203,7 @@ var Xn = class {
3161
3203
  a(), i.destroy();
3162
3204
  return;
3163
3205
  }
3164
- this.engine = i, this.detachMedia = a, this.opts.maxBitrate != null && i.setMaxBitrate(this.opts.maxBitrate), this.startStats(), this.scheduleLicenseRefresh(), this.startLiveState(t), this.startAnalytics(e, i, t), r && (this.pendingStartLoad = !0, this.setState("paused"), i.name === "native" && setTimeout(() => {
3206
+ this.engine = i, this.detachMedia = a, this.opts.maxBitrate != null && i.setMaxBitrate(this.opts.maxBitrate), this.startStats(), this.scheduleLicenseRefresh(), this.startLiveState(t), this.watchReturn(), this.startAnalytics(e, i, t), r && (this.pendingStartLoad = !0, this.setState("paused"), i.name === "native" && setTimeout(() => {
3165
3207
  this.destroyed || t !== this.loadGen || this.engine !== i || (this.analyticsSession?.noteReady(), this.emit("ready"));
3166
3208
  }, 0));
3167
3209
  }
@@ -3172,26 +3214,26 @@ var Xn = class {
3172
3214
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
3173
3215
  let i = e.analytics?.sid;
3174
3216
  if (!i) return;
3175
- let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = gn(this.opts.baseUrl, a, e.analytics?.endpoint);
3217
+ let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = yn(this.opts.baseUrl, a, e.analytics?.endpoint);
3176
3218
  if (!o) {
3177
3219
  s && setTimeout(() => {
3178
3220
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, s));
3179
3221
  }, 0);
3180
3222
  return;
3181
3223
  }
3182
- let c = this.opts.video, l = new _n({
3224
+ let c = this.opts.video, l = new bn({
3183
3225
  endpoint: o,
3184
3226
  sid: i,
3185
3227
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
3186
- sdkVersion: Cn,
3187
- environment: xn(c),
3228
+ sdkVersion: En,
3229
+ environment: wn(c),
3188
3230
  idleMs: typeof this.opts.analytics == "object" ? this.opts.analytics.idleMs : void 0,
3189
3231
  onIdle: () => this.onAnalyticsIdle(t),
3190
3232
  onBlocked: (e) => setTimeout(() => {
3191
3233
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, e));
3192
3234
  }, 0),
3193
3235
  sample: () => {
3194
- let e = this.engine?.getStats() ?? qe, t = c.buffered.length ? Math.max(0, c.buffered.end(c.buffered.length - 1) - c.currentTime) : 0;
3236
+ let e = this.engine?.getStats() ?? Ke, t = c.buffered.length ? Math.max(0, c.buffered.end(c.buffered.length - 1) - c.currentTime) : 0;
3195
3237
  return {
3196
3238
  ...e,
3197
3239
  bufferedMs: Math.round(t * 1e3),
@@ -3205,7 +3247,7 @@ var Xn = class {
3205
3247
  let u = () => l.noteSeek(), d = () => l.noteVolume(c.muted, c.volume);
3206
3248
  c.addEventListener("seeking", u), c.addEventListener("volumechange", d), this.detachMediaListeners = () => {
3207
3249
  c.removeEventListener("seeking", u), c.removeEventListener("volumechange", d);
3208
- }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), Sn().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
3250
+ }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), Tn().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
3209
3251
  }
3210
3252
  currentPresentation() {
3211
3253
  let e = this.opts.video;
@@ -3232,9 +3274,9 @@ var Xn = class {
3232
3274
  freshLicenseUrl: () => this.refreshLicense()
3233
3275
  }, o = this.opts.engine ?? "auto";
3234
3276
  if (o === "native") return new W(a);
3235
- if (o === "hls") return new Qt(a);
3236
- if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && H(this.opts.video) && U() || !e.drmEnabled && ot() && H(this.opts.video)) return new W(a);
3237
- if (vt()) return new Qt(a);
3277
+ if (o === "hls") return new en(a);
3278
+ if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && H(this.opts.video) && U() || !e.drmEnabled && lt() && H(this.opts.video)) return new W(a);
3279
+ if (bt()) return new en(a);
3238
3280
  if (H(this.opts.video)) return new W(a);
3239
3281
  throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
3240
3282
  }
@@ -3297,29 +3339,53 @@ var Xn = class {
3297
3339
  r = 0, i = !1;
3298
3340
  },
3299
3341
  onError: () => {
3300
- this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= Wn && (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.`))));
3342
+ this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= qn && (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.`))));
3301
3343
  }
3302
3344
  });
3303
3345
  }
3304
3346
  closeLiveState() {
3305
3347
  this.liveStateSub?.close(), this.liveStateSub = null;
3306
3348
  }
3349
+ watchReturn() {
3350
+ this.onReturnVisible || typeof document > "u" || this.opts.liveStateEvents !== !1 && (document.visibilityState === "hidden" && (this.hiddenAtMs = Date.now()), this.onReturnVisible = () => {
3351
+ if (document.visibilityState === "hidden") {
3352
+ this.hiddenAtMs ??= Date.now();
3353
+ return;
3354
+ }
3355
+ let e = this.hiddenAtMs;
3356
+ this.hiddenAtMs = null, e != null && Date.now() - e >= Jn && this.resyncLiveState();
3357
+ }, this.onReturnPageShow = (e) => {
3358
+ e.persisted && this.resyncLiveState();
3359
+ }, document.addEventListener("visibilitychange", this.onReturnVisible), typeof window < "u" && window.addEventListener("pageshow", this.onReturnPageShow));
3360
+ }
3361
+ unwatchReturn() {
3362
+ this.onReturnVisible && typeof document < "u" && document.removeEventListener("visibilitychange", this.onReturnVisible), this.onReturnPageShow && typeof window < "u" && window.removeEventListener("pageshow", this.onReturnPageShow), this.onReturnVisible = null, this.onReturnPageShow = null, this.hiddenAtMs = null;
3363
+ }
3364
+ resyncLiveState() {
3365
+ !this.destroyed && this.liveStateSub && this.startLiveState(this.loadGen);
3366
+ }
3307
3367
  onStreamEnded(e = !1, t) {
3308
3368
  if (!this.destroyed) {
3309
3369
  if ((e || !this.rejoinEnabled()) && (this.cancelRejoin(), this.closeLiveState()), this.endedHandled) {
3310
- t === "match_ended" && this._endedReason !== "match_ended" && (this._endedReason = t, this.emit("ended", t));
3370
+ t === "match_ended" && this._endedReason !== "match_ended" && (this._endedReason = t, this.emit("ended", t), this.leavePipOnEnd());
3311
3371
  return;
3312
3372
  }
3313
- this.endedHandled = !0, this._endedReason = t ?? null, this.stopRefresh(), this.pendingStartLoad = !1, this.engine?.stop(), this.setState("ended");
3373
+ this.endedHandled = !0, this._endedReason = t ?? null, this.stopRefresh(), this.pendingStartLoad = !1, this.engine?.stop(), this.setState("ended"), this.leavePipOnEnd();
3314
3374
  }
3315
3375
  }
3376
+ leavePipOnEnd() {
3377
+ this._endedReason === "interrupted" && this.rejoinEnabled() || this.exitPictureInPicture().catch(() => {});
3378
+ }
3379
+ get streamEnded() {
3380
+ return this._state === "ended" && this.endedHandled;
3381
+ }
3316
3382
  rejoinEnabled() {
3317
3383
  return this.opts.rejoinOnLive !== !1 && this.opts.liveStateEvents !== !1;
3318
3384
  }
3319
3385
  scheduleRejoin(e) {
3320
3386
  this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
3321
3387
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
3322
- }, Math.random() * Un));
3388
+ }, Math.random() * Kn));
3323
3389
  }
3324
3390
  cancelRejoin() {
3325
3391
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -3343,13 +3409,14 @@ var Xn = class {
3343
3409
  }
3344
3410
  t.controls = !1;
3345
3411
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
3346
- r && r.insertBefore(n, t), n.appendChild(t), En(n, this.opts.theme), this.container = n, this.controlBar = Fn({
3412
+ r && r.insertBefore(n, t), n.appendChild(t), kn(n, this.opts.theme), this.container = n, this.controlBar = Rn({
3347
3413
  root: n,
3348
3414
  video: t,
3349
3415
  player: this,
3350
3416
  logoUrl: this.opts.theme?.logoUrl,
3351
3417
  statusOverlays: this.opts.statusOverlays,
3352
3418
  endedMessage: this.opts.endedMessage,
3419
+ streamEnded: () => this.streamEnded,
3353
3420
  interruptedMessage: this.opts.interruptedMessage,
3354
3421
  errorMessage: this.opts.errorMessage,
3355
3422
  cspNonce: this.opts.cspNonce
@@ -3372,9 +3439,15 @@ var Xn = class {
3372
3439
  e.removeEventListener("enterpictureinpicture", n), e.removeEventListener("leavepictureinpicture", r), i?.removeEventListener("fullscreenchange", t), i?.removeEventListener("webkitfullscreenchange", t);
3373
3440
  };
3374
3441
  }
3442
+ attachEndedPlayGuard() {
3443
+ let e = this.opts.video, t = () => {
3444
+ this.streamEnded && e.pause();
3445
+ };
3446
+ e.addEventListener("play", t), this.detachEndedPlayGuard = () => e.removeEventListener("play", t);
3447
+ }
3375
3448
  startStats() {
3376
3449
  this.stopStats();
3377
- let e = Math.max(500, this.opts.statsIntervalMs ?? Gn);
3450
+ let e = Math.max(500, this.opts.statsIntervalMs ?? Yn);
3378
3451
  this.statsTimer = setInterval(() => {
3379
3452
  if (!this.engine) return;
3380
3453
  let e = this.engine.getStats();
@@ -3385,14 +3458,14 @@ var Xn = class {
3385
3458
  this.statsTimer && clearInterval(this.statsTimer), this.statsTimer = void 0;
3386
3459
  }
3387
3460
  isStaleLicenseDenial(e) {
3388
- return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== qn || e.error.httpStatus !== 403 ? !1 : K(this._descriptor?.drm?.widevine?.licenseUrl);
3461
+ return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== Zn || e.error.httpStatus !== 403 ? !1 : K(this._descriptor?.drm?.widevine?.licenseUrl);
3389
3462
  }
3390
3463
  scheduleLicenseRefresh() {
3391
3464
  if (this.stopRefresh(), !this._descriptor?.drmEnabled) return;
3392
3465
  typeof document < "u" && this.engine?.name === "hls" && (this.onRefocus = () => {
3393
3466
  document.visibilityState !== "visible" || this.destroyed || K(this._descriptor?.drm?.widevine?.licenseUrl) && this.refreshLicense();
3394
3467
  }, document.addEventListener("visibilitychange", this.onRefocus));
3395
- let e = this.opts.licenseRefreshMs ?? Kn;
3468
+ let e = this.opts.licenseRefreshMs ?? Xn;
3396
3469
  e && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
3397
3470
  }
3398
3471
  stopRefresh() {
@@ -3404,7 +3477,7 @@ var Xn = class {
3404
3477
  if (this.refreshInFlight?.engine === e) return this.refreshInFlight.promise;
3405
3478
  let t = (async () => {
3406
3479
  try {
3407
- let t = await v(this.resolveOptions());
3480
+ let t = await y(this.resolveOptions());
3408
3481
  if (this.destroyed || this.engine !== e) return;
3409
3482
  let n = t.drm?.widevine?.licenseUrl, r = t.drm?.fairplay?.licenseUrl;
3410
3483
  return (n || r) && (e.setLicenseUrls({
@@ -3430,6 +3503,7 @@ var Xn = class {
3430
3503
  this.idleClosed = !1, await this.reload();
3431
3504
  return;
3432
3505
  }
3506
+ if (this.streamEnded) return;
3433
3507
  let t = this.pendingStartLoad && this._state !== "ended" && this._state !== "error";
3434
3508
  this.opts.autoplay === !1 && this.analyticsSession?.notePlayIntent(), t && (this.pendingStartLoad = !1, this.setState("loading"), this.engine?.startLoad());
3435
3509
  try {
@@ -3448,7 +3522,7 @@ var Xn = class {
3448
3522
  this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
3449
3523
  }
3450
3524
  getStats() {
3451
- return this.engine?.getStats() ?? qe;
3525
+ return this.engine?.getStats() ?? Ke;
3452
3526
  }
3453
3527
  getQualityLevels() {
3454
3528
  return this.engine?.getQualityLevels() ?? [];
@@ -3500,7 +3574,7 @@ var Xn = class {
3500
3574
  let e = ++this.loadGen;
3501
3575
  this.teardownPlayback();
3502
3576
  try {
3503
- let t = await y({
3577
+ let t = await b({
3504
3578
  ...this.resolveOptions(),
3505
3579
  waitForLive: this.opts.waitForLive ? this.buildWaitConfig(e) : void 0
3506
3580
  });
@@ -3515,7 +3589,7 @@ var Xn = class {
3515
3589
  return n.add(t), () => n.delete(t);
3516
3590
  }
3517
3591
  destroy() {
3518
- this.destroyed || (this.destroyed = !0, this.loadGen += 1, this.abort.abort(), this.stopStats(), this.stopRefresh(), this.unmountControls(), this.cancelRejoin(), this.closeArming(), this.closeLiveState(), this.detachMedia?.(), this.detachPip?.(), this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.engine?.destroy(), this.engine = null, this.setState("idle"), this.clearListeners());
3592
+ this.destroyed || (this.destroyed = !0, this.loadGen += 1, this.abort.abort(), this.stopStats(), this.stopRefresh(), this.unmountControls(), this.cancelRejoin(), this.closeArming(), this.closeLiveState(), this.unwatchReturn(), this.detachMedia?.(), this.detachPip?.(), this.detachEndedPlayGuard?.(), this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.engine?.destroy(), this.engine = null, this.setState("idle"), this.clearListeners());
3519
3593
  }
3520
3594
  clearListeners() {
3521
3595
  for (let e of Object.keys(this.listeners)) this.listeners[e]?.clear();
@@ -3558,7 +3632,7 @@ function $(e) {
3558
3632
  }
3559
3633
  //#endregion
3560
3634
  //#region src/embed/host.ts
3561
- function Zn(e) {
3635
+ function tr(e) {
3562
3636
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
3563
3637
  if (!e.src) throw Error("mountEmbed: opts.src is required");
3564
3638
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -3617,15 +3691,15 @@ function Zn(e) {
3617
3691
  }
3618
3692
  //#endregion
3619
3693
  //#region src/embed/page.ts
3620
- var Qn = /^[A-Za-z0-9:_.-]{1,128}$/;
3621
- function $n(e) {
3694
+ var nr = /^[A-Za-z0-9:_.-]{1,128}$/;
3695
+ function rr(e) {
3622
3696
  if (typeof e == "boolean") return e;
3623
3697
  if (typeof e == "object" && e) {
3624
3698
  let t = e.endpoint;
3625
3699
  return typeof t == "string" ? { endpoint: t } : {};
3626
3700
  }
3627
3701
  }
3628
- function er() {
3702
+ function ir() {
3629
3703
  let e = window.HAVIK_EMBED_CONFIG;
3630
3704
  if (e) return {
3631
3705
  baseUrl: e.baseUrl ?? "",
@@ -3635,7 +3709,7 @@ function er() {
3635
3709
  autoplay: e.autoplay ?? !0,
3636
3710
  muted: e.muted ?? !0,
3637
3711
  waitForLive: e.waitForLive ?? !0,
3638
- analytics: $n(e.analytics)
3712
+ analytics: rr(e.analytics)
3639
3713
  };
3640
3714
  let t = new URLSearchParams(location.search);
3641
3715
  return {
@@ -3648,9 +3722,9 @@ function er() {
3648
3722
  waitForLive: !0
3649
3723
  };
3650
3724
  }
3651
- async function tr() {
3652
- let e = er();
3653
- rr();
3725
+ async function ar() {
3726
+ let e = ir();
3727
+ sr();
3654
3728
  let t = document.createElement("video");
3655
3729
  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);
3656
3730
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -3705,12 +3779,12 @@ async function tr() {
3705
3779
  code: e.code,
3706
3780
  message: e.message
3707
3781
  })), e.on("autoplayblocked", () => {
3708
- i({ type: "oddin:autoplayblocked" }), nr(t, e);
3782
+ i({ type: "oddin:autoplayblocked" }), or(t, e);
3709
3783
  });
3710
3784
  }, s = async (r) => {
3711
3785
  a?.destroy(), a = null;
3712
3786
  try {
3713
- let n = await Yn({
3787
+ let n = await $n({
3714
3788
  video: t,
3715
3789
  baseUrl: e.baseUrl,
3716
3790
  matchUrn: r,
@@ -3772,7 +3846,7 @@ async function tr() {
3772
3846
  a?.retry();
3773
3847
  break;
3774
3848
  case "oddin:load":
3775
- typeof e.matchUrn == "string" && Qn.test(e.matchUrn) && s(e.matchUrn);
3849
+ typeof e.matchUrn == "string" && nr.test(e.matchUrn) && s(e.matchUrn);
3776
3850
  break;
3777
3851
  case "oddin:destroy": c();
3778
3852
  }
@@ -3784,18 +3858,18 @@ async function tr() {
3784
3858
  protocol: 1
3785
3859
  });
3786
3860
  }
3787
- function nr(e, t) {
3861
+ function or(e, t) {
3788
3862
  if (document.getElementById("havik-tap")) return;
3789
3863
  let n = document.createElement("button");
3790
3864
  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", () => {
3791
3865
  n.remove(), t.play();
3792
3866
  }), document.body.appendChild(n);
3793
3867
  }
3794
- function rr() {
3868
+ function sr() {
3795
3869
  let e = document.createElement("style");
3796
3870
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
3797
3871
  }
3798
3872
  //#endregion
3799
- export { wn as ODDIN_THEME, n as PlaybackError, En as applyTheme, Ke as baseUrlForEnv, tr as bootEmbed, Te as clearDeviceId, Yn 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, Zn as mountEmbed, de as pollToLive, y as resolveStream, T as subscribeLiveState, S as videoFullscreenActive, Ce as watchStatus };
3873
+ export { Dn as ODDIN_THEME, n as PlaybackError, kn as applyTheme, Ge as baseUrlForEnv, ar as bootEmbed, Te as clearDeviceId, $n as createPlayer, E as deriveEventsBaseUrl, Ve as detectDrmSupport, C as enterVideoFullscreen, w as exitVideoFullscreen, _e as fetchCatalog, ye as fetchTournament, xe as flattenMatches, k as getDeviceId, Ue as isLowLatencyManifest, $ as isOddinMessage, De as licenseRequestHeaders, tr as mountEmbed, v as pollToLive, b as resolveStream, T as subscribeLiveState, S as videoFullscreenActive, Ce as watchStatus };
3800
3874
 
3801
3875
  //# sourceMappingURL=index.js.map