@oddin-gg/havik-player 2.22.0 → 2.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -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 = 9, yn = .5, bn = 64;
1498
- function xn(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 ??= _n;
1504
- let t = Math.max(12, e.liveSyncDuration + vn);
1505
- (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Sn(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 Sn(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 Cn = class {
1525
+ var Fn = class {
1512
1526
  name = "hls";
1513
1527
  hls = null;
1514
1528
  deps;
@@ -1572,6 +1586,15 @@ var Cn = 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;
@@ -1584,6 +1607,7 @@ var Cn = class {
1584
1607
  fpsLastFrames = -1;
1585
1608
  fpsLastDropped = -1;
1586
1609
  fpsBadTicks = 0;
1610
+ fpsGraceTicks = 0;
1587
1611
  fpsDownCaps = 0;
1588
1612
  sizeCapManaged = !0;
1589
1613
  dprMedia = null;
@@ -1592,11 +1616,11 @@ var Cn = class {
1592
1616
  frameCounter;
1593
1617
  detachFrames = null;
1594
1618
  constructor(e) {
1595
- 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;
1596
1620
  }
1597
1621
  async load() {
1598
1622
  this.detachFrames ??= this.frameCounter.attach();
1599
- 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;
1600
1624
  if (e.drmEnabled && !o) {
1601
1625
  i({
1602
1626
  type: "error",
@@ -1628,7 +1652,7 @@ var Cn = class {
1628
1652
  }
1629
1653
  this.loadSuspended = !0;
1630
1654
  }
1631
- }, en));
1655
+ }, tn));
1632
1656
  }
1633
1657
  resumeLoading() {
1634
1658
  if (this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.resetChaseWindow(), this.resetFrameDropWindow(), this.loadSuspended && (this.loadSuspended = !1, !this.stopped && this.hls)) {
@@ -1660,7 +1684,7 @@ var Cn = class {
1660
1684
  let t = e.liveSyncPosition;
1661
1685
  if (typeof t != "number" || !Number.isFinite(t)) return;
1662
1686
  let { video: n } = this.deps;
1663
- n.currentTime <= t + yn || (n.currentTime = t);
1687
+ n.currentTime <= t + xn || (n.currentTime = t);
1664
1688
  }
1665
1689
  reanchorPartTimeline(e, t) {
1666
1690
  let { frag: n, part: r } = t;
@@ -1672,7 +1696,7 @@ var Cn = class {
1672
1696
  let o = n.sn, s = i.fragments.find((e) => e && e.sn === o) ?? (i.fragmentHint?.sn === o ? i.fragmentHint : void 0);
1673
1697
  if (!s) return;
1674
1698
  let c = a.startPTS - s.start;
1675
- if (!(!Number.isFinite(c) || Math.abs(c) < qt)) {
1699
+ if (!(!Number.isFinite(c) || Math.abs(c) < Jt)) {
1676
1700
  for (let e of i.fragments) e && e.setStart(e.start + c);
1677
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})`);
1678
1702
  }
@@ -1680,7 +1704,7 @@ var Cn = class {
1680
1704
  startHls() {
1681
1705
  let { video: t, descriptor: n, credential: r, deviceId: i, userId: a, lowLatency: o, debug: s, hlsConfig: c, emit: l } = this.deps;
1682
1706
  this.clearKick(), this.resetStarvationWindow(), this.resetChaseWindow(), this.resetFrameDropWindow(), this.lastFragLoadingMs = 0, this.lastEndSN = -1, this.lastAdvanceMs = 0;
1683
- 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 = {
1684
1708
  debug: s === !0,
1685
1709
  enableWorker: !o,
1686
1710
  lowLatencyMode: o,
@@ -1707,12 +1731,12 @@ var Cn = class {
1707
1731
  this.licenseTimeMs === 0 && (this.licenseTimeMs = Math.max(1, Math.round(performance.now() - o)));
1708
1732
  });
1709
1733
  }, { freshLicenseUrl: c } = this.deps;
1710
- if (!c || !K(this.licenseUrl)) {
1734
+ if (!c || !G(this.licenseUrl)) {
1711
1735
  s();
1712
1736
  return;
1713
1737
  }
1714
1738
  let l = this.hls, u, d = new Promise((e) => {
1715
- u = setTimeout(() => e(void 0), Ut);
1739
+ u = setTimeout(() => e(void 0), Wt);
1716
1740
  });
1717
1741
  return Promise.race([c().catch(() => void 0), d]).finally(() => clearTimeout(u)).then((e) => {
1718
1742
  if (!this.hls || this.hls !== l) throw Error("havik-player: engine torn down during refresh");
@@ -1728,20 +1752,20 @@ var Cn = class {
1728
1752
  ...c
1729
1753
  };
1730
1754
  if (this.prefetch?.destroy(), this.prefetch = null, u && this.deps.mediaDuringKey !== !1 && !f.fLoader) {
1731
- let e = new jt({
1755
+ let e = new Mt({
1732
1756
  sessionId: n.analytics?.sid,
1733
1757
  contentId: n.matchUrn
1734
- }), t = Mt(e);
1758
+ }), t = Nt(e);
1735
1759
  t && (this.prefetch = e, f.fLoader = t);
1736
1760
  }
1737
- xn(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;
1738
1762
  let p = new e(f);
1739
- 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, () => {
1740
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" });
1741
1765
  }), p.on(e.Events.FRAG_BUFFERED, (e, t) => {
1742
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(() => {
1743
1767
  this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.starvationRebuilds = 0, this.recoveryResetTimer = null;
1744
- }, Wt)));
1768
+ }, Gt)));
1745
1769
  }), p.on(e.Events.FRAG_LOADING, (e, t) => {
1746
1770
  t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
1747
1771
  }), p.on(e.Events.FRAG_LOADED, (e, t) => {
@@ -1749,10 +1773,10 @@ var Cn = class {
1749
1773
  let n = t?.frag?.stats, r = n?.loaded;
1750
1774
  typeof r == "number" && (this.bytesLoaded += r), !this.reportedFirstBytes && typeof r == "number" && r > 0 && (this.reportedFirstBytes = !0, l({ type: "firstBytes" }));
1751
1775
  let i = n?.loading;
1752
- 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 > bn && 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);
1753
1777
  let a = t?.frag?.url;
1754
1778
  if (a) {
1755
- let e = ot(a);
1779
+ let e = st(a);
1756
1780
  e && (this.cdnHost = e);
1757
1781
  }
1758
1782
  }), p.on(e.Events.LEVEL_LOADED, (e, t) => {
@@ -1766,18 +1790,18 @@ var Cn = class {
1766
1790
  }), p.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.prefetch) {
1767
1791
  let t = this.prefetch;
1768
1792
  p.on(e.Events.KEY_LOADING, (e, n) => {
1769
- let r = n?.frag, i = Pt(r?.type);
1770
- if (r && t.isActive(i)) for (let e of Ft(p, r)) t.prefetch(e.url, i, e.kind);
1771
- }), 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)));
1772
1796
  }
1773
1797
  p.on(e.Events.KEY_LOADED, () => {
1774
1798
  u && !this.reportedDrmReady && (this.reportedDrmReady = !0, l({ type: "drmReady" }));
1775
1799
  }), this.endWatchdog && clearInterval(this.endWatchdog), this.endWatchdog = setInterval(() => {
1776
- this.checkEndStale(), this.checkStarvation(), this.updateCatchUpBand(), this.checkFutileChase(), this.checkFrameDrops();
1777
- }, 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) => {
1778
1802
  this.renditionSwitches += 1;
1779
1803
  let n = this.lastLevelIndex >= 0 ? p.levels[this.lastLevelIndex] : void 0, r = p.levels[t.level];
1780
- n && r && r.bitrate !== n.bitrate && (r.bitrate > n.bitrate ? this.upshifts += 1 : this.downshifts += 1), this.lastLevelIndex = t.level, l({
1804
+ n && r && r.bitrate !== n.bitrate && (r.bitrate > n.bitrate ? this.upshifts += 1 : this.downshifts += 1), this.lastLevelIndex = t.level, this.armFrameDropGrace(), l({
1781
1805
  type: "qualitychange",
1782
1806
  index: t.level
1783
1807
  });
@@ -1825,9 +1849,9 @@ var Cn = class {
1825
1849
  let o = e.config ?? {}, s = typeof self < "u" && self.devicePixelRatio > 0 ? self.devicePixelRatio : 1, c = o.ignoreDevicePixelRatio ? 1 : s;
1826
1850
  c = Math.min(c, o.maxDevicePixelRatio ?? Infinity);
1827
1851
  let l = t.getMaxLevelByMediaSize(e.levels, i * c, a * c), u = this.deps.sizeCapHeadroom;
1828
- if (u == null && s >= fn) return l;
1829
- let d = u ?? dn, f = Number.isNaN(d) ? dn : d === Infinity ? e.levels.length : Math.max(0, Math.floor(d));
1830
- 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);
1831
1855
  }
1832
1856
  onMediaResize() {
1833
1857
  if (!this.sizeCapManaged) return;
@@ -1842,7 +1866,7 @@ var Cn = class {
1842
1866
  return;
1843
1867
  }
1844
1868
  this.capTimer && clearTimeout(this.capTimer);
1845
- let t = e > this.sizeCapLevel ? ln : un;
1869
+ let t = e > this.sizeCapLevel ? un : dn;
1846
1870
  this.capTimer = setTimeout(() => {
1847
1871
  this.capTimer = null, this.computeSizeCapTarget() === e ? this.applySizeCap() : this.onMediaResize();
1848
1872
  }, t);
@@ -1924,6 +1948,7 @@ var Cn = class {
1924
1948
  playlistBytesLoaded: this.playlistBytesLoaded,
1925
1949
  renditionSwitches: this.renditionSwitches,
1926
1950
  liveRetargets: this.liveRetargets,
1951
+ holdBackSteps: this.holdBackSteps,
1927
1952
  fpsDownCaps: this.fpsDownCaps,
1928
1953
  licenseTimeMs: this.licenseTimeMs,
1929
1954
  upshifts: this.upshifts,
@@ -1952,9 +1977,10 @@ var Cn = class {
1952
1977
  let t = e.details;
1953
1978
  if (t) {
1954
1979
  if (t.live === !0) {
1955
- this.sawLive = !0;
1956
- let e = t.endSN ?? -1;
1957
- 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());
1958
1984
  return;
1959
1985
  }
1960
1986
  this.sawLive && this.emitEnded("endlist");
@@ -1971,7 +1997,7 @@ var Cn = class {
1971
1997
  return;
1972
1998
  }
1973
1999
  let e = Date.now() - this.lastAdvanceMs;
1974
- !this.chaseSuspended && e > Jt && this.suspendChasing(), e > Gt && this.emitEnded("stalled");
2000
+ !this.chaseSuspended && e > Yt && this.suspendChasing(), e > Kt && this.emitEnded("stalled");
1975
2001
  }
1976
2002
  resetStarvationWindow() {
1977
2003
  this.starvedTicks = 0, this.deadTicks = 0, this.mainFragsSinceTick = 0, this.recentMainFrags = [];
@@ -1986,7 +2012,7 @@ var Cn = class {
1986
2012
  this.resetStarvationWindow();
1987
2013
  return;
1988
2014
  }
1989
- 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;
1990
2016
  if (this.starvedFrames = c, !a || l) {
1991
2017
  (t.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA || l) && this.clearKick(), this.resetStarvationWindow();
1992
2018
  return;
@@ -1995,12 +2021,12 @@ var Cn = class {
1995
2021
  this.resetStarvationWindow();
1996
2022
  return;
1997
2023
  }
1998
- 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)) {
1999
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");
2000
2026
  return;
2001
2027
  }
2002
- let u = i && this.starvedTicks >= nn, d = !i && this.deadTicks >= sn && this.recentMainFrags.reduce((e, t) => e + t, 0) >= cn;
2003
- 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))) {
2004
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");
2005
2031
  try {
2006
2032
  e.stopLoad(), e.startLoad(-1);
@@ -2015,7 +2041,7 @@ var Cn = class {
2015
2041
  let t = e.latency, n = e.targetLatency;
2016
2042
  if (typeof t != "number" || typeof n != "number" || !Number.isFinite(t)) return;
2017
2043
  let r = t - n;
2018
- !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);
2019
2045
  let i = this.catchUpEngaged ? this.catchUpRate : 1;
2020
2046
  e.config.maxLiveSyncPlaybackRate !== i && (e.config.maxLiveSyncPlaybackRate = i);
2021
2047
  }
@@ -2027,6 +2053,9 @@ var Cn = class {
2027
2053
  e.bitrate <= this.fpsCapBitrate && i && (n = r);
2028
2054
  }), n < 0 ? 0 : n;
2029
2055
  }
2056
+ armFrameDropGrace() {
2057
+ this.fpsGraceTicks > 0 || (this.fpsGraceTicks = vn, this.fpsBadTicks = 0);
2058
+ }
2030
2059
  resetFrameDropWindow() {
2031
2060
  this.fpsLastFrames = -1, this.fpsLastDropped = -1, this.fpsBadTicks = 0;
2032
2061
  }
@@ -2036,16 +2065,20 @@ var Cn = class {
2036
2065
  this.resetFrameDropWindow();
2037
2066
  return;
2038
2067
  }
2039
- let { totalFrames: n, dropped: r } = B(t), i = this.fpsLastFrames < 0, a = n - this.fpsLastFrames, o = r - this.fpsLastDropped;
2040
- 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) {
2041
2070
  this.fpsBadTicks = 0;
2042
2071
  return;
2043
2072
  }
2044
- if (o <= mn * a) {
2073
+ if (this.fpsGraceTicks > 0) {
2074
+ --this.fpsGraceTicks, this.fpsBadTicks = 0;
2075
+ return;
2076
+ }
2077
+ if (o <= hn * a) {
2045
2078
  this.fpsBadTicks = 0;
2046
2079
  return;
2047
2080
  }
2048
- if (this.fpsBadTicks += 1, this.fpsBadTicks < gn) return;
2081
+ if (this.fpsBadTicks += 1, this.fpsBadTicks < _n) return;
2049
2082
  this.fpsBadTicks = 0;
2050
2083
  let s = e.levels, c = s[e.currentLevel];
2051
2084
  if (!c) return;
@@ -2062,20 +2095,113 @@ var Cn = class {
2062
2095
  return;
2063
2096
  }
2064
2097
  let n = e.latency, r = t.playbackRate, i = e.config.maxLiveSyncPlaybackRate;
2065
- 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))) {
2066
2099
  this.resetChaseWindow();
2067
2100
  return;
2068
2101
  }
2069
- if (this.chaseLatency < 0 || this.chaseLatency - n >= Zt) {
2102
+ if (this.chaseLatency < 0 || this.chaseLatency - n >= Qt) {
2070
2103
  this.chaseLatency = n, this.chaseTicks = 0;
2071
2104
  return;
2072
2105
  }
2073
- if (this.chaseTicks += 1, this.chaseTicks < Xt) return;
2106
+ if (this.chaseTicks += 1, this.chaseTicks < Zt) return;
2074
2107
  this.resetChaseWindow();
2075
2108
  let a = typeof e.config.liveMaxLatencyDuration == "number" ? e.config.liveMaxLatencyDuration : e.maxLatency;
2076
2109
  if (!(a > 1)) return;
2077
- let o = typeof e.targetLatency == "number" ? e.targetLatency : 0, s = Math.min(n, a - 1);
2078
- 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
+ });
2079
2205
  }
2080
2206
  suspendChasing() {
2081
2207
  let e = this.hls;
@@ -2105,12 +2231,12 @@ var Cn = class {
2105
2231
  } catch {}
2106
2232
  }
2107
2233
  startEstimate() {
2108
- return this.deps.rememberBandwidth === !1 ? Lt : Ie() ?? Lt;
2234
+ return this.deps.rememberBandwidth === !1 ? Rt : Ie() ?? Rt;
2109
2235
  }
2110
2236
  maybeRememberBandwidth(e) {
2111
- if (this.deps.rememberBandwidth === !1 || this.bwFragsLoaded < Rt) return;
2237
+ if (this.deps.rememberBandwidth === !1 || this.bwFragsLoaded < zt) return;
2112
2238
  let t = Date.now();
2113
- if (!e && t - this.bwSavedAt < zt) return;
2239
+ if (!e && t - this.bwSavedAt < Bt) return;
2114
2240
  let n = this.hls?.bandwidthEstimate;
2115
2241
  typeof n != "number" || !Number.isFinite(n) || n <= 0 || (this.bwSavedAt = t, Le(n));
2116
2242
  }
@@ -2130,7 +2256,7 @@ var Cn = class {
2130
2256
  scheduleLicenseRetry(t) {
2131
2257
  if (t.details !== e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED && t.details !== e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED) return !1;
2132
2258
  let n = t.response?.code ?? 0;
2133
- 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;
2134
2260
  this.licenseRecoveries += 1;
2135
2261
  let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
2136
2262
  return this.licenseRetryTimer = setTimeout(() => {
@@ -2144,7 +2270,7 @@ var Cn = class {
2144
2270
  } catch (t) {
2145
2271
  console.warn(`havik-player: hls.js destroy() threw during ${e}`, t);
2146
2272
  }
2147
- this.hls = null, this.startHls();
2273
+ this.hls = null, this.startHls(), this.armFrameDropGrace();
2148
2274
  }
2149
2275
  }
2150
2276
  onError(t) {
@@ -2162,7 +2288,7 @@ var Cn = class {
2162
2288
  return;
2163
2289
  }
2164
2290
  if (t.type === e.ErrorTypes.NETWORK_ERROR) {
2165
- if (this.networkRecoveries < Vt) {
2291
+ if (this.networkRecoveries < Ht) {
2166
2292
  this.networkRecoveries += 1;
2167
2293
  let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
2168
2294
  this.networkRetryTimer = setTimeout(() => {
@@ -2179,7 +2305,7 @@ var Cn = class {
2179
2305
  return;
2180
2306
  }
2181
2307
  if (t.type === e.ErrorTypes.MEDIA_ERROR) {
2182
- if (this.mediaRecoveries < Bt) {
2308
+ if (this.mediaRecoveries < Vt) {
2183
2309
  this.mediaRecoveries += 1, n.recoverMediaError();
2184
2310
  return;
2185
2311
  }
@@ -2200,22 +2326,18 @@ var Cn = class {
2200
2326
  }
2201
2327
  }
2202
2328
  toError(t, r = "PLAYBACK") {
2203
- 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;
2204
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);
2205
2331
  }
2206
- }, wn = 15e3, Tn = 6e5, En = 3e5, Dn = 100, q = 1, On = 1e3, kn = 3e4, An = 1e3, jn = {
2332
+ }, In = 15e3, Ln = 6e5, Rn = 3e5, zn = 100, J = 1, Bn = 1e3, Vn = 3e4, Hn = 1e3, Un = {
2207
2333
  inline: "PRESENTATION_INLINE",
2208
2334
  fullscreen: "PRESENTATION_FULLSCREEN",
2209
2335
  pip: "PRESENTATION_PIP"
2210
- }, Mn = {
2336
+ }, Wn = {
2211
2337
  widevine: "KEY_SYSTEM_WIDEVINE",
2212
2338
  fairplay: "KEY_SYSTEM_FAIRPLAY",
2213
2339
  none: "KEY_SYSTEM_NONE"
2214
- };
2215
- function Nn(e, t) {
2216
- return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
2217
- }
2218
- var Pn = /* @__PURE__ */ new Set([
2340
+ }, Gn = /* @__PURE__ */ new Set([
2219
2341
  "INVALID_URN",
2220
2342
  "NOT_FOUND",
2221
2343
  "GEO_DENIED",
@@ -2226,7 +2348,7 @@ var Pn = /* @__PURE__ */ new Set([
2226
2348
  "FORBIDDEN",
2227
2349
  "RATE_LIMITED",
2228
2350
  "TIMEOUT"
2229
- ]), J = {
2351
+ ]), Y = {
2230
2352
  idle: "PLAYER_STATE_IDLE",
2231
2353
  waiting: "PLAYER_STATE_WAITING",
2232
2354
  loading: "PLAYER_STATE_LOADING",
@@ -2236,7 +2358,7 @@ var Pn = /* @__PURE__ */ new Set([
2236
2358
  ended: "PLAYER_STATE_ENDED",
2237
2359
  error: "PLAYER_STATE_ERROR"
2238
2360
  };
2239
- function Fn(e) {
2361
+ function Kn(e) {
2240
2362
  try {
2241
2363
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
2242
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);
@@ -2244,8 +2366,8 @@ function Fn(e) {
2244
2366
  return;
2245
2367
  }
2246
2368
  }
2247
- var In = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), Ln = /* @__PURE__ */ new Set(), Y = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), Rn = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
2248
- function zn(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) {
2249
2371
  if (e.disposition !== "enforce" || e.effectiveDirective !== "connect-src") return;
2250
2372
  let t;
2251
2373
  try {
@@ -2254,19 +2376,19 @@ function zn(e) {
2254
2376
  } catch {
2255
2377
  return;
2256
2378
  }
2257
- if (Ln.has(t) && !Y.has(t)) {
2258
- Y.add(t), _(Rn(new URL(t).origin));
2259
- 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();
2260
2382
  }
2261
2383
  }
2262
- function Z(e, t) {
2384
+ function Zn(e, t) {
2263
2385
  return _(t, e), { disabledReason: t };
2264
2386
  }
2265
- function Bn(e, t, n) {
2266
- if (n) return t && !In(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 };
2267
2389
  if (t) return { endpoint: t };
2268
- 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).");
2269
- let r = Fn(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);
2270
2392
  if (r) return { endpoint: r };
2271
2393
  let i;
2272
2394
  try {
@@ -2274,9 +2396,9 @@ function Bn(e, t, n) {
2274
2396
  } catch {
2275
2397
  i = e;
2276
2398
  }
2277
- 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).`);
2278
2400
  }
2279
- var Vn = class {
2401
+ var $n = class {
2280
2402
  opts;
2281
2403
  url;
2282
2404
  cspKey;
@@ -2358,8 +2480,8 @@ var Vn = class {
2358
2480
  constructor(e) {
2359
2481
  this.opts = {
2360
2482
  ...e,
2361
- heartbeatMs: e.heartbeatMs ?? wn,
2362
- idleMs: e.idleMs ?? Tn
2483
+ heartbeatMs: e.heartbeatMs ?? In,
2484
+ idleMs: e.idleMs ?? Ln
2363
2485
  }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`;
2364
2486
  try {
2365
2487
  let e = new URL(this.url);
@@ -2376,11 +2498,11 @@ var Vn = class {
2376
2498
  sdkVersion: this.opts.sdkVersion,
2377
2499
  ...this.opts.environment
2378
2500
  };
2379
- this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), On), this.lastTickMs = this.now(), this.timer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs), typeof window < "u" && window.addEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.addEventListener("visibilitychange", this.onVisibility), document.addEventListener("freeze", this.onFreeze), document.addEventListener("resume", this.onResume), this.cspKey && Ln.add(this.cspKey), document.addEventListener("securitypolicyviolation", zn), 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());
2380
2502
  }
2381
2503
  checkSuspension(e) {
2382
2504
  if (this.endedFlag || !this.opts.idleMs) return !1;
2383
- let t = Math.max(this.opts.idleMs, En);
2505
+ let t = Math.max(this.opts.idleMs, Rn);
2384
2506
  return this.lastTickMs > 0 && e - this.lastTickMs >= t ? (this.endIdle(this.lastTickMs), !0) : (this.lastTickMs = e, !1);
2385
2507
  }
2386
2508
  syncIdle(e) {
@@ -2394,21 +2516,21 @@ var Vn = class {
2394
2516
  noteStateChange(e, t, n, r) {
2395
2517
  if (this.endedFlag || e === t) return;
2396
2518
  let i = this.now();
2397
- 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") {
2398
2520
  this.stallIsSeekInduced = this.seekInduced(i);
2399
2521
  let e = !this.isHidden && !this.stallIsSeekInduced;
2400
- 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)));
2401
2523
  } else e === "buffering" && (this.isBuffering = !1, this.stallIsSeekInduced = !1);
2402
2524
  this.syncBuffering(), t === "paused" && (this.pauseCount += 1), this.syncIdle(t);
2403
2525
  let a = {
2404
- fromState: J[e] ?? "PLAYER_STATE_UNSPECIFIED",
2405
- toState: J[t] ?? "PLAYER_STATE_UNSPECIFIED",
2406
- presentation: jn[this.presentation]
2526
+ fromState: Y[e] ?? "PLAYER_STATE_UNSPECIFIED",
2527
+ toState: Y[t] ?? "PLAYER_STATE_UNSPECIFIED",
2528
+ presentation: Un[this.presentation]
2407
2529
  }, o = this.causeOf(e, t, i);
2408
2530
  o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
2409
2531
  }
2410
2532
  seekInduced(e) {
2411
- return this.lastSeekMs >= 0 && e - this.lastSeekMs < An;
2533
+ return this.lastSeekMs >= 0 && e - this.lastSeekMs < Hn;
2412
2534
  }
2413
2535
  causeOf(e, t, n) {
2414
2536
  if (t === "error") return "TRANSITION_CAUSE_ERROR";
@@ -2445,25 +2567,25 @@ var Vn = class {
2445
2567
  }
2446
2568
  this.muteChanges += 1;
2447
2569
  let r = this.now();
2448
- 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();
2449
2571
  }
2450
2572
  notePresentation(e) {
2451
2573
  this.endedFlag || e === this.presentation || (this.closePresentation(), this.presentation = e);
2452
2574
  }
2453
2575
  notePlayIntent() {
2454
- 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));
2455
2577
  }
2456
2578
  noteReady() {
2457
- 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));
2458
2580
  }
2459
2581
  noteFirstPlaylist() {
2460
- 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));
2461
2583
  }
2462
2584
  noteDrmReady() {
2463
- 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));
2464
2586
  }
2465
2587
  noteFirstBytes() {
2466
- 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));
2467
2589
  }
2468
2590
  noteSeek() {
2469
2591
  this.endedFlag || (this.seekCount += 1, this.lastSeekMs = this.now());
@@ -2482,7 +2604,7 @@ var Vn = class {
2482
2604
  let r = n ?? "";
2483
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";
2484
2606
  if (e === "DRM_CLIENT" || e === "DRM_DEVICE_REVOKED") return "ERROR_STAGE_DRM";
2485
- if (Pn.has(e)) return "ERROR_STAGE_RESOLVE";
2607
+ if (Gn.has(e)) return "ERROR_STAGE_RESOLVE";
2486
2608
  let i = t ?? "";
2487
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";
2488
2610
  }
@@ -2514,7 +2636,7 @@ var Vn = class {
2514
2636
  let l = t.droppedFrames ?? 0;
2515
2637
  l > this.lastDropped && (o.droppedFramesDelta = l - this.lastDropped), this.lastDropped = l;
2516
2638
  let u = t.renditionSwitches ?? 0;
2517
- 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";
2518
2640
  let d = this.mutedTotalMs;
2519
2641
  this.mutedSinceMs !== null && (d += e - this.mutedSinceMs), d > this.lastHbMutedMs && (o.mutedMsDelta = Math.round(d - this.lastHbMutedMs)), this.lastHbMutedMs = d;
2520
2642
  let f = t.upshifts ?? 0;
@@ -2529,20 +2651,20 @@ var Vn = class {
2529
2651
  if (this.lastRequestCount = g, _ > 0) {
2530
2652
  o.requestCountDelta = _;
2531
2653
  let e = t.requestTimesMs ?? [], n = e.slice(Math.max(0, e.length - _)).sort((e, t) => e - t);
2532
- n.length > 0 && (o.requestTimeP50Ms = Nn(n, 50), o.requestTimeP95Ms = Nn(n, 95));
2654
+ n.length > 0 && (o.requestTimeP50Ms = K(n, 50), o.requestTimeP95Ms = K(n, 95));
2533
2655
  }
2534
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();
2535
2657
  }
2536
2658
  maybeEmitMediaInfo(e) {
2537
2659
  if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
2538
2660
  let t = {};
2539
- e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = Mn[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);
2540
2662
  let n = JSON.stringify(t);
2541
2663
  n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
2542
2664
  }
2543
2665
  end(e, t) {
2544
2666
  if (this.endedFlag) return;
2545
- this.endedFlag = !0, X.delete(this);
2667
+ this.endedFlag = !0, Z.delete(this);
2546
2668
  let n = t ?? this.now();
2547
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));
2548
2670
  let r = this.safeSample();
@@ -2556,7 +2678,7 @@ var Vn = class {
2556
2678
  let c = r?.positionMs ?? 0;
2557
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));
2558
2680
  let l = e !== "END_REASON_ENDED";
2559
- l && this.lastStallMs > 0 && n - this.lastStallMs <= kn && (i.abandonAfterStallMs = Math.max(q, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= kn && (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);
2560
2682
  let u = r?.decodedFrames ?? this.lastDecoded;
2561
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");
2562
2684
  }
@@ -2576,13 +2698,13 @@ var Vn = class {
2576
2698
  };
2577
2699
  }
2578
2700
  noteCspBlocked() {
2579
- this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(Rn(new URL(this.url).origin)));
2701
+ this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(Yn(new URL(this.url).origin)));
2580
2702
  }
2581
2703
  cspBlocked() {
2582
- return !!this.cspKey && Y.has(this.cspKey);
2704
+ return !!this.cspKey && X.has(this.cspKey);
2583
2705
  }
2584
2706
  enqueue(e) {
2585
- if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > Dn;) {
2707
+ if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > zn;) {
2586
2708
  let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
2587
2709
  this.pending.splice(e >= 0 ? e : 0, 1);
2588
2710
  }
@@ -2609,7 +2731,7 @@ var Vn = class {
2609
2731
  };
2610
2732
  //#endregion
2611
2733
  //#region src/core/environment.ts
2612
- function Hn() {
2734
+ function er() {
2613
2735
  if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
2614
2736
  let e;
2615
2737
  try {
@@ -2634,14 +2756,14 @@ function Hn() {
2634
2756
  integration: "INTEGRATION_KIND_IFRAME"
2635
2757
  };
2636
2758
  }
2637
- function Un() {
2759
+ function tr() {
2638
2760
  if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
2639
2761
  let e = navigator.userAgentData, t = e?.platform ?? "";
2640
2762
  if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
2641
2763
  let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
2642
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";
2643
2765
  }
2644
- function Wn() {
2766
+ function nr() {
2645
2767
  let e = typeof navigator < "u" ? navigator.connection : void 0;
2646
2768
  if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
2647
2769
  let t = {
@@ -2660,17 +2782,17 @@ function Wn() {
2660
2782
  }
2661
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;
2662
2784
  }
2663
- function Gn(e) {
2664
- let t = Hn(), n = {
2665
- deviceClass: Un(),
2785
+ function rr(e) {
2786
+ let t = er(), n = {
2787
+ deviceClass: tr(),
2666
2788
  integration: t.integration,
2667
- ...Wn()
2789
+ ...nr()
2668
2790
  };
2669
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);
2670
2792
  let r = e.getBoundingClientRect?.();
2671
2793
  return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
2672
2794
  }
2673
- async function Kn() {
2795
+ async function ir() {
2674
2796
  let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
2675
2797
  if (!e?.getHighEntropyValues) return {};
2676
2798
  try {
@@ -2682,7 +2804,7 @@ async function Kn() {
2682
2804
  }
2683
2805
  //#endregion
2684
2806
  //#region src/core/version.ts
2685
- var qn = "2.22.0", Jn = {
2807
+ var ar = "2.23.0", or = {
2686
2808
  accent: "#E1B600",
2687
2809
  accentText: "#1B1C23",
2688
2810
  background: "#0e0f13",
@@ -2693,7 +2815,7 @@ var qn = "2.22.0", Jn = {
2693
2815
  border: "rgba(255, 255, 255, 0.1)",
2694
2816
  radius: "8px",
2695
2817
  fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
2696
- }, Yn = {
2818
+ }, sr = {
2697
2819
  accent: "--havik-accent",
2698
2820
  accentText: "--havik-accent-text",
2699
2821
  background: "--havik-bg",
@@ -2705,26 +2827,26 @@ var qn = "2.22.0", Jn = {
2705
2827
  radius: "--havik-radius",
2706
2828
  fontFamily: "--havik-font"
2707
2829
  };
2708
- function Xn(e, t) {
2830
+ function cr(e, t) {
2709
2831
  let n = {
2710
- ...Jn,
2832
+ ...or,
2711
2833
  ...t
2712
2834
  };
2713
- for (let t of Object.keys(Yn)) e.style.setProperty(Yn[t], n[t]);
2835
+ for (let t of Object.keys(sr)) e.style.setProperty(sr[t], n[t]);
2714
2836
  n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
2715
2837
  }
2716
2838
  //#endregion
2717
2839
  //#region src/ui/styles.ts
2718
- var Zn = "havik-player-styles", Qn = "\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", $n = /* @__PURE__ */ new WeakMap();
2719
- function er(e = document, t) {
2720
- let n = e.querySelector(`style#${Zn}`);
2721
- if (n && (!t || $n.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;
2722
2844
  let r = e.createElement("style");
2723
- r.id = Zn, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = Qn, $n.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);
2724
2846
  }
2725
2847
  //#endregion
2726
2848
  //#region src/ui/icons.ts
2727
- var tr = {
2849
+ var pr = {
2728
2850
  play: "<path d=\"M8 5v14l11-7z\"/>",
2729
2851
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
2730
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\"/>",
@@ -2738,11 +2860,11 @@ var tr = {
2738
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\"/>"
2739
2861
  };
2740
2862
  function Q(e) {
2741
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${tr[e]}</svg>`;
2863
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${pr[e]}</svg>`;
2742
2864
  }
2743
2865
  //#endregion
2744
2866
  //#region src/ui/controls.ts
2745
- var nr = 3e3, rr = 250, ir = /* @__PURE__ */ new Set([
2867
+ var mr = 3e3, hr = 250, gr = /* @__PURE__ */ new Set([
2746
2868
  "GONE",
2747
2869
  "NOT_FOUND",
2748
2870
  "GEO_DENIED",
@@ -2750,15 +2872,15 @@ var nr = 3e3, rr = 250, ir = /* @__PURE__ */ new Set([
2750
2872
  "UNAUTHORIZED",
2751
2873
  "DRM_DEVICE_REVOKED"
2752
2874
  ]);
2753
- function ar(e) {
2875
+ function _r(e) {
2754
2876
  let t = "This match isn't available in your country";
2755
2877
  return e ? `${t} (${e})` : t;
2756
2878
  }
2757
- var or = "This device can't play protected video";
2758
- function sr(e) {
2759
- return new cr(e);
2879
+ var vr = "This device can't play protected video";
2880
+ function yr(e) {
2881
+ return new br(e);
2760
2882
  }
2761
- var cr = class {
2883
+ var br = class {
2762
2884
  root;
2763
2885
  video;
2764
2886
  player;
@@ -2806,7 +2928,7 @@ var cr = class {
2806
2928
  scrubbing = !1;
2807
2929
  offFns = [];
2808
2930
  constructor(e) {
2809
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? nr, 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, er(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");
2810
2932
  let t = (e, t, n) => {
2811
2933
  let r = this.doc.createElement(e);
2812
2934
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -2892,7 +3014,7 @@ var cr = class {
2892
3014
  let t = this.surfaceWasHidden;
2893
3015
  this.tapTimer = setTimeout(() => {
2894
3016
  this.tapTimer = void 0, this.toggleBar(t);
2895
- }, rr);
3017
+ }, hr);
2896
3018
  }
2897
3019
  toggleBar(e) {
2898
3020
  e ? this.poke() : this.hideIfIdle();
@@ -2912,7 +3034,7 @@ var cr = class {
2912
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);
2913
3035
  }
2914
3036
  statusMessageFor(e) {
2915
- 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" ? ar(this.lastError.country) : this.lastError?.code === "DRM_DEVICE_REVOKED" ? or : 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;
2916
3038
  }
2917
3039
  renderStatus(e) {
2918
3040
  let t = this.statusMessageFor(e);
@@ -2927,7 +3049,7 @@ var cr = class {
2927
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);
2928
3050
  } else {
2929
3051
  let r = this.doc.createElement("div");
2930
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !ir.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)) {
2931
3053
  let e = this.doc.createElement("button");
2932
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);
2933
3055
  }
@@ -2968,7 +3090,7 @@ var cr = class {
2968
3090
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
2969
3091
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
2970
3092
  }
2971
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${lr(this.video.currentTime)} / ${lr(this.video.duration)}`);
3093
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${xr(this.video.currentTime)} / ${xr(this.video.duration)}`);
2972
3094
  }
2973
3095
  syncLive() {
2974
3096
  let e = this.player.getStats(), t = e.isLive !== !1, n = this.player.state === "ended", r = !t && !this.streamEnded();
@@ -3018,17 +3140,17 @@ var cr = class {
3018
3140
  renderQualityMenu() {
3019
3141
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
3020
3142
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
3021
- for (let n of e) this.menu.append(this.menuItem(ur(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)));
3022
3144
  }
3023
3145
  renderAudioMenu() {
3024
3146
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
3025
3147
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
3026
- for (let t of e) this.menu.append(this.menuItem(dr(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)));
3027
3149
  }
3028
3150
  renderCcMenu() {
3029
3151
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
3030
3152
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
3031
- for (let t of e) this.menu.append(this.menuItem(fr(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)));
3032
3154
  }
3033
3155
  onTracksChanged() {
3034
3156
  this.openMenu && this.renderMenu(this.openMenu);
@@ -3133,41 +3255,41 @@ var cr = class {
3133
3255
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
3134
3256
  }
3135
3257
  };
3136
- function lr(e) {
3258
+ function xr(e) {
3137
3259
  (!Number.isFinite(e) || e < 0) && (e = 0);
3138
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");
3139
3261
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
3140
3262
  }
3141
- function ur(e) {
3263
+ function Sr(e) {
3142
3264
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
3143
3265
  }
3144
- function dr(e) {
3266
+ function Cr(e) {
3145
3267
  return e.name || e.lang || `Track ${e.id}`;
3146
3268
  }
3147
- function fr(e) {
3269
+ function wr(e) {
3148
3270
  return e.name || e.lang || `Track ${e.id}`;
3149
3271
  }
3150
3272
  //#endregion
3151
3273
  //#region src/managed/index.ts
3152
- var pr = 6e3, mr = 45e3, hr = 2e3, gr = 3, _r = 5e3, vr = 2e3, yr = 48e4, br = "keySystemLicenseRequestFailed";
3153
- function xr(e) {
3274
+ var Tr = 6e3, Er = 45e3, Dr = 2e3, Or = 3, kr = 5e3, Ar = 2e3, jr = 48e4, Mr = "keySystemLicenseRequestFailed";
3275
+ function Nr(e) {
3154
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)})`);
3155
3277
  if (e.env !== void 0) return at(e.env);
3156
3278
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
3157
3279
  return e.baseUrl;
3158
3280
  }
3159
- async function Sr(e) {
3281
+ async function Pr(e) {
3160
3282
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
3161
- let t = xr(e);
3283
+ let t = Nr(e);
3162
3284
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
3163
3285
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
3164
- let r = new Cr({
3286
+ let r = new Fr({
3165
3287
  ...e,
3166
3288
  baseUrl: t
3167
3289
  });
3168
3290
  return await r.start(), r;
3169
3291
  }
3170
- var Cr = class {
3292
+ var Fr = class {
3171
3293
  opts;
3172
3294
  abort = new AbortController();
3173
3295
  listeners = {};
@@ -3262,7 +3384,7 @@ var Cr = class {
3262
3384
  setTimeout(() => {
3263
3385
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
3264
3386
  phase: i ? "unavailable" : "tooEarly",
3265
- retryInMs: mr,
3387
+ retryInMs: Er,
3266
3388
  attempt: 1,
3267
3389
  elapsedMs: 0,
3268
3390
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -3301,13 +3423,13 @@ var Cr = class {
3301
3423
  !a && !p() && h();
3302
3424
  }, e);
3303
3425
  };
3304
- _(pr), this.armingSub = E({
3426
+ _(Tr), this.armingSub = E({
3305
3427
  eventsBaseUrl: t,
3306
3428
  matchUrn: this.opts.matchUrn,
3307
3429
  credential: this.opts.credential,
3308
- onAlive: () => _(mr),
3430
+ onAlive: () => _(Er),
3309
3431
  onState: (e) => {
3310
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * hr);
3432
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Dr);
3311
3433
  },
3312
3434
  onError: () => h()
3313
3435
  });
@@ -3330,7 +3452,7 @@ var Cr = class {
3330
3452
  let n = await c(this.opts.credential);
3331
3453
  if (this.destroyed || t !== this.loadGen) return;
3332
3454
  this.setState("loading");
3333
- 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) => {
3334
3456
  this._state !== "ended" && this._state !== "error" && this.setState(e);
3335
3457
  });
3336
3458
  if (await i.load(), this.destroyed || t !== this.loadGen) {
@@ -3352,26 +3474,26 @@ var Cr = class {
3352
3474
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
3353
3475
  let i = e.analytics?.sid;
3354
3476
  if (!i) return;
3355
- let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = Bn(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);
3356
3478
  if (!o) {
3357
3479
  s && setTimeout(() => {
3358
3480
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, s));
3359
3481
  }, 0);
3360
3482
  return;
3361
3483
  }
3362
- let c = this.opts.video, l = new Vn({
3484
+ let c = this.opts.video, l = new $n({
3363
3485
  endpoint: o,
3364
3486
  sid: i,
3365
3487
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
3366
- sdkVersion: qn,
3367
- environment: Gn(c),
3488
+ sdkVersion: ar,
3489
+ environment: rr(c),
3368
3490
  idleMs: typeof this.opts.analytics == "object" ? this.opts.analytics.idleMs : void 0,
3369
3491
  onIdle: () => this.onAnalyticsIdle(t),
3370
3492
  onBlocked: (e) => setTimeout(() => {
3371
3493
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, e));
3372
3494
  }, 0),
3373
3495
  sample: () => {
3374
- 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;
3375
3497
  return {
3376
3498
  ...e,
3377
3499
  bufferedMs: Math.round(t * 1e3),
@@ -3385,7 +3507,7 @@ var Cr = class {
3385
3507
  let u = () => l.noteSeek(), d = () => l.noteVolume(c.muted, c.volume);
3386
3508
  c.addEventListener("seeking", u), c.addEventListener("volumechange", d), this.detachMediaListeners = () => {
3387
3509
  c.removeEventListener("seeking", u), c.removeEventListener("volumechange", d);
3388
- }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), Kn().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");
3389
3511
  }
3390
3512
  currentPresentation() {
3391
3513
  let e = this.opts.video;
@@ -3406,6 +3528,8 @@ var Cr = class {
3406
3528
  snapToLiveOnRefocus: this.opts.snapToLiveOnRefocus,
3407
3529
  rememberBandwidth: this.opts.rememberBandwidth,
3408
3530
  sizeCapHeadroom: this.opts.sizeCapHeadroom,
3531
+ adaptiveHoldBack: this.opts.adaptiveHoldBack,
3532
+ adaptiveHoldBackCeiling: this.opts.adaptiveHoldBackCeiling,
3409
3533
  mediaDuringKey: this.opts.mediaDuringKey,
3410
3534
  debug: this.opts.debug,
3411
3535
  deferLoad: r,
@@ -3413,11 +3537,11 @@ var Cr = class {
3413
3537
  emit: (e) => this.onEngineEvent(e),
3414
3538
  freshLicenseUrl: () => this.refreshLicense()
3415
3539
  }, o = this.opts.engine ?? "auto";
3416
- if (o === "native") return new W(a);
3417
- if (o === "hls") return new Cn(a);
3418
- 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);
3419
- if (It()) return new Cn(a);
3420
- 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);
3421
3545
  throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
3422
3546
  }
3423
3547
  onEngineEvent(e) {
@@ -3437,6 +3561,9 @@ var Cr = class {
3437
3561
  case "lowLatencyDetected":
3438
3562
  this._descriptor && (this._descriptor.isLowLatency = e.value);
3439
3563
  break;
3564
+ case "livetargetchange":
3565
+ this.emit("latencytargetchange", e.change);
3566
+ break;
3440
3567
  case "qualitychange":
3441
3568
  this.emit("qualitychange", e.index);
3442
3569
  break;
@@ -3479,7 +3606,7 @@ var Cr = class {
3479
3606
  r = 0, i = !1;
3480
3607
  },
3481
3608
  onError: () => {
3482
- this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= gr && (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.`))));
3483
3610
  }
3484
3611
  });
3485
3612
  }
@@ -3493,7 +3620,7 @@ var Cr = class {
3493
3620
  return;
3494
3621
  }
3495
3622
  let e = this.hiddenAtMs;
3496
- this.hiddenAtMs = null, e != null && Date.now() - e >= _r && this.resyncLiveState();
3623
+ this.hiddenAtMs = null, e != null && Date.now() - e >= kr && this.resyncLiveState();
3497
3624
  }, this.onReturnPageShow = (e) => {
3498
3625
  e.persisted && this.resyncLiveState();
3499
3626
  }, document.addEventListener("visibilitychange", this.onReturnVisible), typeof window < "u" && window.addEventListener("pageshow", this.onReturnPageShow));
@@ -3525,7 +3652,7 @@ var Cr = class {
3525
3652
  scheduleRejoin(e) {
3526
3653
  this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
3527
3654
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
3528
- }, Math.random() * hr));
3655
+ }, Math.random() * Dr));
3529
3656
  }
3530
3657
  cancelRejoin() {
3531
3658
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -3549,7 +3676,7 @@ var Cr = class {
3549
3676
  }
3550
3677
  t.controls = !1;
3551
3678
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
3552
- r && r.insertBefore(n, t), n.appendChild(t), Xn(n, this.opts.theme), this.container = n, this.controlBar = sr({
3679
+ r && r.insertBefore(n, t), n.appendChild(t), cr(n, this.opts.theme), this.container = n, this.controlBar = yr({
3553
3680
  root: n,
3554
3681
  video: t,
3555
3682
  player: this,
@@ -3587,7 +3714,7 @@ var Cr = class {
3587
3714
  }
3588
3715
  startStats() {
3589
3716
  this.stopStats();
3590
- let e = Math.max(500, this.opts.statsIntervalMs ?? vr);
3717
+ let e = Math.max(500, this.opts.statsIntervalMs ?? Ar);
3591
3718
  this.statsTimer = setInterval(() => {
3592
3719
  if (!this.engine) return;
3593
3720
  let e = this.engine.getStats();
@@ -3598,14 +3725,14 @@ var Cr = class {
3598
3725
  this.statsTimer && clearInterval(this.statsTimer), this.statsTimer = void 0;
3599
3726
  }
3600
3727
  isStaleLicenseDenial(e) {
3601
- return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== br || 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);
3602
3729
  }
3603
3730
  scheduleLicenseRefresh() {
3604
3731
  if (this.stopRefresh(), !this._descriptor?.drmEnabled) return;
3605
3732
  typeof document < "u" && this.engine?.name === "hls" && (this.onRefocus = () => {
3606
- 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();
3607
3734
  }, document.addEventListener("visibilitychange", this.onRefocus));
3608
- let e = this.opts.licenseRefreshMs ?? yr;
3735
+ let e = this.opts.licenseRefreshMs ?? jr;
3609
3736
  e && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
3610
3737
  }
3611
3738
  stopRefresh() {
@@ -3662,7 +3789,7 @@ var Cr = class {
3662
3789
  this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
3663
3790
  }
3664
3791
  getStats() {
3665
- return this.engine?.getStats() ?? z;
3792
+ return this.engine?.getStats() ?? ot;
3666
3793
  }
3667
3794
  getQualityLevels() {
3668
3795
  return this.engine?.getQualityLevels() ?? [];
@@ -3775,7 +3902,7 @@ function $(e) {
3775
3902
  }
3776
3903
  //#endregion
3777
3904
  //#region src/embed/host.ts
3778
- function wr(e) {
3905
+ function Ir(e) {
3779
3906
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
3780
3907
  if (!e.src) throw Error("mountEmbed: opts.src is required");
3781
3908
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -3834,15 +3961,15 @@ function wr(e) {
3834
3961
  }
3835
3962
  //#endregion
3836
3963
  //#region src/embed/page.ts
3837
- var Tr = /^[A-Za-z0-9:_.-]{1,128}$/;
3838
- function Er(e) {
3964
+ var Lr = /^[A-Za-z0-9:_.-]{1,128}$/;
3965
+ function Rr(e) {
3839
3966
  if (typeof e == "boolean") return e;
3840
3967
  if (typeof e == "object" && e) {
3841
3968
  let t = e.endpoint;
3842
3969
  return typeof t == "string" ? { endpoint: t } : {};
3843
3970
  }
3844
3971
  }
3845
- function Dr() {
3972
+ function zr() {
3846
3973
  let e = window.HAVIK_EMBED_CONFIG;
3847
3974
  if (e) return {
3848
3975
  baseUrl: e.baseUrl ?? "",
@@ -3852,7 +3979,7 @@ function Dr() {
3852
3979
  autoplay: e.autoplay ?? !0,
3853
3980
  muted: e.muted ?? !0,
3854
3981
  waitForLive: e.waitForLive ?? !0,
3855
- analytics: Er(e.analytics)
3982
+ analytics: Rr(e.analytics)
3856
3983
  };
3857
3984
  let t = new URLSearchParams(location.search);
3858
3985
  return {
@@ -3865,9 +3992,9 @@ function Dr() {
3865
3992
  waitForLive: !0
3866
3993
  };
3867
3994
  }
3868
- async function Or() {
3869
- let e = Dr();
3870
- Ar();
3995
+ async function Br() {
3996
+ let e = zr();
3997
+ Hr();
3871
3998
  let t = document.createElement("video");
3872
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);
3873
4000
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -3923,12 +4050,12 @@ async function Or() {
3923
4050
  message: e.message,
3924
4051
  ...e.country === void 0 ? {} : { country: e.country }
3925
4052
  })), e.on("autoplayblocked", () => {
3926
- i({ type: "oddin:autoplayblocked" }), kr(t, e);
4053
+ i({ type: "oddin:autoplayblocked" }), Vr(t, e);
3927
4054
  });
3928
4055
  }, s = async (r) => {
3929
4056
  a?.destroy(), a = null;
3930
4057
  try {
3931
- let n = await Sr({
4058
+ let n = await Pr({
3932
4059
  video: t,
3933
4060
  baseUrl: e.baseUrl,
3934
4061
  matchUrn: r,
@@ -3991,7 +4118,7 @@ async function Or() {
3991
4118
  a?.retry();
3992
4119
  break;
3993
4120
  case "oddin:load":
3994
- typeof e.matchUrn == "string" && Tr.test(e.matchUrn) && s(e.matchUrn);
4121
+ typeof e.matchUrn == "string" && Lr.test(e.matchUrn) && s(e.matchUrn);
3995
4122
  break;
3996
4123
  case "oddin:destroy": c();
3997
4124
  }
@@ -4003,18 +4130,18 @@ async function Or() {
4003
4130
  protocol: 1
4004
4131
  });
4005
4132
  }
4006
- function kr(e, t) {
4133
+ function Vr(e, t) {
4007
4134
  if (document.getElementById("havik-tap")) return;
4008
4135
  let n = document.createElement("button");
4009
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", () => {
4010
4137
  n.remove(), t.play();
4011
4138
  }), document.body.appendChild(n);
4012
4139
  }
4013
- function Ar() {
4140
+ function Hr() {
4014
4141
  let e = document.createElement("style");
4015
4142
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
4016
4143
  }
4017
4144
  //#endregion
4018
- export { Jn as ODDIN_THEME, n as PlaybackError, Xn as applyTheme, at as baseUrlForEnv, Or as bootEmbed, Ee as clearDeviceId, Re as clearRememberedBandwidth, Sr 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, wr 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 };
4019
4146
 
4020
4147
  //# sourceMappingURL=index.js.map