@oddin-gg/havik-player 2.22.1 → 2.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -897,32 +897,32 @@ function at(e) {
897
897
  }
898
898
  //#endregion
899
899
  //#region src/core/stats.ts
900
- var z = { droppedFrames: 0 };
901
- function ot(e) {
900
+ var ot = { droppedFrames: 0 };
901
+ function st(e) {
902
902
  try {
903
903
  return new URL(e).host;
904
904
  } catch {
905
905
  return "";
906
906
  }
907
907
  }
908
- var st = /\.(m3u8|ts|m4s|mp4|cmfv|cmfa)(\?|$)/i;
909
- function ct(e) {
908
+ var ct = /\.(m3u8|ts|m4s|mp4|cmfv|cmfa)(\?|$)/i;
909
+ function lt(e) {
910
910
  if (typeof performance > "u" || typeof performance.getEntriesByType != "function") return "";
911
911
  let t = "", n = -1, r = performance.getEntriesByType("resource");
912
- for (let i of r) if (!(i.startTime < e) && (i.initiatorType === "video" || i.initiatorType === "audio" || st.test(i.name)) && i.startTime >= n) {
913
- let e = ot(i.name);
912
+ for (let i of r) if (!(i.startTime < e) && (i.initiatorType === "video" || i.initiatorType === "audio" || ct.test(i.name)) && i.startTime >= n) {
913
+ let e = st(i.name);
914
914
  e && (t = e, n = i.startTime);
915
915
  }
916
916
  return t;
917
917
  }
918
- function B(e) {
918
+ function z(e) {
919
919
  let t = e.getVideoPlaybackQuality?.();
920
920
  return {
921
921
  dropped: t?.droppedVideoFrames ?? 0,
922
922
  totalFrames: t?.totalVideoFrames ?? 0
923
923
  };
924
924
  }
925
- var lt = class {
925
+ var ut = class {
926
926
  video;
927
927
  baseFrames = 0;
928
928
  baseDropped = 0;
@@ -932,7 +932,7 @@ var lt = class {
932
932
  this.video = e;
933
933
  }
934
934
  read() {
935
- let e = B(this.video);
935
+ let e = z(this.video);
936
936
  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, {
937
937
  dropped: this.baseDropped + e.dropped,
938
938
  totalFrames: this.baseFrames + e.totalFrames
@@ -945,7 +945,7 @@ var lt = class {
945
945
  };
946
946
  }
947
947
  };
948
- function ut(e, t) {
948
+ function dt(e, t) {
949
949
  let n = [
950
950
  ["playing", () => t("playing")],
951
951
  ["waiting", () => t("buffering")],
@@ -962,8 +962,8 @@ function ut(e, t) {
962
962
  }
963
963
  //#endregion
964
964
  //#region src/core/engine/native.ts
965
- var dt = 2e3, ft = 3, V = 500, pt = 6e3, mt = .25, ht = 250, gt = .05, _t = 4, vt = 12e3, yt = .25, bt = 1e3;
966
- function xt(e) {
965
+ var ft = 2e3, pt = 3, B = 500, mt = 6e3, ht = .25, gt = 250, _t = .05, vt = 4, yt = 12e3, bt = .25, xt = 1e3;
966
+ function St(e) {
967
967
  try {
968
968
  let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
969
969
  return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
@@ -971,20 +971,20 @@ function xt(e) {
971
971
  return;
972
972
  }
973
973
  }
974
- function H(e) {
974
+ function V(e) {
975
975
  return !!(e ?? (typeof document < "u" ? document.createElement("video") : void 0))?.canPlayType("application/vnd.apple.mpegurl");
976
976
  }
977
- function U() {
977
+ function H() {
978
978
  if (typeof window > "u") return !1;
979
979
  let e = "WebKitMediaKeys" in window || typeof HTMLMediaElement < "u" && "webkitSetMediaKeys" in HTMLMediaElement.prototype, t = typeof navigator < "u" && /Apple/i.test(navigator.vendor ?? "");
980
980
  return e || t;
981
981
  }
982
- function St() {
983
- if (typeof navigator > "u" || !U()) return !1;
982
+ function Ct() {
983
+ if (typeof navigator > "u" || !H()) return !1;
984
984
  let e = navigator.userAgent ?? "";
985
985
  return /iPhone|iPad|iPod/.test(e) ? !0 : /Macintosh/.test(e) && typeof navigator.maxTouchPoints == "number" && navigator.maxTouchPoints > 1;
986
986
  }
987
- var W = class {
987
+ var U = class {
988
988
  name = "native";
989
989
  deps;
990
990
  onLoaded;
@@ -1014,7 +1014,7 @@ var W = class {
1014
1014
  frameCounter;
1015
1015
  detachFrames = null;
1016
1016
  constructor(e) {
1017
- this.deps = e, this.frameCounter = new lt(e.video), this.loadStarted = e.deferLoad !== !0, this.builtAt = typeof performance < "u" ? performance.now() : 0;
1017
+ this.deps = e, this.frameCounter = new ut(e.video), this.loadStarted = e.deferLoad !== !0, this.builtAt = typeof performance < "u" ? performance.now() : 0;
1018
1018
  }
1019
1019
  async load() {
1020
1020
  this.detachFrames ??= this.frameCounter.attach();
@@ -1035,7 +1035,7 @@ var W = class {
1035
1035
  type: "error",
1036
1036
  error: new n("INTERNAL", 0, "native HLS playback error"),
1037
1037
  fatal: !0
1038
- }), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = wt(t.manifestUrl, t.analytics?.sid), e.load();
1038
+ }), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = Tt(t.manifestUrl, t.analytics?.sid), e.load();
1039
1039
  }
1040
1040
  startLoad() {
1041
1041
  this.loadStarted || (this.loadStarted = !0, this.restorePreload());
@@ -1063,7 +1063,7 @@ var W = class {
1063
1063
  };
1064
1064
  this.onEncrypted = (n) => {
1065
1065
  let a = n;
1066
- a.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = xt(a.initData), (async () => {
1066
+ a.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = St(a.initData), (async () => {
1067
1067
  try {
1068
1068
  let n = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
1069
1069
  initDataTypes: [a.initDataType],
@@ -1163,10 +1163,10 @@ var W = class {
1163
1163
  droppedFrames: t.dropped,
1164
1164
  decodedFrames: t.totalFrames
1165
1165
  };
1166
- if (this.cdnHost ||= ct(this.builtAt), this.cdnHost && (n.cdnHost = this.cdnHost), this.deps.descriptor.drmEnabled && (n.keySystem = "fairplay"), e.duration === Infinity) {
1166
+ if (this.cdnHost ||= lt(this.builtAt), this.cdnHost && (n.cdnHost = this.cdnHost), this.deps.descriptor.drmEnabled && (n.keySystem = "fairplay"), e.duration === Infinity) {
1167
1167
  n.isLive = !0;
1168
1168
  let t = this.liveEdgeEstimate(), r = t == null ? NaN : t - e.currentTime;
1169
- Number.isFinite(r) && (n.atLiveEdge = r <= _t, !e.paused && r >= 0 && (n.latencySeconds = r));
1169
+ Number.isFinite(r) && (n.atLiveEdge = r <= vt, !e.paused && r >= 0 && (n.latencySeconds = r));
1170
1170
  } else Number.isFinite(e.duration) && e.duration > 0 && (n.isLive = !1);
1171
1171
  return n;
1172
1172
  }
@@ -1178,30 +1178,30 @@ var W = class {
1178
1178
  }
1179
1179
  watchRefocus() {
1180
1180
  this.deps.snapToLiveOnRefocus === !1 || typeof document > "u" || this.onVisibility || (this.onVisibility = () => {
1181
- let e = G();
1181
+ let e = W();
1182
1182
  if (document.visibilityState === "hidden") {
1183
1183
  this.hiddenSince = e, this.cancelRefocus();
1184
1184
  return;
1185
1185
  }
1186
1186
  let t = this.hiddenSince;
1187
- this.hiddenSince = null, t != null && e - t >= dt && this.armRefocus();
1187
+ this.hiddenSince = null, t != null && e - t >= ft && this.armRefocus();
1188
1188
  }, this.onPageShow = (e) => {
1189
1189
  e.persisted && this.armRefocus();
1190
1190
  }, document.addEventListener("visibilitychange", this.onVisibility), typeof window < "u" && window.addEventListener("pageshow", this.onPageShow));
1191
1191
  }
1192
1192
  armRefocus() {
1193
1193
  if (this.cancelRefocus(), Number.isFinite(this.deps.video.duration)) return;
1194
- let e = this.liveEnd(), t = G, n = t(), r = 0, i = () => {
1194
+ let e = this.liveEnd(), t = W, n = t(), r = 0, i = () => {
1195
1195
  if (this.refocusTimer = null, Number.isFinite(this.deps.video.duration)) return;
1196
- r += V;
1197
- let a = this.liveEnd(), o = a != null && (e == null || a >= e + mt), s = Math.max(r, t() - n) >= pt;
1196
+ r += B;
1197
+ let a = this.liveEnd(), o = a != null && (e == null || a >= e + ht), s = Math.max(r, t() - n) >= mt;
1198
1198
  if (o || s && a != null) {
1199
1199
  this.snapToLive();
1200
1200
  return;
1201
1201
  }
1202
- this.refocusTimer = setTimeout(i, V);
1202
+ this.refocusTimer = setTimeout(i, B);
1203
1203
  };
1204
- this.refocusTimer = setTimeout(i, V);
1204
+ this.refocusTimer = setTimeout(i, B);
1205
1205
  }
1206
1206
  cancelRefocus() {
1207
1207
  this.refocusTimer != null && clearTimeout(this.refocusTimer), this.refocusTimer = null;
@@ -1214,24 +1214,24 @@ var W = class {
1214
1214
  }, e.addEventListener("seeking", this.onEdgeReset), e.addEventListener("emptied", this.onEdgeReset), this.edgeTimer = setInterval(() => {
1215
1215
  if (e.duration !== Infinity) return;
1216
1216
  this.checkEndStale();
1217
- let t = Ct(e, e.currentTime);
1218
- t != null && ((this.lastBufferedEnd == null || Math.abs(t - this.lastBufferedEnd) >= gt) && (this.bufferedAdvancedAt = G()), this.lastBufferedEnd = t);
1219
- }, ht);
1217
+ let t = wt(e, e.currentTime);
1218
+ t != null && ((this.lastBufferedEnd == null || Math.abs(t - this.lastBufferedEnd) >= _t) && (this.bufferedAdvancedAt = W()), this.lastBufferedEnd = t);
1219
+ }, gt);
1220
1220
  }
1221
1221
  checkEndStale() {
1222
1222
  if (this.endedEmitted) return;
1223
- let e = G(), t = this.lastTickAt == null ? 0 : e - this.lastTickAt;
1223
+ let e = W(), t = this.lastTickAt == null ? 0 : e - this.lastTickAt;
1224
1224
  this.lastTickAt = e;
1225
1225
  let n = this.liveEnd();
1226
1226
  if (n == null) return;
1227
- let r = this.lastSeekableEnd == null || Math.abs(n - this.lastSeekableEnd) >= yt;
1227
+ let r = this.lastSeekableEnd == null || Math.abs(n - this.lastSeekableEnd) >= bt;
1228
1228
  this.lastSeekableEnd = n;
1229
1229
  let i = this.deps.video, a = typeof document < "u" && document.visibilityState === "hidden";
1230
- if (r || t > bt || a || i.paused) {
1230
+ if (r || t > xt || a || i.paused) {
1231
1231
  this.edgeStampedAt = e;
1232
1232
  return;
1233
1233
  }
1234
- e - (this.edgeStampedAt ?? e) > vt && (this.endedEmitted = !0, this.deps.emit({
1234
+ e - (this.edgeStampedAt ?? e) > yt && (this.endedEmitted = !0, this.deps.emit({
1235
1235
  type: "ended",
1236
1236
  reason: "stalled"
1237
1237
  }));
@@ -1245,9 +1245,9 @@ var W = class {
1245
1245
  liveEdgeEstimate() {
1246
1246
  let e = this.deps.video;
1247
1247
  if (e.duration !== Infinity) return null;
1248
- let t = this.liveEnd(), n = Ct(e, e.currentTime) ?? this.lastBufferedEnd;
1248
+ let t = this.liveEnd(), n = wt(e, e.currentTime) ?? this.lastBufferedEnd;
1249
1249
  if (n != null) {
1250
- let e = this.bufferedAdvancedAt, r = n + (e == null ? 0 : Math.max(0, (G() - e) / 1e3));
1250
+ let e = this.bufferedAdvancedAt, r = n + (e == null ? 0 : Math.max(0, (W() - e) / 1e3));
1251
1251
  (t == null || r > t) && (t = r);
1252
1252
  }
1253
1253
  return t != null && Number.isFinite(t) ? t : null;
@@ -1261,7 +1261,7 @@ var W = class {
1261
1261
  snapToLive() {
1262
1262
  let e = this.deps.video, t = this.liveEnd();
1263
1263
  if (t == null) return;
1264
- let n = e.seekable.start(e.seekable.length - 1), r = Math.min(e.currentTime, t), i = Math.max(n, t - ft, r);
1264
+ let n = e.seekable.start(e.seekable.length - 1), r = Math.min(e.currentTime, t), i = Math.max(n, t - pt, r);
1265
1265
  try {
1266
1266
  e.currentTime = i;
1267
1267
  } catch {}
@@ -1279,10 +1279,10 @@ var W = class {
1279
1279
  } catch {}
1280
1280
  }
1281
1281
  };
1282
- function G() {
1282
+ function W() {
1283
1283
  return typeof performance < "u" ? performance.now() : Date.now();
1284
1284
  }
1285
- function Ct(e, t) {
1285
+ function wt(e, t) {
1286
1286
  let n = e.buffered;
1287
1287
  if (!n || !Number.isFinite(t)) return null;
1288
1288
  for (let e = 0; e < n.length; e += 1) if (n.start(e) <= t + .1 && n.end(e) > t) {
@@ -1291,12 +1291,12 @@ function Ct(e, t) {
1291
1291
  }
1292
1292
  return null;
1293
1293
  }
1294
- function wt(e, t) {
1294
+ function Tt(e, t) {
1295
1295
  return t ? `${e}${e.includes("?") ? "&" : "?"}CMCD=${encodeURIComponent(`sid="${t}"`)}` : e;
1296
1296
  }
1297
1297
  //#endregion
1298
1298
  //#region src/core/drm/signedUrl.ts
1299
- function K(e, t = Date.now()) {
1299
+ function G(e, t = Date.now()) {
1300
1300
  if (!e) return !1;
1301
1301
  let n;
1302
1302
  try {
@@ -1308,13 +1308,13 @@ function K(e, t = Date.now()) {
1308
1308
  }
1309
1309
  //#endregion
1310
1310
  //#region src/core/drm/xhrBody.ts
1311
- var Tt = 4096;
1312
- function Et(e) {
1311
+ var Et = 4096;
1312
+ function Dt(e) {
1313
1313
  if (!e || typeof e != "object") return;
1314
1314
  let { response: t } = e;
1315
1315
  if (typeof t == "string") return t;
1316
1316
  let n;
1317
- if (Object.prototype.toString.call(t) === "[object ArrayBuffer]" ? n = new Uint8Array(t) : ArrayBuffer.isView(t) && (n = new Uint8Array(t.buffer, t.byteOffset, t.byteLength)), !(!n || n.byteLength === 0 || n.byteLength > Tt)) try {
1317
+ if (Object.prototype.toString.call(t) === "[object ArrayBuffer]" ? n = new Uint8Array(t) : ArrayBuffer.isView(t) && (n = new Uint8Array(t.buffer, t.byteOffset, t.byteLength)), !(!n || n.byteLength === 0 || n.byteLength > Et)) try {
1318
1318
  return new TextDecoder().decode(n);
1319
1319
  } catch {
1320
1320
  return;
@@ -1322,8 +1322,8 @@ function Et(e) {
1322
1322
  }
1323
1323
  //#endregion
1324
1324
  //#region src/core/engine/prefetch.ts
1325
- var Dt = 15e3, Ot = 5e3, kt = 6;
1326
- function At(e) {
1325
+ var Ot = 15e3, kt = 5e3, At = 6;
1326
+ function jt(e) {
1327
1327
  try {
1328
1328
  let t = new URL(e);
1329
1329
  return t.searchParams.delete("CMCD"), t.toString();
@@ -1331,7 +1331,7 @@ function At(e) {
1331
1331
  return e;
1332
1332
  }
1333
1333
  }
1334
- var jt = class {
1334
+ var Mt = class {
1335
1335
  cmcd;
1336
1336
  entries = /* @__PURE__ */ new Map();
1337
1337
  keyed = /* @__PURE__ */ new Set();
@@ -1354,10 +1354,10 @@ var jt = class {
1354
1354
  }
1355
1355
  prefetch(e, t, n) {
1356
1356
  if (!this.isActive(t)) return;
1357
- let r = At(e);
1357
+ let r = jt(e);
1358
1358
  if (this.entries.has(r)) return;
1359
1359
  let i = this.perTrack.get(t) ?? 0;
1360
- if (i >= kt) return;
1360
+ if (i >= At) return;
1361
1361
  this.perTrack.set(t, i + 1);
1362
1362
  let a = new AbortController(), o = {
1363
1363
  started: performance.now(),
@@ -1371,10 +1371,10 @@ var jt = class {
1371
1371
  signal: a.signal
1372
1372
  }).then(async (e) => (o.first = performance.now(), e.ok ? e.arrayBuffer() : null)).catch(() => null), this.entries.set(r, o), this.stats.started += 1, setTimeout(() => {
1373
1373
  this.entries.get(r) === o && (this.entries.delete(r), a.abort(), this.stats.expired += 1);
1374
- }, Dt);
1374
+ }, Ot);
1375
1375
  }
1376
1376
  take(e) {
1377
- let t = At(e), n = this.entries.get(t);
1377
+ let t = jt(e), n = this.entries.get(t);
1378
1378
  return n && this.entries.delete(t), n;
1379
1379
  }
1380
1380
  destroy() {
@@ -1394,13 +1394,13 @@ var jt = class {
1394
1394
  }
1395
1395
  }
1396
1396
  };
1397
- function Mt(t) {
1397
+ function Nt(t) {
1398
1398
  let n = e.DefaultConfig?.loader;
1399
1399
  if (typeof n != "function") return;
1400
1400
  class r extends n {
1401
1401
  served = !1;
1402
1402
  cancelled = !1;
1403
- stats = Nt();
1403
+ stats = Pt();
1404
1404
  load(e, n, r) {
1405
1405
  let i = e.rangeEnd ? void 0 : t.take(e.url);
1406
1406
  if (!i) {
@@ -1408,7 +1408,7 @@ function Mt(t) {
1408
1408
  return;
1409
1409
  }
1410
1410
  this.served = !0;
1411
- let a = performance.now(), o = n?.loadPolicy?.maxTimeToFirstByteMs || Ot, s = setTimeout(() => {
1411
+ let a = performance.now(), o = n?.loadPolicy?.maxTimeToFirstByteMs || kt, s = setTimeout(() => {
1412
1412
  !this.cancelled && this.served && (i.abort.abort(), t.stats.failed += 1, this.served = !1, super.load(e, n, r));
1413
1413
  }, o);
1414
1414
  i.bytes.then((o) => {
@@ -1435,7 +1435,7 @@ function Mt(t) {
1435
1435
  }
1436
1436
  return r;
1437
1437
  }
1438
- function Nt() {
1438
+ function Pt() {
1439
1439
  return {
1440
1440
  aborted: !1,
1441
1441
  loaded: 0,
@@ -1459,10 +1459,10 @@ function Nt() {
1459
1459
  }
1460
1460
  };
1461
1461
  }
1462
- function Pt(e) {
1462
+ function Ft(e) {
1463
1463
  return e === "audio" ? "audio" : "main";
1464
1464
  }
1465
- function Ft(e, t) {
1465
+ function It(e, t) {
1466
1466
  let n = [];
1467
1467
  t.initSegment?.url && !t.initSegment.data && n.push({
1468
1468
  url: t.initSegment.url,
@@ -1480,12 +1480,17 @@ function Ft(e, t) {
1480
1480
  return n;
1481
1481
  }
1482
1482
  //#endregion
1483
+ //#region src/core/percentile.ts
1484
+ function K(e, t) {
1485
+ return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
1486
+ }
1487
+ //#endregion
1483
1488
  //#region src/core/engine/hls.ts
1484
- function It() {
1489
+ function Lt() {
1485
1490
  return e.isSupported();
1486
1491
  }
1487
- var Lt = 1e6, Rt = 20, zt = 1e4, Bt = 3, Vt = 5, Ht = 3, Ut = 1e4, Wt = 3e4, Gt = 12e3, Kt = 3e3, qt = .02, Jt = 6e3, Yt = .5, Xt = 3, Zt = .5, Qt = 1.1, $t = .25, en = 5e3, tn = 4e3, nn = 2, rn = 5, an = 2, on = 2, sn = 4, cn = 4, ln = 2e3, un = 3e4, dn = 1, fn = 2;
1488
- function pn(e, t, n) {
1492
+ var Rt = 1e6, zt = 20, Bt = 1e4, Vt = 3, Ht = 5, Ut = 3, Wt = 1e4, Gt = 3e4, Kt = 12e3, qt = 3e3, Jt = .02, Yt = 6e3, Xt = .5, Zt = 3, Qt = .5, $t = 1.1, en = .25, tn = 5e3, nn = 4e3, rn = 2, an = 5, on = 2, sn = 2, cn = 4, ln = 4, un = 2e3, dn = 3e4, fn = 1, pn = 2;
1493
+ function mn(e, t, n) {
1489
1494
  if (n <= 0 || t < 0 || t >= e.length) return t;
1490
1495
  let r = (t, n) => e[t].width === e[n].width && e[t].height === e[n].height, i = (t) => {
1491
1496
  for (; t + 1 < e.length && r(t + 1, t);) t++;
@@ -1494,21 +1499,30 @@ function pn(e, t, n) {
1494
1499
  for (let t = 0; t < n && a + 1 < e.length; t++) a = i(a + 1);
1495
1500
  return a;
1496
1501
  }
1497
- var mn = .2, hn = 30, gn = 2, _n = 2, vn = 2, yn = 9, bn = .5, xn = 64;
1498
- function Sn(e) {
1502
+ var hn = .2, gn = 30, _n = 2, vn = 2, yn = 2, bn = 9, xn = .5, Sn = 64, Cn = 1e4, wn = 8, Tn = .6, En = .4, Dn = .4, On = 2, kn = 6e4, q = .5, An = 3.5, jn = 300;
1503
+ function Mn() {
1504
+ if (typeof navigator > "u") return 0;
1505
+ try {
1506
+ let e = navigator.connection?.rtt;
1507
+ return typeof e == "number" && Number.isFinite(e) && e > 0 ? e : 0;
1508
+ } catch {
1509
+ return 0;
1510
+ }
1511
+ }
1512
+ function Nn(e) {
1499
1513
  if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
1500
1514
  delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
1501
1515
  return;
1502
1516
  }
1503
- e.liveSyncDuration ??= vn;
1504
- let t = Math.max(12, e.liveSyncDuration + yn);
1505
- (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Cn(e, e.liveSyncDuration);
1517
+ e.liveSyncDuration ??= yn;
1518
+ let t = Math.max(12, e.liveSyncDuration + bn);
1519
+ (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Pn(e, e.liveSyncDuration);
1506
1520
  }
1507
- function Cn(e, t) {
1521
+ function Pn(e, t) {
1508
1522
  let n = (e, t, n) => Math.min(n, Math.max(t, e));
1509
1523
  e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
1510
1524
  }
1511
- var wn = class {
1525
+ var Fn = class {
1512
1526
  name = "hls";
1513
1527
  hls = null;
1514
1528
  deps;
@@ -1572,6 +1586,15 @@ var wn = class {
1572
1586
  onTimeUpdate = null;
1573
1587
  chaseTicks = 0;
1574
1588
  liveRetargets = 0;
1589
+ holdBackEnabled;
1590
+ holdBackSamples = [];
1591
+ holdBackBase = -1;
1592
+ holdBackStepCount = 0;
1593
+ holdBackApplied = 0;
1594
+ holdBackSlowTicks = 0;
1595
+ holdBackFastSince = 0;
1596
+ holdBackSteps = 0;
1597
+ holdBackPriorChecked = !1;
1575
1598
  capObserver = null;
1576
1599
  capTimer = null;
1577
1600
  sizeCapLevel = null;
@@ -1593,11 +1616,11 @@ var wn = class {
1593
1616
  frameCounter;
1594
1617
  detachFrames = null;
1595
1618
  constructor(e) {
1596
- this.deps = e, this.frameCounter = new lt(e.video), this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl, this.loadStarted = e.deferLoad !== !0;
1619
+ this.deps = e, this.holdBackEnabled = e.adaptiveHoldBack !== !1, this.frameCounter = new ut(e.video), this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl, this.loadStarted = e.deferLoad !== !0;
1597
1620
  }
1598
1621
  async load() {
1599
1622
  this.detachFrames ??= this.frameCounter.attach();
1600
- let { descriptor: e, snapToLiveOnRefocus: t, video: r, emit: i } = this.deps, a = H(r) && U(), o = !!e.drm?.widevine?.licenseUrl && !a;
1623
+ let { descriptor: e, snapToLiveOnRefocus: t, video: r, emit: i } = this.deps, a = V(r) && H(), o = !!e.drm?.widevine?.licenseUrl && !a;
1601
1624
  if (e.drmEnabled && !o) {
1602
1625
  i({
1603
1626
  type: "error",
@@ -1629,7 +1652,7 @@ var wn = class {
1629
1652
  }
1630
1653
  this.loadSuspended = !0;
1631
1654
  }
1632
- }, en));
1655
+ }, tn));
1633
1656
  }
1634
1657
  resumeLoading() {
1635
1658
  if (this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.resetChaseWindow(), this.resetFrameDropWindow(), this.loadSuspended && (this.loadSuspended = !1, !this.stopped && this.hls)) {
@@ -1661,7 +1684,7 @@ var wn = class {
1661
1684
  let t = e.liveSyncPosition;
1662
1685
  if (typeof t != "number" || !Number.isFinite(t)) return;
1663
1686
  let { video: n } = this.deps;
1664
- n.currentTime <= t + bn || (n.currentTime = t);
1687
+ n.currentTime <= t + xn || (n.currentTime = t);
1665
1688
  }
1666
1689
  reanchorPartTimeline(e, t) {
1667
1690
  let { frag: n, part: r } = t;
@@ -1673,7 +1696,7 @@ var wn = class {
1673
1696
  let o = n.sn, s = i.fragments.find((e) => e && e.sn === o) ?? (i.fragmentHint?.sn === o ? i.fragmentHint : void 0);
1674
1697
  if (!s) return;
1675
1698
  let c = a.startPTS - s.start;
1676
- if (!(!Number.isFinite(c) || Math.abs(c) < qt)) {
1699
+ if (!(!Number.isFinite(c) || Math.abs(c) < Jt)) {
1677
1700
  for (let e of i.fragments) e && e.setStart(e.start + c);
1678
1701
  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})`);
1679
1702
  }
@@ -1681,7 +1704,7 @@ var wn = class {
1681
1704
  startHls() {
1682
1705
  let { video: t, descriptor: n, credential: r, deviceId: i, userId: a, lowLatency: o, debug: s, hlsConfig: c, emit: l } = this.deps;
1683
1706
  this.clearKick(), this.resetStarvationWindow(), this.resetChaseWindow(), this.resetFrameDropWindow(), this.lastFragLoadingMs = 0, this.lastEndSN = -1, this.lastAdvanceMs = 0;
1684
- let u = n.drmEnabled && !!(n.drm?.widevine?.licenseUrl || n.drm?.fairplay?.licenseUrl), d = H(this.deps.video) && U(), f = {
1707
+ let u = n.drmEnabled && !!(n.drm?.widevine?.licenseUrl || n.drm?.fairplay?.licenseUrl), d = V(this.deps.video) && H(), f = {
1685
1708
  debug: s === !0,
1686
1709
  enableWorker: !o,
1687
1710
  lowLatencyMode: o,
@@ -1708,12 +1731,12 @@ var wn = class {
1708
1731
  this.licenseTimeMs === 0 && (this.licenseTimeMs = Math.max(1, Math.round(performance.now() - o)));
1709
1732
  });
1710
1733
  }, { freshLicenseUrl: c } = this.deps;
1711
- if (!c || !K(this.licenseUrl)) {
1734
+ if (!c || !G(this.licenseUrl)) {
1712
1735
  s();
1713
1736
  return;
1714
1737
  }
1715
1738
  let l = this.hls, u, d = new Promise((e) => {
1716
- u = setTimeout(() => e(void 0), Ut);
1739
+ u = setTimeout(() => e(void 0), Wt);
1717
1740
  });
1718
1741
  return Promise.race([c().catch(() => void 0), d]).finally(() => clearTimeout(u)).then((e) => {
1719
1742
  if (!this.hls || this.hls !== l) throw Error("havik-player: engine torn down during refresh");
@@ -1729,20 +1752,20 @@ var wn = class {
1729
1752
  ...c
1730
1753
  };
1731
1754
  if (this.prefetch?.destroy(), this.prefetch = null, u && this.deps.mediaDuringKey !== !1 && !f.fLoader) {
1732
- let e = new jt({
1755
+ let e = new Mt({
1733
1756
  sessionId: n.analytics?.sid,
1734
1757
  contentId: n.matchUrn
1735
- }), t = Mt(e);
1758
+ }), t = Nt(e);
1736
1759
  t && (this.prefetch = e, f.fLoader = t);
1737
1760
  }
1738
- Sn(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;
1761
+ Nn(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;
1739
1762
  let p = new e(f);
1740
- if (this.hls = p, p.on(e.Events.FRAG_PARSED, (e, t) => this.reanchorPartTimeline(p, t)), p.on(e.Events.MANIFEST_PARSED, () => {
1763
+ if (this.hls = p, this.armHoldBack(p, f), p.on(e.Events.FRAG_PARSED, (e, t) => this.reanchorPartTimeline(p, t)), p.on(e.Events.MANIFEST_PARSED, () => {
1741
1764
  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" });
1742
1765
  }), p.on(e.Events.FRAG_BUFFERED, (e, t) => {
1743
1766
  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(() => {
1744
1767
  this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.starvationRebuilds = 0, this.recoveryResetTimer = null;
1745
- }, Wt)));
1768
+ }, Gt)));
1746
1769
  }), p.on(e.Events.FRAG_LOADING, (e, t) => {
1747
1770
  t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
1748
1771
  }), p.on(e.Events.FRAG_LOADED, (e, t) => {
@@ -1750,10 +1773,10 @@ var wn = class {
1750
1773
  let n = t?.frag?.stats, r = n?.loaded;
1751
1774
  typeof r == "number" && (this.bytesLoaded += r), !this.reportedFirstBytes && typeof r == "number" && r > 0 && (this.reportedFirstBytes = !0, l({ type: "firstBytes" }));
1752
1775
  let i = n?.loading;
1753
- 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 > xn && this.requestTimesMs.shift());
1776
+ 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 > Sn && this.requestTimesMs.shift()), this.sampleHoldBack(t);
1754
1777
  let a = t?.frag?.url;
1755
1778
  if (a) {
1756
- let e = ot(a);
1779
+ let e = st(a);
1757
1780
  e && (this.cdnHost = e);
1758
1781
  }
1759
1782
  }), p.on(e.Events.LEVEL_LOADED, (e, t) => {
@@ -1767,15 +1790,15 @@ var wn = class {
1767
1790
  }), p.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.prefetch) {
1768
1791
  let t = this.prefetch;
1769
1792
  p.on(e.Events.KEY_LOADING, (e, n) => {
1770
- let r = n?.frag, i = Pt(r?.type);
1771
- if (r && t.isActive(i)) for (let e of Ft(p, r)) t.prefetch(e.url, i, e.kind);
1772
- }), p.on(e.Events.KEY_LOADED, (e, n) => t.keyUsable(Pt(n?.frag?.type)));
1793
+ let r = n?.frag, i = Ft(r?.type);
1794
+ if (r && t.isActive(i)) for (let e of It(p, r)) t.prefetch(e.url, i, e.kind);
1795
+ }), p.on(e.Events.KEY_LOADED, (e, n) => t.keyUsable(Ft(n?.frag?.type)));
1773
1796
  }
1774
1797
  p.on(e.Events.KEY_LOADED, () => {
1775
1798
  u && !this.reportedDrmReady && (this.reportedDrmReady = !0, l({ type: "drmReady" }));
1776
1799
  }), this.endWatchdog && clearInterval(this.endWatchdog), this.endWatchdog = setInterval(() => {
1777
- this.checkEndStale(), this.checkStarvation(), this.updateCatchUpBand(), this.checkFutileChase(), this.checkFrameDrops();
1778
- }, Kt), p.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1800
+ this.checkEndStale(), this.checkStarvation(), this.updateCatchUpBand(), this.checkFutileChase(), this.adaptHoldBack(), this.checkFrameDrops();
1801
+ }, qt), p.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1779
1802
  this.renditionSwitches += 1;
1780
1803
  let n = this.lastLevelIndex >= 0 ? p.levels[this.lastLevelIndex] : void 0, r = p.levels[t.level];
1781
1804
  n && r && r.bitrate !== n.bitrate && (r.bitrate > n.bitrate ? this.upshifts += 1 : this.downshifts += 1), this.lastLevelIndex = t.level, this.armFrameDropGrace(), l({
@@ -1826,9 +1849,9 @@ var wn = class {
1826
1849
  let o = e.config ?? {}, s = typeof self < "u" && self.devicePixelRatio > 0 ? self.devicePixelRatio : 1, c = o.ignoreDevicePixelRatio ? 1 : s;
1827
1850
  c = Math.min(c, o.maxDevicePixelRatio ?? Infinity);
1828
1851
  let l = t.getMaxLevelByMediaSize(e.levels, i * c, a * c), u = this.deps.sizeCapHeadroom;
1829
- if (u == null && s >= fn) return l;
1830
- let d = u ?? dn, f = Number.isNaN(d) ? dn : d === Infinity ? e.levels.length : Math.max(0, Math.floor(d));
1831
- return pn(e.levels, l, f);
1852
+ if (u == null && s >= pn) return l;
1853
+ let d = u ?? fn, f = Number.isNaN(d) ? fn : d === Infinity ? e.levels.length : Math.max(0, Math.floor(d));
1854
+ return mn(e.levels, l, f);
1832
1855
  }
1833
1856
  onMediaResize() {
1834
1857
  if (!this.sizeCapManaged) return;
@@ -1843,7 +1866,7 @@ var wn = class {
1843
1866
  return;
1844
1867
  }
1845
1868
  this.capTimer && clearTimeout(this.capTimer);
1846
- let t = e > this.sizeCapLevel ? ln : un;
1869
+ let t = e > this.sizeCapLevel ? un : dn;
1847
1870
  this.capTimer = setTimeout(() => {
1848
1871
  this.capTimer = null, this.computeSizeCapTarget() === e ? this.applySizeCap() : this.onMediaResize();
1849
1872
  }, t);
@@ -1925,6 +1948,7 @@ var wn = class {
1925
1948
  playlistBytesLoaded: this.playlistBytesLoaded,
1926
1949
  renditionSwitches: this.renditionSwitches,
1927
1950
  liveRetargets: this.liveRetargets,
1951
+ holdBackSteps: this.holdBackSteps,
1928
1952
  fpsDownCaps: this.fpsDownCaps,
1929
1953
  licenseTimeMs: this.licenseTimeMs,
1930
1954
  upshifts: this.upshifts,
@@ -1953,9 +1977,10 @@ var wn = class {
1953
1977
  let t = e.details;
1954
1978
  if (t) {
1955
1979
  if (t.live === !0) {
1956
- this.sawLive = !0;
1957
- let e = t.endSN ?? -1;
1958
- e !== this.lastEndSN && (this.lastEndSN = e, this.lastAdvanceMs = Date.now(), this.chaseSuspended && this.resumeChasing());
1980
+ let e = !this.sawLive;
1981
+ this.sawLive = !0, e && this.applyHoldBackPrior();
1982
+ let n = t.endSN ?? -1;
1983
+ n !== this.lastEndSN && (this.lastEndSN = n, this.lastAdvanceMs = Date.now(), this.chaseSuspended && this.resumeChasing());
1959
1984
  return;
1960
1985
  }
1961
1986
  this.sawLive && this.emitEnded("endlist");
@@ -1972,7 +1997,7 @@ var wn = class {
1972
1997
  return;
1973
1998
  }
1974
1999
  let e = Date.now() - this.lastAdvanceMs;
1975
- !this.chaseSuspended && e > Jt && this.suspendChasing(), e > Gt && this.emitEnded("stalled");
2000
+ !this.chaseSuspended && e > Yt && this.suspendChasing(), e > Kt && this.emitEnded("stalled");
1976
2001
  }
1977
2002
  resetStarvationWindow() {
1978
2003
  this.starvedTicks = 0, this.deadTicks = 0, this.mainFragsSinceTick = 0, this.recentMainFrags = [];
@@ -1987,7 +2012,7 @@ var wn = class {
1987
2012
  this.resetStarvationWindow();
1988
2013
  return;
1989
2014
  }
1990
- let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < Jt, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > tn, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = B(t), c = o - s, l = c !== this.starvedFrames;
2015
+ let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < Yt, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > nn, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = z(t), c = o - s, l = c !== this.starvedFrames;
1991
2016
  if (this.starvedFrames = c, !a || l) {
1992
2017
  (t.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA || l) && this.clearKick(), this.resetStarvationWindow();
1993
2018
  return;
@@ -1996,12 +2021,12 @@ var wn = class {
1996
2021
  this.resetStarvationWindow();
1997
2022
  return;
1998
2023
  }
1999
- 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 > sn && this.recentMainFrags.shift(), this.clearRecoveryReset(), this.kickPending && this.kickTicks >= an && (this.clearKick(), this.starvationRebuilds < on)) {
2024
+ 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 > cn && this.recentMainFrags.shift(), this.clearRecoveryReset(), this.kickPending && this.kickTicks >= on && (this.clearKick(), this.starvationRebuilds < sn)) {
2000
2025
  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");
2001
2026
  return;
2002
2027
  }
2003
- let u = i && this.starvedTicks >= nn, d = !i && this.deadTicks >= sn && this.recentMainFrags.reduce((e, t) => e + t, 0) >= cn;
2004
- if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >= rn))) {
2028
+ let u = i && this.starvedTicks >= rn, d = !i && this.deadTicks >= cn && this.recentMainFrags.reduce((e, t) => e + t, 0) >= ln;
2029
+ if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >= an))) {
2005
2030
  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");
2006
2031
  try {
2007
2032
  e.stopLoad(), e.startLoad(-1);
@@ -2016,7 +2041,7 @@ var wn = class {
2016
2041
  let t = e.latency, n = e.targetLatency;
2017
2042
  if (typeof t != "number" || typeof n != "number" || !Number.isFinite(t)) return;
2018
2043
  let r = t - n;
2019
- !this.catchUpEngaged && r > Yt ? this.catchUpEngaged = !0 : this.catchUpEngaged && r <= Yt && this.deps.video.playbackRate === 1 && (this.catchUpEngaged = !1);
2044
+ !this.catchUpEngaged && r > Xt ? this.catchUpEngaged = !0 : this.catchUpEngaged && r <= Xt && this.deps.video.playbackRate === 1 && (this.catchUpEngaged = !1);
2020
2045
  let i = this.catchUpEngaged ? this.catchUpRate : 1;
2021
2046
  e.config.maxLiveSyncPlaybackRate !== i && (e.config.maxLiveSyncPlaybackRate = i);
2022
2047
  }
@@ -2029,7 +2054,7 @@ var wn = class {
2029
2054
  }), n < 0 ? 0 : n;
2030
2055
  }
2031
2056
  armFrameDropGrace() {
2032
- this.fpsGraceTicks > 0 || (this.fpsGraceTicks = _n, this.fpsBadTicks = 0);
2057
+ this.fpsGraceTicks > 0 || (this.fpsGraceTicks = vn, this.fpsBadTicks = 0);
2033
2058
  }
2034
2059
  resetFrameDropWindow() {
2035
2060
  this.fpsLastFrames = -1, this.fpsLastDropped = -1, this.fpsBadTicks = 0;
@@ -2040,8 +2065,8 @@ var wn = class {
2040
2065
  this.resetFrameDropWindow();
2041
2066
  return;
2042
2067
  }
2043
- let { totalFrames: n, dropped: r } = B(t), i = this.fpsLastFrames < 0, a = n - this.fpsLastFrames, o = r - this.fpsLastDropped;
2044
- if (this.fpsLastFrames = n, this.fpsLastDropped = r, i || a < hn || o < 0) {
2068
+ let { totalFrames: n, dropped: r } = z(t), i = this.fpsLastFrames < 0, a = n - this.fpsLastFrames, o = r - this.fpsLastDropped;
2069
+ if (this.fpsLastFrames = n, this.fpsLastDropped = r, i || a < gn || o < 0) {
2045
2070
  this.fpsBadTicks = 0;
2046
2071
  return;
2047
2072
  }
@@ -2049,11 +2074,11 @@ var wn = class {
2049
2074
  --this.fpsGraceTicks, this.fpsBadTicks = 0;
2050
2075
  return;
2051
2076
  }
2052
- if (o <= mn * a) {
2077
+ if (o <= hn * a) {
2053
2078
  this.fpsBadTicks = 0;
2054
2079
  return;
2055
2080
  }
2056
- if (this.fpsBadTicks += 1, this.fpsBadTicks < gn) return;
2081
+ if (this.fpsBadTicks += 1, this.fpsBadTicks < _n) return;
2057
2082
  this.fpsBadTicks = 0;
2058
2083
  let s = e.levels, c = s[e.currentLevel];
2059
2084
  if (!c) return;
@@ -2070,20 +2095,113 @@ var wn = class {
2070
2095
  return;
2071
2096
  }
2072
2097
  let n = e.latency, r = t.playbackRate, i = e.config.maxLiveSyncPlaybackRate;
2073
- if (!(!t.paused && typeof i == "number" && i > 1 && r >= Math.min(Qt, i) && r <= i && typeof n == "number" && Number.isFinite(n))) {
2098
+ if (!(!t.paused && typeof i == "number" && i > 1 && r >= Math.min($t, i) && r <= i && typeof n == "number" && Number.isFinite(n))) {
2074
2099
  this.resetChaseWindow();
2075
2100
  return;
2076
2101
  }
2077
- if (this.chaseLatency < 0 || this.chaseLatency - n >= Zt) {
2102
+ if (this.chaseLatency < 0 || this.chaseLatency - n >= Qt) {
2078
2103
  this.chaseLatency = n, this.chaseTicks = 0;
2079
2104
  return;
2080
2105
  }
2081
- if (this.chaseTicks += 1, this.chaseTicks < Xt) return;
2106
+ if (this.chaseTicks += 1, this.chaseTicks < Zt) return;
2082
2107
  this.resetChaseWindow();
2083
2108
  let a = typeof e.config.liveMaxLatencyDuration == "number" ? e.config.liveMaxLatencyDuration : e.maxLatency;
2084
2109
  if (!(a > 1)) return;
2085
- let o = typeof e.targetLatency == "number" ? e.targetLatency : 0, s = Math.min(n, a - 1);
2086
- s < o + $t || (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);
2110
+ let o = e.targetLatency;
2111
+ if (typeof o != "number" || !(o > 0)) return;
2112
+ let s = Math.min(n, a - 1);
2113
+ s < o + en || (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, this.deps.emit({
2114
+ type: "livetargetchange",
2115
+ change: {
2116
+ targetSeconds: s,
2117
+ previousSeconds: o,
2118
+ reason: "futile-chase"
2119
+ }
2120
+ }));
2121
+ }
2122
+ armHoldBack(e, t) {
2123
+ this.holdBackEnabled && (this.holdBackSlowTicks = 0, this.holdBackFastSince = 0, this.holdBackSamples = [], this.holdBackBase < 0 && typeof t.liveSyncDuration == "number" && (this.holdBackBase = t.liveSyncDuration), this.holdBackApplied > 0 && (e.targetLatency = this.holdBackApplied));
2124
+ }
2125
+ applyHoldBackPrior() {
2126
+ let e = this.hls;
2127
+ if (!this.holdBackEnabled || this.holdBackPriorChecked || !e || (this.holdBackPriorChecked = !0, this.holdBackApplied > 0 || this.holdBackStepCount > 0)) return;
2128
+ let t = e.targetLatency;
2129
+ if (this.holdBackBase < 0 && typeof t == "number" && t > 0 && (this.holdBackBase = t), this.holdBackBase < 0) return;
2130
+ let n = Mn();
2131
+ if (!(n >= jn)) return;
2132
+ let r = typeof t == "number" && t > 0 ? t : this.holdBackBase, i = Math.min(this.holdBackCeiling(e), this.holdBackBase + q);
2133
+ i > r && (this.holdBackStepCount = 1, this.moveHoldBack(e, r, i, "connection-rtt", `connection rtt ${n} ms`));
2134
+ }
2135
+ holdBackCeiling(e) {
2136
+ let t = this.deps.adaptiveHoldBackCeiling, n = typeof t == "number" && Number.isFinite(t) ? t : An, r = typeof e.config.liveMaxLatencyDuration == "number" ? e.config.liveMaxLatencyDuration : e.maxLatency;
2137
+ return r > 1 ? Math.min(n, r - 1) : n;
2138
+ }
2139
+ sampleHoldBack(e) {
2140
+ if (!this.holdBackEnabled || typeof document < "u" && document.hidden) return;
2141
+ let t = e.frag?.type;
2142
+ if (t !== "main" && t !== "audio") return;
2143
+ let n = e.part ? e.part.duration : this.detectedLowLatency ? 0 : e.frag?.duration;
2144
+ if (typeof n != "number" || !(n > 0)) return;
2145
+ let r = (e.part ?? e.frag)?.stats?.loading;
2146
+ if (!r || !(r.start > 0) || !(r.end > 0)) return;
2147
+ let i = r.first > r.start ? r.first : r.end, a = Math.max(0, i - r.start) / (n * 1e3);
2148
+ this.holdBackSamples.push({
2149
+ t: Date.now(),
2150
+ ratio: a
2151
+ }), this.holdBackSamples.length > 256 && this.holdBackSamples.shift();
2152
+ }
2153
+ adaptHoldBack() {
2154
+ if (!this.holdBackEnabled) return;
2155
+ let e = this.hls;
2156
+ if (!e || this.endedEmitted || !this.sawLive || this.chaseSuspended || this.loadSuspended || typeof document < "u" && document.hidden) {
2157
+ this.holdBackSlowTicks = 0, this.holdBackFastSince = 0, this.holdBackSamples = [];
2158
+ return;
2159
+ }
2160
+ let t = Date.now(), n = t - Cn, r = this.holdBackSamples, i = 0;
2161
+ for (; i < r.length && r[i].t < n;) i += 1;
2162
+ i > 0 && r.splice(0, i);
2163
+ let a = e.targetLatency;
2164
+ if (typeof a != "number" || !(a > 0)) return;
2165
+ this.holdBackBase < 0 && (this.holdBackBase = a);
2166
+ let o = this.holdBackBase, s = this.holdBackCeiling(e);
2167
+ if (r.length < wn) {
2168
+ this.holdBackSlowTicks = 0;
2169
+ return;
2170
+ }
2171
+ let c = r.map((e) => e.ratio).sort((e, t) => e - t), l = K(c, 95), u = K(c, 50), d = Math.round(l * 1e3);
2172
+ if (l >= Tn && u >= En) {
2173
+ if (this.holdBackFastSince = 0, this.holdBackSlowTicks += 1, this.holdBackSlowTicks < On) return;
2174
+ this.holdBackSlowTicks = 0;
2175
+ let t = Math.ceil((s - o) / q), n = Math.ceil((a - o) / q - 1e-9);
2176
+ if (n > this.holdBackStepCount && (this.holdBackStepCount = n), this.holdBackStepCount >= t) return;
2177
+ this.holdBackStepCount += 1;
2178
+ let r = Math.min(s, o + this.holdBackStepCount * q);
2179
+ if (r <= a) return;
2180
+ this.moveHoldBack(e, a, r, "slow-fetches", `part fetches slow (first byte p50 ${Math.round(u * 1e3)} / p95 ${d} ms per 1 s of media)`);
2181
+ return;
2182
+ }
2183
+ if (this.holdBackSlowTicks = 0, l >= Dn) {
2184
+ this.holdBackFastSince = 0;
2185
+ return;
2186
+ }
2187
+ if (this.holdBackFastSince === 0) {
2188
+ this.holdBackFastSince = t;
2189
+ return;
2190
+ }
2191
+ if (t - this.holdBackFastSince < kn || (this.holdBackFastSince = t, this.holdBackStepCount === 0 || a !== this.holdBackApplied)) return;
2192
+ --this.holdBackStepCount;
2193
+ let f = Math.max(o, o + this.holdBackStepCount * q);
2194
+ f >= a || this.moveHoldBack(e, a, f, "fast-fetches", `part fetches fast again (p95 first byte ${d} ms per 1 s of media)`);
2195
+ }
2196
+ moveHoldBack(e, t, n, r, i) {
2197
+ console.info(`havik-player: ${i}; live target ${t.toFixed(1)}s → ${n.toFixed(1)}s`), this.holdBackApplied = n, this.holdBackSteps += 1, e.targetLatency = n, this.deps.emit({
2198
+ type: "livetargetchange",
2199
+ change: {
2200
+ targetSeconds: n,
2201
+ previousSeconds: t,
2202
+ reason: r
2203
+ }
2204
+ });
2087
2205
  }
2088
2206
  suspendChasing() {
2089
2207
  let e = this.hls;
@@ -2113,12 +2231,12 @@ var wn = class {
2113
2231
  } catch {}
2114
2232
  }
2115
2233
  startEstimate() {
2116
- return this.deps.rememberBandwidth === !1 ? Lt : Ie() ?? Lt;
2234
+ return this.deps.rememberBandwidth === !1 ? Rt : Ie() ?? Rt;
2117
2235
  }
2118
2236
  maybeRememberBandwidth(e) {
2119
- if (this.deps.rememberBandwidth === !1 || this.bwFragsLoaded < Rt) return;
2237
+ if (this.deps.rememberBandwidth === !1 || this.bwFragsLoaded < zt) return;
2120
2238
  let t = Date.now();
2121
- if (!e && t - this.bwSavedAt < zt) return;
2239
+ if (!e && t - this.bwSavedAt < Bt) return;
2122
2240
  let n = this.hls?.bandwidthEstimate;
2123
2241
  typeof n != "number" || !Number.isFinite(n) || n <= 0 || (this.bwSavedAt = t, Le(n));
2124
2242
  }
@@ -2138,7 +2256,7 @@ var wn = class {
2138
2256
  scheduleLicenseRetry(t) {
2139
2257
  if (t.details !== e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED && t.details !== e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED) return !1;
2140
2258
  let n = t.response?.code ?? 0;
2141
- if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Ht) return !1;
2259
+ if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Ut) return !1;
2142
2260
  this.licenseRecoveries += 1;
2143
2261
  let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
2144
2262
  return this.licenseRetryTimer = setTimeout(() => {
@@ -2170,7 +2288,7 @@ var wn = class {
2170
2288
  return;
2171
2289
  }
2172
2290
  if (t.type === e.ErrorTypes.NETWORK_ERROR) {
2173
- if (this.networkRecoveries < Vt) {
2291
+ if (this.networkRecoveries < Ht) {
2174
2292
  this.networkRecoveries += 1;
2175
2293
  let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
2176
2294
  this.networkRetryTimer = setTimeout(() => {
@@ -2187,7 +2305,7 @@ var wn = class {
2187
2305
  return;
2188
2306
  }
2189
2307
  if (t.type === e.ErrorTypes.MEDIA_ERROR) {
2190
- if (this.mediaRecoveries < Bt) {
2308
+ if (this.mediaRecoveries < Vt) {
2191
2309
  this.mediaRecoveries += 1, n.recoverMediaError();
2192
2310
  return;
2193
2311
  }
@@ -2208,22 +2326,18 @@ var wn = class {
2208
2326
  }
2209
2327
  }
2210
2328
  toError(t, r = "PLAYBACK") {
2211
- let o = t.response?.code ?? 0, s = t.details ?? t.type, c = t.reason ?? t.error?.message ?? "", l = r === "DRM" && o === 403 && t.details === e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED ? i(Et(t.networkDetails)) : void 0;
2329
+ let o = t.response?.code ?? 0, s = t.details ?? t.type, c = t.reason ?? t.error?.message ?? "", l = r === "DRM" && o === 403 && t.details === e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED ? i(Dt(t.networkDetails)) : void 0;
2212
2330
  return new n(r === "DRM" ? a(o, l) : o === 401 ? "UNAUTHORIZED" : o === 403 ? "FORBIDDEN" : "INTERNAL", o, `${r} error: ${s}${c ? ` (${c})` : ""}`, l ? { serverCode: l } : void 0);
2213
2331
  }
2214
- }, Tn = 15e3, En = 6e5, Dn = 3e5, On = 100, q = 1, kn = 1e3, An = 3e4, jn = 1e3, Mn = {
2332
+ }, In = 15e3, Ln = 6e5, Rn = 3e5, zn = 100, J = 1, Bn = 1e3, Vn = 3e4, Hn = 1e3, Un = {
2215
2333
  inline: "PRESENTATION_INLINE",
2216
2334
  fullscreen: "PRESENTATION_FULLSCREEN",
2217
2335
  pip: "PRESENTATION_PIP"
2218
- }, Nn = {
2336
+ }, Wn = {
2219
2337
  widevine: "KEY_SYSTEM_WIDEVINE",
2220
2338
  fairplay: "KEY_SYSTEM_FAIRPLAY",
2221
2339
  none: "KEY_SYSTEM_NONE"
2222
- };
2223
- function Pn(e, t) {
2224
- return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
2225
- }
2226
- var Fn = /* @__PURE__ */ new Set([
2340
+ }, Gn = /* @__PURE__ */ new Set([
2227
2341
  "INVALID_URN",
2228
2342
  "NOT_FOUND",
2229
2343
  "GEO_DENIED",
@@ -2234,7 +2348,7 @@ var Fn = /* @__PURE__ */ new Set([
2234
2348
  "FORBIDDEN",
2235
2349
  "RATE_LIMITED",
2236
2350
  "TIMEOUT"
2237
- ]), J = {
2351
+ ]), Y = {
2238
2352
  idle: "PLAYER_STATE_IDLE",
2239
2353
  waiting: "PLAYER_STATE_WAITING",
2240
2354
  loading: "PLAYER_STATE_LOADING",
@@ -2244,7 +2358,7 @@ var Fn = /* @__PURE__ */ new Set([
2244
2358
  ended: "PLAYER_STATE_ENDED",
2245
2359
  error: "PLAYER_STATE_ERROR"
2246
2360
  };
2247
- function In(e) {
2361
+ function Kn(e) {
2248
2362
  try {
2249
2363
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
2250
2364
  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);
@@ -2252,8 +2366,8 @@ function In(e) {
2252
2366
  return;
2253
2367
  }
2254
2368
  }
2255
- var Ln = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), Rn = /* @__PURE__ */ new Set(), Y = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), zn = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
2256
- function Bn(e) {
2369
+ var qn = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), Jn = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), Z = /* @__PURE__ */ new Set(), Yn = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
2370
+ function Xn(e) {
2257
2371
  if (e.disposition !== "enforce" || e.effectiveDirective !== "connect-src") return;
2258
2372
  let t;
2259
2373
  try {
@@ -2262,19 +2376,19 @@ function Bn(e) {
2262
2376
  } catch {
2263
2377
  return;
2264
2378
  }
2265
- if (Rn.has(t) && !Y.has(t)) {
2266
- Y.add(t), _(zn(new URL(t).origin));
2267
- for (let e of X) e.noteCspBlocked();
2379
+ if (Jn.has(t) && !X.has(t)) {
2380
+ X.add(t), _(Yn(new URL(t).origin));
2381
+ for (let e of Z) e.noteCspBlocked();
2268
2382
  }
2269
2383
  }
2270
- function Z(e, t) {
2384
+ function Zn(e, t) {
2271
2385
  return _(t, e), { disabledReason: t };
2272
2386
  }
2273
- function Vn(e, t, n) {
2274
- if (n) return t && !Ln(t, n) && _(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
2387
+ function Qn(e, t, n) {
2388
+ if (n) return t && !qn(t, n) && _(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
2275
2389
  if (t) return { endpoint: t };
2276
- 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).");
2277
- let r = In(e);
2390
+ if (t !== void 0) return Zn("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).");
2391
+ let r = Kn(e);
2278
2392
  if (r) return { endpoint: r };
2279
2393
  let i;
2280
2394
  try {
@@ -2282,9 +2396,9 @@ function Vn(e, t, n) {
2282
2396
  } catch {
2283
2397
  i = e;
2284
2398
  }
2285
- 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).`);
2399
+ return Zn(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).`);
2286
2400
  }
2287
- var Hn = class {
2401
+ var $n = class {
2288
2402
  opts;
2289
2403
  url;
2290
2404
  cspKey;
@@ -2366,8 +2480,8 @@ var Hn = class {
2366
2480
  constructor(e) {
2367
2481
  this.opts = {
2368
2482
  ...e,
2369
- heartbeatMs: e.heartbeatMs ?? Tn,
2370
- idleMs: e.idleMs ?? En
2483
+ heartbeatMs: e.heartbeatMs ?? In,
2484
+ idleMs: e.idleMs ?? Ln
2371
2485
  }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`;
2372
2486
  try {
2373
2487
  let e = new URL(this.url);
@@ -2384,11 +2498,11 @@ var Hn = class {
2384
2498
  sdkVersion: this.opts.sdkVersion,
2385
2499
  ...this.opts.environment
2386
2500
  };
2387
- this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), kn), 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 && Rn.add(this.cspKey), document.addEventListener("securitypolicyviolation", Bn), X.add(this), this.noteCspBlocked(), this.onVisibility());
2501
+ this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), Bn), 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 && Jn.add(this.cspKey), document.addEventListener("securitypolicyviolation", Xn), Z.add(this), this.noteCspBlocked(), this.onVisibility());
2388
2502
  }
2389
2503
  checkSuspension(e) {
2390
2504
  if (this.endedFlag || !this.opts.idleMs) return !1;
2391
- let t = Math.max(this.opts.idleMs, Dn);
2505
+ let t = Math.max(this.opts.idleMs, Rn);
2392
2506
  return this.lastTickMs > 0 && e - this.lastTickMs >= t ? (this.endIdle(this.lastTickMs), !0) : (this.lastTickMs = e, !1);
2393
2507
  }
2394
2508
  syncIdle(e) {
@@ -2402,21 +2516,21 @@ var Hn = class {
2402
2516
  noteStateChange(e, t, n, r) {
2403
2517
  if (this.endedFlag || e === t) return;
2404
2518
  let i = this.now();
2405
- 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") {
2519
+ 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") {
2406
2520
  this.stallIsSeekInduced = this.seekInduced(i);
2407
2521
  let e = !this.isHidden && !this.stallIsSeekInduced;
2408
- e && (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, e && this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(q, Math.round(this.playingTotalMs)));
2522
+ e && (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, e && this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(J, Math.round(this.playingTotalMs)));
2409
2523
  } else e === "buffering" && (this.isBuffering = !1, this.stallIsSeekInduced = !1);
2410
2524
  this.syncBuffering(), t === "paused" && (this.pauseCount += 1), this.syncIdle(t);
2411
2525
  let a = {
2412
- fromState: J[e] ?? "PLAYER_STATE_UNSPECIFIED",
2413
- toState: J[t] ?? "PLAYER_STATE_UNSPECIFIED",
2414
- presentation: Mn[this.presentation]
2526
+ fromState: Y[e] ?? "PLAYER_STATE_UNSPECIFIED",
2527
+ toState: Y[t] ?? "PLAYER_STATE_UNSPECIFIED",
2528
+ presentation: Un[this.presentation]
2415
2529
  }, o = this.causeOf(e, t, i);
2416
2530
  o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
2417
2531
  }
2418
2532
  seekInduced(e) {
2419
- return this.lastSeekMs >= 0 && e - this.lastSeekMs < jn;
2533
+ return this.lastSeekMs >= 0 && e - this.lastSeekMs < Hn;
2420
2534
  }
2421
2535
  causeOf(e, t, n) {
2422
2536
  if (t === "error") return "TRANSITION_CAUSE_ERROR";
@@ -2453,25 +2567,25 @@ var Hn = class {
2453
2567
  }
2454
2568
  this.muteChanges += 1;
2455
2569
  let r = this.now();
2456
- 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();
2570
+ 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();
2457
2571
  }
2458
2572
  notePresentation(e) {
2459
2573
  this.endedFlag || e === this.presentation || (this.closePresentation(), this.presentation = e);
2460
2574
  }
2461
2575
  notePlayIntent() {
2462
- 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));
2576
+ 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));
2463
2577
  }
2464
2578
  noteReady() {
2465
- this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(q, this.now() - this.startupAnchorMs));
2579
+ this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(J, this.now() - this.startupAnchorMs));
2466
2580
  }
2467
2581
  noteFirstPlaylist() {
2468
- this.firstPlaylistMs === 0 && (this.firstPlaylistMs = Math.max(q, this.now() - this.startupAnchorMs));
2582
+ this.firstPlaylistMs === 0 && (this.firstPlaylistMs = Math.max(J, this.now() - this.startupAnchorMs));
2469
2583
  }
2470
2584
  noteDrmReady() {
2471
- this.drmSetupMs === 0 && (this.drmSetupMs = Math.max(q, this.now() - this.startupAnchorMs));
2585
+ this.drmSetupMs === 0 && (this.drmSetupMs = Math.max(J, this.now() - this.startupAnchorMs));
2472
2586
  }
2473
2587
  noteFirstBytes() {
2474
- this.sawFirstBytes = !0, this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(q, this.now() - this.startupAnchorMs));
2588
+ this.sawFirstBytes = !0, this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(J, this.now() - this.startupAnchorMs));
2475
2589
  }
2476
2590
  noteSeek() {
2477
2591
  this.endedFlag || (this.seekCount += 1, this.lastSeekMs = this.now());
@@ -2490,7 +2604,7 @@ var Hn = class {
2490
2604
  let r = n ?? "";
2491
2605
  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";
2492
2606
  if (e === "DRM_CLIENT" || e === "DRM_DEVICE_REVOKED") return "ERROR_STAGE_DRM";
2493
- if (Fn.has(e)) return "ERROR_STAGE_RESOLVE";
2607
+ if (Gn.has(e)) return "ERROR_STAGE_RESOLVE";
2494
2608
  let i = t ?? "";
2495
2609
  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";
2496
2610
  }
@@ -2522,7 +2636,7 @@ var Hn = class {
2522
2636
  let l = t.droppedFrames ?? 0;
2523
2637
  l > this.lastDropped && (o.droppedFramesDelta = l - this.lastDropped), this.lastDropped = l;
2524
2638
  let u = t.renditionSwitches ?? 0;
2525
- 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";
2639
+ 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";
2526
2640
  let d = this.mutedTotalMs;
2527
2641
  this.mutedSinceMs !== null && (d += e - this.mutedSinceMs), d > this.lastHbMutedMs && (o.mutedMsDelta = Math.round(d - this.lastHbMutedMs)), this.lastHbMutedMs = d;
2528
2642
  let f = t.upshifts ?? 0;
@@ -2537,20 +2651,20 @@ var Hn = class {
2537
2651
  if (this.lastRequestCount = g, _ > 0) {
2538
2652
  o.requestCountDelta = _;
2539
2653
  let e = t.requestTimesMs ?? [], n = e.slice(Math.max(0, e.length - _)).sort((e, t) => e - t);
2540
- n.length > 0 && (o.requestTimeP50Ms = Pn(n, 50), o.requestTimeP95Ms = Pn(n, 95));
2654
+ n.length > 0 && (o.requestTimeP50Ms = K(n, 50), o.requestTimeP95Ms = K(n, 95));
2541
2655
  }
2542
2656
  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();
2543
2657
  }
2544
2658
  maybeEmitMediaInfo(e) {
2545
2659
  if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
2546
2660
  let t = {};
2547
- e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = Nn[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);
2661
+ e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = Wn[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);
2548
2662
  let n = JSON.stringify(t);
2549
2663
  n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
2550
2664
  }
2551
2665
  end(e, t) {
2552
2666
  if (this.endedFlag) return;
2553
- this.endedFlag = !0, X.delete(this);
2667
+ this.endedFlag = !0, Z.delete(this);
2554
2668
  let n = t ?? this.now();
2555
2669
  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));
2556
2670
  let r = this.safeSample();
@@ -2564,7 +2678,7 @@ var Hn = class {
2564
2678
  let c = r?.positionMs ?? 0;
2565
2679
  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));
2566
2680
  let l = e !== "END_REASON_ENDED";
2567
- 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);
2681
+ l && this.lastStallMs > 0 && n - this.lastStallMs <= Vn && (i.abandonAfterStallMs = Math.max(J, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= Vn && (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);
2568
2682
  let u = r?.decodedFrames ?? this.lastDecoded;
2569
2683
  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");
2570
2684
  }
@@ -2584,13 +2698,13 @@ var Hn = class {
2584
2698
  };
2585
2699
  }
2586
2700
  noteCspBlocked() {
2587
- this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(zn(new URL(this.url).origin)));
2701
+ this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(Yn(new URL(this.url).origin)));
2588
2702
  }
2589
2703
  cspBlocked() {
2590
- return !!this.cspKey && Y.has(this.cspKey);
2704
+ return !!this.cspKey && X.has(this.cspKey);
2591
2705
  }
2592
2706
  enqueue(e) {
2593
- if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > On;) {
2707
+ if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > zn;) {
2594
2708
  let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
2595
2709
  this.pending.splice(e >= 0 ? e : 0, 1);
2596
2710
  }
@@ -2617,7 +2731,7 @@ var Hn = class {
2617
2731
  };
2618
2732
  //#endregion
2619
2733
  //#region src/core/environment.ts
2620
- function Un() {
2734
+ function er() {
2621
2735
  if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
2622
2736
  let e;
2623
2737
  try {
@@ -2642,14 +2756,14 @@ function Un() {
2642
2756
  integration: "INTEGRATION_KIND_IFRAME"
2643
2757
  };
2644
2758
  }
2645
- function Wn() {
2759
+ function tr() {
2646
2760
  if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
2647
2761
  let e = navigator.userAgentData, t = e?.platform ?? "";
2648
2762
  if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
2649
2763
  let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
2650
2764
  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";
2651
2765
  }
2652
- function Gn() {
2766
+ function nr() {
2653
2767
  let e = typeof navigator < "u" ? navigator.connection : void 0;
2654
2768
  if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
2655
2769
  let t = {
@@ -2668,17 +2782,17 @@ function Gn() {
2668
2782
  }
2669
2783
  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;
2670
2784
  }
2671
- function Kn(e) {
2672
- let t = Un(), n = {
2673
- deviceClass: Wn(),
2785
+ function rr(e) {
2786
+ let t = er(), n = {
2787
+ deviceClass: tr(),
2674
2788
  integration: t.integration,
2675
- ...Gn()
2789
+ ...nr()
2676
2790
  };
2677
2791
  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);
2678
2792
  let r = e.getBoundingClientRect?.();
2679
2793
  return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
2680
2794
  }
2681
- async function qn() {
2795
+ async function ir() {
2682
2796
  let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
2683
2797
  if (!e?.getHighEntropyValues) return {};
2684
2798
  try {
@@ -2690,7 +2804,7 @@ async function qn() {
2690
2804
  }
2691
2805
  //#endregion
2692
2806
  //#region src/core/version.ts
2693
- var Jn = "2.22.1", Yn = {
2807
+ var ar = "2.23.1", or = {
2694
2808
  accent: "#E1B600",
2695
2809
  accentText: "#1B1C23",
2696
2810
  background: "#0e0f13",
@@ -2701,7 +2815,7 @@ var Jn = "2.22.1", Yn = {
2701
2815
  border: "rgba(255, 255, 255, 0.1)",
2702
2816
  radius: "8px",
2703
2817
  fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
2704
- }, Xn = {
2818
+ }, sr = {
2705
2819
  accent: "--havik-accent",
2706
2820
  accentText: "--havik-accent-text",
2707
2821
  background: "--havik-bg",
@@ -2713,26 +2827,26 @@ var Jn = "2.22.1", Yn = {
2713
2827
  radius: "--havik-radius",
2714
2828
  fontFamily: "--havik-font"
2715
2829
  };
2716
- function Zn(e, t) {
2830
+ function cr(e, t) {
2717
2831
  let n = {
2718
- ...Yn,
2832
+ ...or,
2719
2833
  ...t
2720
2834
  };
2721
- for (let t of Object.keys(Xn)) e.style.setProperty(Xn[t], n[t]);
2835
+ for (let t of Object.keys(sr)) e.style.setProperty(sr[t], n[t]);
2722
2836
  n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
2723
2837
  }
2724
2838
  //#endregion
2725
2839
  //#region src/ui/styles.ts
2726
- var Qn = "havik-player-styles", $n = "\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", er = /* @__PURE__ */ new WeakMap();
2727
- function tr(e = document, t) {
2728
- let n = e.querySelector(`style#${Qn}`);
2729
- if (n && (!t || er.get(n) === t)) return;
2840
+ var lr = "havik-player-styles", ur = "\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", dr = /* @__PURE__ */ new WeakMap();
2841
+ function fr(e = document, t) {
2842
+ let n = e.querySelector(`style#${lr}`);
2843
+ if (n && (!t || dr.get(n) === t)) return;
2730
2844
  let r = e.createElement("style");
2731
- r.id = Qn, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = $n, er.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
2845
+ r.id = lr, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = ur, dr.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
2732
2846
  }
2733
2847
  //#endregion
2734
2848
  //#region src/ui/icons.ts
2735
- var nr = {
2849
+ var pr = {
2736
2850
  play: "<path d=\"M8 5v14l11-7z\"/>",
2737
2851
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
2738
2852
  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\"/>",
@@ -2746,11 +2860,11 @@ var nr = {
2746
2860
  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\"/>"
2747
2861
  };
2748
2862
  function Q(e) {
2749
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${nr[e]}</svg>`;
2863
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${pr[e]}</svg>`;
2750
2864
  }
2751
2865
  //#endregion
2752
2866
  //#region src/ui/controls.ts
2753
- var rr = 3e3, ir = 250, ar = /* @__PURE__ */ new Set([
2867
+ var mr = 3e3, hr = 250, gr = /* @__PURE__ */ new Set([
2754
2868
  "GONE",
2755
2869
  "NOT_FOUND",
2756
2870
  "GEO_DENIED",
@@ -2758,15 +2872,15 @@ var rr = 3e3, ir = 250, ar = /* @__PURE__ */ new Set([
2758
2872
  "UNAUTHORIZED",
2759
2873
  "DRM_DEVICE_REVOKED"
2760
2874
  ]);
2761
- function or(e) {
2875
+ function _r(e) {
2762
2876
  let t = "This match isn't available in your country";
2763
2877
  return e ? `${t} (${e})` : t;
2764
2878
  }
2765
- var sr = "This device can't play protected video";
2766
- function cr(e) {
2767
- return new lr(e);
2879
+ var vr = "This device can't play protected video";
2880
+ function yr(e) {
2881
+ return new br(e);
2768
2882
  }
2769
- var lr = class {
2883
+ var br = class {
2770
2884
  root;
2771
2885
  video;
2772
2886
  player;
@@ -2814,7 +2928,7 @@ var lr = class {
2814
2928
  scrubbing = !1;
2815
2929
  offFns = [];
2816
2930
  constructor(e) {
2817
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? rr, 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, tr(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");
2931
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? mr, 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, fr(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");
2818
2932
  let t = (e, t, n) => {
2819
2933
  let r = this.doc.createElement(e);
2820
2934
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -2900,7 +3014,7 @@ var lr = class {
2900
3014
  let t = this.surfaceWasHidden;
2901
3015
  this.tapTimer = setTimeout(() => {
2902
3016
  this.tapTimer = void 0, this.toggleBar(t);
2903
- }, ir);
3017
+ }, hr);
2904
3018
  }
2905
3019
  toggleBar(e) {
2906
3020
  e ? this.poke() : this.hideIfIdle();
@@ -2920,7 +3034,7 @@ var lr = class {
2920
3034
  this.statusOverlays && (e === "ended" ? t = "ended" : e === "error" ? t = "error" : e === "waiting" ? t = "waiting" : !this.hasPlayed && (e === "idle" || e === "paused") && (t = "preplay")), this.renderStatus(t);
2921
3035
  }
2922
3036
  statusMessageFor(e) {
2923
- return e === "ended" ? this.player.endedReason === "interrupted" ? this.interruptedMessage : this.endedMessage : e === "waiting" ? this.hasPlayed ? "Reconnecting — please stand by" : "Starting soon…" : e === "error" ? this.errorMessage === void 0 ? this.lastError?.code === "GEO_DENIED" ? or(this.lastError.country) : this.lastError?.code === "DRM_DEVICE_REVOKED" ? sr : this.lastError?.message ?? "Playback unavailable" : this.errorMessage : null;
3037
+ return e === "ended" ? this.player.endedReason === "interrupted" ? this.interruptedMessage : this.endedMessage : e === "waiting" ? this.hasPlayed ? "Reconnecting — please stand by" : "Starting soon…" : e === "error" ? this.errorMessage === void 0 ? this.lastError?.code === "GEO_DENIED" ? _r(this.lastError.country) : this.lastError?.code === "DRM_DEVICE_REVOKED" ? vr : this.lastError?.message ?? "Playback unavailable" : this.errorMessage : null;
2924
3038
  }
2925
3039
  renderStatus(e) {
2926
3040
  let t = this.statusMessageFor(e);
@@ -2935,7 +3049,7 @@ var lr = class {
2935
3049
  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);
2936
3050
  } else {
2937
3051
  let r = this.doc.createElement("div");
2938
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !ar.has(this.lastError.code)) {
3052
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !gr.has(this.lastError.code)) {
2939
3053
  let e = this.doc.createElement("button");
2940
3054
  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);
2941
3055
  }
@@ -2976,7 +3090,7 @@ var lr = class {
2976
3090
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
2977
3091
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
2978
3092
  }
2979
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${ur(this.video.currentTime)} / ${ur(this.video.duration)}`);
3093
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${xr(this.video.currentTime)} / ${xr(this.video.duration)}`);
2980
3094
  }
2981
3095
  syncLive() {
2982
3096
  let e = this.player.getStats(), t = e.isLive !== !1, n = this.player.state === "ended", r = !t && !this.streamEnded();
@@ -3026,17 +3140,17 @@ var lr = class {
3026
3140
  renderQualityMenu() {
3027
3141
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
3028
3142
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
3029
- for (let n of e) this.menu.append(this.menuItem(dr(n), t === n.index, () => this.player.setQuality(n.index)));
3143
+ for (let n of e) this.menu.append(this.menuItem(Sr(n), t === n.index, () => this.player.setQuality(n.index)));
3030
3144
  }
3031
3145
  renderAudioMenu() {
3032
3146
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
3033
3147
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
3034
- for (let t of e) this.menu.append(this.menuItem(fr(t), t.default, () => this.player.setAudioTrack(t.id)));
3148
+ for (let t of e) this.menu.append(this.menuItem(Cr(t), t.default, () => this.player.setAudioTrack(t.id)));
3035
3149
  }
3036
3150
  renderCcMenu() {
3037
3151
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
3038
3152
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
3039
- for (let t of e) this.menu.append(this.menuItem(pr(t), !1, () => this.player.setTextTrack(t.id)));
3153
+ for (let t of e) this.menu.append(this.menuItem(wr(t), !1, () => this.player.setTextTrack(t.id)));
3040
3154
  }
3041
3155
  onTracksChanged() {
3042
3156
  this.openMenu && this.renderMenu(this.openMenu);
@@ -3141,41 +3255,41 @@ var lr = class {
3141
3255
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
3142
3256
  }
3143
3257
  };
3144
- function ur(e) {
3258
+ function xr(e) {
3145
3259
  (!Number.isFinite(e) || e < 0) && (e = 0);
3146
3260
  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");
3147
3261
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
3148
3262
  }
3149
- function dr(e) {
3263
+ function Sr(e) {
3150
3264
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
3151
3265
  }
3152
- function fr(e) {
3266
+ function Cr(e) {
3153
3267
  return e.name || e.lang || `Track ${e.id}`;
3154
3268
  }
3155
- function pr(e) {
3269
+ function wr(e) {
3156
3270
  return e.name || e.lang || `Track ${e.id}`;
3157
3271
  }
3158
3272
  //#endregion
3159
3273
  //#region src/managed/index.ts
3160
- var mr = 6e3, hr = 45e3, gr = 2e3, _r = 3, vr = 5e3, yr = 2e3, br = 48e4, xr = "keySystemLicenseRequestFailed";
3161
- function Sr(e) {
3274
+ var Tr = 6e3, Er = 45e3, Dr = 2e3, Or = 3, kr = 5e3, Ar = 2e3, jr = 48e4, Mr = "keySystemLicenseRequestFailed";
3275
+ function Nr(e) {
3162
3276
  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)})`);
3163
3277
  if (e.env !== void 0) return at(e.env);
3164
3278
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
3165
3279
  return e.baseUrl;
3166
3280
  }
3167
- async function Cr(e) {
3281
+ async function Pr(e) {
3168
3282
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
3169
- let t = Sr(e);
3283
+ let t = Nr(e);
3170
3284
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
3171
3285
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
3172
- let r = new wr({
3286
+ let r = new Fr({
3173
3287
  ...e,
3174
3288
  baseUrl: t
3175
3289
  });
3176
3290
  return await r.start(), r;
3177
3291
  }
3178
- var wr = class {
3292
+ var Fr = class {
3179
3293
  opts;
3180
3294
  abort = new AbortController();
3181
3295
  listeners = {};
@@ -3270,7 +3384,7 @@ var wr = class {
3270
3384
  setTimeout(() => {
3271
3385
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
3272
3386
  phase: i ? "unavailable" : "tooEarly",
3273
- retryInMs: hr,
3387
+ retryInMs: Er,
3274
3388
  attempt: 1,
3275
3389
  elapsedMs: 0,
3276
3390
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -3309,13 +3423,13 @@ var wr = class {
3309
3423
  !a && !p() && h();
3310
3424
  }, e);
3311
3425
  };
3312
- _(mr), this.armingSub = E({
3426
+ _(Tr), this.armingSub = E({
3313
3427
  eventsBaseUrl: t,
3314
3428
  matchUrn: this.opts.matchUrn,
3315
3429
  credential: this.opts.credential,
3316
- onAlive: () => _(hr),
3430
+ onAlive: () => _(Er),
3317
3431
  onState: (e) => {
3318
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * gr);
3432
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Dr);
3319
3433
  },
3320
3434
  onError: () => h()
3321
3435
  });
@@ -3338,7 +3452,7 @@ var wr = class {
3338
3452
  let n = await c(this.opts.credential);
3339
3453
  if (this.destroyed || t !== this.loadGen) return;
3340
3454
  this.setState("loading");
3341
- let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = ut(this.opts.video, (e) => {
3455
+ let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = dt(this.opts.video, (e) => {
3342
3456
  this._state !== "ended" && this._state !== "error" && this.setState(e);
3343
3457
  });
3344
3458
  if (await i.load(), this.destroyed || t !== this.loadGen) {
@@ -3360,26 +3474,26 @@ var wr = class {
3360
3474
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
3361
3475
  let i = e.analytics?.sid;
3362
3476
  if (!i) return;
3363
- let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = Vn(this.opts.baseUrl, a, e.analytics?.endpoint);
3477
+ let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = Qn(this.opts.baseUrl, a, e.analytics?.endpoint);
3364
3478
  if (!o) {
3365
3479
  s && setTimeout(() => {
3366
3480
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, s));
3367
3481
  }, 0);
3368
3482
  return;
3369
3483
  }
3370
- let c = this.opts.video, l = new Hn({
3484
+ let c = this.opts.video, l = new $n({
3371
3485
  endpoint: o,
3372
3486
  sid: i,
3373
3487
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
3374
- sdkVersion: Jn,
3375
- environment: Kn(c),
3488
+ sdkVersion: ar,
3489
+ environment: rr(c),
3376
3490
  idleMs: typeof this.opts.analytics == "object" ? this.opts.analytics.idleMs : void 0,
3377
3491
  onIdle: () => this.onAnalyticsIdle(t),
3378
3492
  onBlocked: (e) => setTimeout(() => {
3379
3493
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, e));
3380
3494
  }, 0),
3381
3495
  sample: () => {
3382
- let e = this.engine?.getStats() ?? z, t = c.buffered.length ? Math.max(0, c.buffered.end(c.buffered.length - 1) - c.currentTime) : 0;
3496
+ let e = this.engine?.getStats() ?? ot, t = c.buffered.length ? Math.max(0, c.buffered.end(c.buffered.length - 1) - c.currentTime) : 0;
3383
3497
  return {
3384
3498
  ...e,
3385
3499
  bufferedMs: Math.round(t * 1e3),
@@ -3393,7 +3507,7 @@ var wr = class {
3393
3507
  let u = () => l.noteSeek(), d = () => l.noteVolume(c.muted, c.volume);
3394
3508
  c.addEventListener("seeking", u), c.addEventListener("volumechange", d), this.detachMediaListeners = () => {
3395
3509
  c.removeEventListener("seeking", u), c.removeEventListener("volumechange", d);
3396
- }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), qn().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
3510
+ }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), ir().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
3397
3511
  }
3398
3512
  currentPresentation() {
3399
3513
  let e = this.opts.video;
@@ -3414,6 +3528,8 @@ var wr = class {
3414
3528
  snapToLiveOnRefocus: this.opts.snapToLiveOnRefocus,
3415
3529
  rememberBandwidth: this.opts.rememberBandwidth,
3416
3530
  sizeCapHeadroom: this.opts.sizeCapHeadroom,
3531
+ adaptiveHoldBack: this.opts.adaptiveHoldBack,
3532
+ adaptiveHoldBackCeiling: this.opts.adaptiveHoldBackCeiling,
3417
3533
  mediaDuringKey: this.opts.mediaDuringKey,
3418
3534
  debug: this.opts.debug,
3419
3535
  deferLoad: r,
@@ -3421,11 +3537,11 @@ var wr = class {
3421
3537
  emit: (e) => this.onEngineEvent(e),
3422
3538
  freshLicenseUrl: () => this.refreshLicense()
3423
3539
  }, o = this.opts.engine ?? "auto";
3424
- if (o === "native") return new W(a);
3425
- if (o === "hls") return new wn(a);
3426
- if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && H(this.opts.video) && U() || !e.drmEnabled && St() && H(this.opts.video)) return new W(a);
3427
- if (It()) return new wn(a);
3428
- if (H(this.opts.video)) return new W(a);
3540
+ if (o === "native") return new U(a);
3541
+ if (o === "hls") return new Fn(a);
3542
+ if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && V(this.opts.video) && H() || !e.drmEnabled && Ct() && V(this.opts.video)) return new U(a);
3543
+ if (Lt()) return new Fn(a);
3544
+ if (V(this.opts.video)) return new U(a);
3429
3545
  throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
3430
3546
  }
3431
3547
  onEngineEvent(e) {
@@ -3445,6 +3561,9 @@ var wr = class {
3445
3561
  case "lowLatencyDetected":
3446
3562
  this._descriptor && (this._descriptor.isLowLatency = e.value);
3447
3563
  break;
3564
+ case "livetargetchange":
3565
+ this.emit("latencytargetchange", e.change);
3566
+ break;
3448
3567
  case "qualitychange":
3449
3568
  this.emit("qualitychange", e.index);
3450
3569
  break;
@@ -3487,7 +3606,7 @@ var wr = class {
3487
3606
  r = 0, i = !1;
3488
3607
  },
3489
3608
  onError: () => {
3490
- this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= _r && (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.`))));
3609
+ this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= Or && (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.`))));
3491
3610
  }
3492
3611
  });
3493
3612
  }
@@ -3501,7 +3620,7 @@ var wr = class {
3501
3620
  return;
3502
3621
  }
3503
3622
  let e = this.hiddenAtMs;
3504
- this.hiddenAtMs = null, e != null && Date.now() - e >= vr && this.resyncLiveState();
3623
+ this.hiddenAtMs = null, e != null && Date.now() - e >= kr && this.resyncLiveState();
3505
3624
  }, this.onReturnPageShow = (e) => {
3506
3625
  e.persisted && this.resyncLiveState();
3507
3626
  }, document.addEventListener("visibilitychange", this.onReturnVisible), typeof window < "u" && window.addEventListener("pageshow", this.onReturnPageShow));
@@ -3533,7 +3652,7 @@ var wr = class {
3533
3652
  scheduleRejoin(e) {
3534
3653
  this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
3535
3654
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
3536
- }, Math.random() * gr));
3655
+ }, Math.random() * Dr));
3537
3656
  }
3538
3657
  cancelRejoin() {
3539
3658
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -3557,7 +3676,7 @@ var wr = class {
3557
3676
  }
3558
3677
  t.controls = !1;
3559
3678
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
3560
- r && r.insertBefore(n, t), n.appendChild(t), Zn(n, this.opts.theme), this.container = n, this.controlBar = cr({
3679
+ r && r.insertBefore(n, t), n.appendChild(t), cr(n, this.opts.theme), this.container = n, this.controlBar = yr({
3561
3680
  root: n,
3562
3681
  video: t,
3563
3682
  player: this,
@@ -3595,7 +3714,7 @@ var wr = class {
3595
3714
  }
3596
3715
  startStats() {
3597
3716
  this.stopStats();
3598
- let e = Math.max(500, this.opts.statsIntervalMs ?? yr);
3717
+ let e = Math.max(500, this.opts.statsIntervalMs ?? Ar);
3599
3718
  this.statsTimer = setInterval(() => {
3600
3719
  if (!this.engine) return;
3601
3720
  let e = this.engine.getStats();
@@ -3606,14 +3725,14 @@ var wr = class {
3606
3725
  this.statsTimer && clearInterval(this.statsTimer), this.statsTimer = void 0;
3607
3726
  }
3608
3727
  isStaleLicenseDenial(e) {
3609
- return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== xr || e.error.httpStatus !== 403 || e.error.code !== "FORBIDDEN" || e.error.serverCode === "license_refused" ? !1 : K(this._descriptor?.drm?.widevine?.licenseUrl);
3728
+ return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== Mr || e.error.httpStatus !== 403 || e.error.code !== "FORBIDDEN" || e.error.serverCode === "license_refused" ? !1 : G(this._descriptor?.drm?.widevine?.licenseUrl);
3610
3729
  }
3611
3730
  scheduleLicenseRefresh() {
3612
3731
  if (this.stopRefresh(), !this._descriptor?.drmEnabled) return;
3613
3732
  typeof document < "u" && this.engine?.name === "hls" && (this.onRefocus = () => {
3614
- document.visibilityState !== "visible" || this.destroyed || K(this._descriptor?.drm?.widevine?.licenseUrl) && this.refreshLicense();
3733
+ document.visibilityState !== "visible" || this.destroyed || G(this._descriptor?.drm?.widevine?.licenseUrl) && this.refreshLicense();
3615
3734
  }, document.addEventListener("visibilitychange", this.onRefocus));
3616
- let e = this.opts.licenseRefreshMs ?? br;
3735
+ let e = this.opts.licenseRefreshMs ?? jr;
3617
3736
  e && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
3618
3737
  }
3619
3738
  stopRefresh() {
@@ -3670,7 +3789,7 @@ var wr = class {
3670
3789
  this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
3671
3790
  }
3672
3791
  getStats() {
3673
- return this.engine?.getStats() ?? z;
3792
+ return this.engine?.getStats() ?? ot;
3674
3793
  }
3675
3794
  getQualityLevels() {
3676
3795
  return this.engine?.getQualityLevels() ?? [];
@@ -3783,7 +3902,7 @@ function $(e) {
3783
3902
  }
3784
3903
  //#endregion
3785
3904
  //#region src/embed/host.ts
3786
- function Tr(e) {
3905
+ function Ir(e) {
3787
3906
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
3788
3907
  if (!e.src) throw Error("mountEmbed: opts.src is required");
3789
3908
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -3842,15 +3961,15 @@ function Tr(e) {
3842
3961
  }
3843
3962
  //#endregion
3844
3963
  //#region src/embed/page.ts
3845
- var Er = /^[A-Za-z0-9:_.-]{1,128}$/;
3846
- function Dr(e) {
3964
+ var Lr = /^[A-Za-z0-9:_.-]{1,128}$/;
3965
+ function Rr(e) {
3847
3966
  if (typeof e == "boolean") return e;
3848
3967
  if (typeof e == "object" && e) {
3849
3968
  let t = e.endpoint;
3850
3969
  return typeof t == "string" ? { endpoint: t } : {};
3851
3970
  }
3852
3971
  }
3853
- function Or() {
3972
+ function zr() {
3854
3973
  let e = window.HAVIK_EMBED_CONFIG;
3855
3974
  if (e) return {
3856
3975
  baseUrl: e.baseUrl ?? "",
@@ -3860,7 +3979,7 @@ function Or() {
3860
3979
  autoplay: e.autoplay ?? !0,
3861
3980
  muted: e.muted ?? !0,
3862
3981
  waitForLive: e.waitForLive ?? !0,
3863
- analytics: Dr(e.analytics)
3982
+ analytics: Rr(e.analytics)
3864
3983
  };
3865
3984
  let t = new URLSearchParams(location.search);
3866
3985
  return {
@@ -3873,9 +3992,9 @@ function Or() {
3873
3992
  waitForLive: !0
3874
3993
  };
3875
3994
  }
3876
- async function kr() {
3877
- let e = Or();
3878
- jr();
3995
+ async function Br() {
3996
+ let e = zr();
3997
+ Hr();
3879
3998
  let t = document.createElement("video");
3880
3999
  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);
3881
4000
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -3931,12 +4050,12 @@ async function kr() {
3931
4050
  message: e.message,
3932
4051
  ...e.country === void 0 ? {} : { country: e.country }
3933
4052
  })), e.on("autoplayblocked", () => {
3934
- i({ type: "oddin:autoplayblocked" }), Ar(t, e);
4053
+ i({ type: "oddin:autoplayblocked" }), Vr(t, e);
3935
4054
  });
3936
4055
  }, s = async (r) => {
3937
4056
  a?.destroy(), a = null;
3938
4057
  try {
3939
- let n = await Cr({
4058
+ let n = await Pr({
3940
4059
  video: t,
3941
4060
  baseUrl: e.baseUrl,
3942
4061
  matchUrn: r,
@@ -3999,7 +4118,7 @@ async function kr() {
3999
4118
  a?.retry();
4000
4119
  break;
4001
4120
  case "oddin:load":
4002
- typeof e.matchUrn == "string" && Er.test(e.matchUrn) && s(e.matchUrn);
4121
+ typeof e.matchUrn == "string" && Lr.test(e.matchUrn) && s(e.matchUrn);
4003
4122
  break;
4004
4123
  case "oddin:destroy": c();
4005
4124
  }
@@ -4011,18 +4130,18 @@ async function kr() {
4011
4130
  protocol: 1
4012
4131
  });
4013
4132
  }
4014
- function Ar(e, t) {
4133
+ function Vr(e, t) {
4015
4134
  if (document.getElementById("havik-tap")) return;
4016
4135
  let n = document.createElement("button");
4017
4136
  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", () => {
4018
4137
  n.remove(), t.play();
4019
4138
  }), document.body.appendChild(n);
4020
4139
  }
4021
- function jr() {
4140
+ function Hr() {
4022
4141
  let e = document.createElement("style");
4023
4142
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
4024
4143
  }
4025
4144
  //#endregion
4026
- export { Yn as ODDIN_THEME, n as PlaybackError, Zn as applyTheme, at as baseUrlForEnv, kr as bootEmbed, Ee as clearDeviceId, Re as clearRememberedBandwidth, Cr as createPlayer, D as deriveEventsBaseUrl, tt as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ve as fetchCatalog, be as fetchTournament, Se as flattenMatches, C as fullscreenSupported, A as getDeviceId, rt as isLowLatencyManifest, $ as isOddinMessage, Oe as licenseRequestHeaders, Tr as mountEmbed, fe as pollToLive, y as resolveStream, E as subscribeLiveState, S as videoFullscreenActive, we as watchStatus };
4145
+ export { or as ODDIN_THEME, n as PlaybackError, cr as applyTheme, at as baseUrlForEnv, Br as bootEmbed, Ee as clearDeviceId, Re as clearRememberedBandwidth, Pr as createPlayer, D as deriveEventsBaseUrl, tt as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ve as fetchCatalog, be as fetchTournament, Se as flattenMatches, C as fullscreenSupported, A as getDeviceId, rt as isLowLatencyManifest, $ as isOddinMessage, Oe as licenseRequestHeaders, Ir as mountEmbed, fe as pollToLive, y as resolveStream, E as subscribeLiveState, S as videoFullscreenActive, we as watchStatus };
4027
4146
 
4028
4147
  //# sourceMappingURL=index.js.map