@oddin-gg/havik-player 2.27.2 → 2.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8,8 +8,9 @@ var n = class extends Error {
8
8
  serverCode;
9
9
  liveStartsAtMs;
10
10
  country;
11
+ detail;
11
12
  constructor(e, t, n, r) {
12
- super(n), this.name = "PlaybackError", this.code = e, this.httpStatus = t, this.retryAfterMs = r?.retryAfterMs, this.requestId = r?.requestId, this.serverCode = r?.serverCode, this.liveStartsAtMs = r?.liveStartsAtMs, this.country = r?.country, r?.cause !== void 0 && (this.cause = r.cause);
13
+ super(n), this.name = "PlaybackError", this.code = e, this.httpStatus = t, this.retryAfterMs = r?.retryAfterMs, this.requestId = r?.requestId, this.serverCode = r?.serverCode, this.liveStartsAtMs = r?.liveStartsAtMs, this.country = r?.country, this.detail = r?.detail, r?.cause !== void 0 && (this.cause = r.cause);
13
14
  }
14
15
  };
15
16
  function r(e) {
@@ -1010,8 +1011,8 @@ function vt(e, t) {
1010
1011
  }
1011
1012
  //#endregion
1012
1013
  //#region src/core/engine/native.ts
1013
- var yt = 2e3, bt = 3, z = 500, xt = 6e3, St = .25, Ct = 250, wt = .05, Tt = 4, Et = 12e3, Dt = .25, Ot = 1e3;
1014
- function kt(e) {
1014
+ var yt = 2e3, bt = 3, z = 500, xt = 6e3, St = .25, Ct = 250, wt = .05, Tt = 4, Et = 12e3, Dt = .25, Ot = 1e3, kt = 8e3, At = .05, jt = 2, Mt = 3e4;
1015
+ function Nt(e) {
1015
1016
  try {
1016
1017
  let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
1017
1018
  return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
@@ -1027,7 +1028,7 @@ function V() {
1027
1028
  let e = "WebKitMediaKeys" in window || typeof HTMLMediaElement < "u" && "webkitSetMediaKeys" in HTMLMediaElement.prototype, t = typeof navigator < "u" && /Apple/i.test(navigator.vendor ?? "");
1028
1029
  return e || t;
1029
1030
  }
1030
- function At() {
1031
+ function Pt() {
1031
1032
  if (typeof navigator > "u" || !V()) return !1;
1032
1033
  let e = navigator.userAgent ?? "";
1033
1034
  return /iPhone|iPad|iPod/.test(e) ? !0 : /Macintosh/.test(e) && typeof navigator.maxTouchPoints == "number" && navigator.maxTouchPoints > 1;
@@ -1053,6 +1054,11 @@ var H = class {
1053
1054
  edgeStampedAt = null;
1054
1055
  lastTickAt = null;
1055
1056
  endedEmitted = !1;
1057
+ lastPlayhead = null;
1058
+ playheadStampedAt = null;
1059
+ movingSince = null;
1060
+ stuckReloads = 0;
1061
+ stuckReloadsTotal = 0;
1056
1062
  fairplayKeyIV;
1057
1063
  loadStarted;
1058
1064
  preloadTouched = !1;
@@ -1083,7 +1089,7 @@ var H = class {
1083
1089
  type: "error",
1084
1090
  error: L() ? new n("INTERNAL", 0, "native HLS playback error") : new n("NETWORK", 0, "native HLS playback error while offline"),
1085
1091
  fatal: !0
1086
- }), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = Mt(t.manifestUrl, t.analytics?.sid), e.load();
1092
+ }), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = It(t.manifestUrl, t.analytics?.sid), e.load();
1087
1093
  }
1088
1094
  startLoad() {
1089
1095
  this.loadStarted || (this.loadStarted = !0, this.restorePreload());
@@ -1111,7 +1117,7 @@ var H = class {
1111
1117
  };
1112
1118
  this.onEncrypted = (n) => {
1113
1119
  let a = n;
1114
- a.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = kt(a.initData), (async () => {
1120
+ a.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = Nt(a.initData), (async () => {
1115
1121
  try {
1116
1122
  let n = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
1117
1123
  initDataTypes: [a.initDataType],
@@ -1211,7 +1217,7 @@ var H = class {
1211
1217
  droppedFrames: t.dropped,
1212
1218
  decodedFrames: t.totalFrames
1213
1219
  };
1214
- if (this.cdnHost ||= mt(this.builtAt), this.cdnHost && (n.cdnHost = this.cdnHost), this.deps.descriptor.drmEnabled && (n.keySystem = "fairplay"), e.duration === Infinity) {
1220
+ if (this.cdnHost ||= mt(this.builtAt), this.cdnHost && (n.cdnHost = this.cdnHost), this.deps.descriptor.drmEnabled && (n.keySystem = "fairplay"), n.stuckReloads = this.stuckReloadsTotal, e.duration === Infinity) {
1215
1221
  n.isLive = !0;
1216
1222
  let t = this.liveEdgeEstimate(), r = t == null ? NaN : t - e.currentTime;
1217
1223
  Number.isFinite(r) && (n.atLiveEdge = r <= Tt, !e.paused && r >= 0 && (n.latencySeconds = r));
@@ -1258,18 +1264,17 @@ var H = class {
1258
1264
  if (this.edgeTimer != null) return;
1259
1265
  let { video: e } = this.deps;
1260
1266
  this.onEdgeReset = (e) => {
1261
- this.lastBufferedEnd = null, this.bufferedAdvancedAt = null, e.type === "emptied" && this.resetEndStale();
1267
+ this.lastBufferedEnd = null, this.bufferedAdvancedAt = null, this.resetStuck(), e.type === "emptied" && this.resetEndStale();
1262
1268
  }, e.addEventListener("seeking", this.onEdgeReset), e.addEventListener("emptied", this.onEdgeReset), this.edgeTimer = setInterval(() => {
1263
1269
  if (e.duration !== Infinity) return;
1264
- this.checkEndStale();
1265
- let t = jt(e, e.currentTime);
1266
- t != null && ((this.lastBufferedEnd == null || Math.abs(t - this.lastBufferedEnd) >= wt) && (this.bufferedAdvancedAt = U()), this.lastBufferedEnd = t);
1270
+ let t = U(), n = this.lastTickAt == null ? 0 : t - this.lastTickAt;
1271
+ this.lastTickAt = t;
1272
+ let r = Ft(e, e.currentTime);
1273
+ r != null && ((this.lastBufferedEnd == null || Math.abs(r - this.lastBufferedEnd) >= wt) && (this.bufferedAdvancedAt = t), this.lastBufferedEnd = r), this.checkEndStale(t, n), this.checkStuck(t, n);
1267
1274
  }, Ct);
1268
1275
  }
1269
- checkEndStale() {
1276
+ checkEndStale(e, t) {
1270
1277
  if (this.endedEmitted) return;
1271
- let e = U(), t = this.lastTickAt == null ? 0 : e - this.lastTickAt;
1272
- this.lastTickAt = e;
1273
1278
  let n = this.liveEnd();
1274
1279
  if (n == null) return;
1275
1280
  let r = this.lastSeekableEnd == null || Math.abs(n - this.lastSeekableEnd) >= Dt;
@@ -1287,13 +1292,44 @@ var H = class {
1287
1292
  resetEndStale() {
1288
1293
  this.lastSeekableEnd = null, this.edgeStampedAt = null, this.lastTickAt = null;
1289
1294
  }
1295
+ checkStuck(e, t) {
1296
+ if (this.deps.stuckReload === !1 || this.endedEmitted) return;
1297
+ let n = this.deps.video, r = n.currentTime, i = this.lastPlayhead == null || !Number.isFinite(r) || Math.abs(r - this.lastPlayhead) >= At;
1298
+ this.lastPlayhead = Number.isFinite(r) ? r : null;
1299
+ let a = typeof document < "u" && document.visibilityState === "hidden", o = t > Ot || a || n.paused || n.seeking || n.readyState < HTMLMediaElement.HAVE_CURRENT_DATA || !L();
1300
+ if (i || o) {
1301
+ this.playheadStampedAt = e, i && !o ? (this.movingSince ??= e, this.stuckReloads > 0 && e - this.movingSince >= Mt && (this.stuckReloads = 0)) : this.movingSince = null;
1302
+ return;
1303
+ }
1304
+ this.movingSince = null;
1305
+ let s = e - (this.playheadStampedAt ?? e), c = this.bufferedAdvancedAt == null ? Infinity : e - this.bufferedAdvancedAt;
1306
+ s <= kt || c <= kt || (this.playheadStampedAt = e, !(this.stuckReloads >= jt) && this.reloadStuck());
1307
+ }
1308
+ resetStuck() {
1309
+ this.lastPlayhead = null, this.playheadStampedAt = null, this.movingSince = null;
1310
+ }
1311
+ reloadStuck() {
1312
+ let { video: e, descriptor: t } = this.deps;
1313
+ this.stuckReloads += 1, this.stuckReloadsTotal += 1;
1314
+ let n = !e.paused;
1315
+ console.warn("havik-player: native playback frozen, buffer not growing: reloading at live");
1316
+ try {
1317
+ e.src = It(t.manifestUrl, t.analytics?.sid), e.load();
1318
+ } catch (e) {
1319
+ console.warn("havik-player: reload failed", e);
1320
+ return;
1321
+ }
1322
+ n && Promise.resolve(e.play()).catch((e) => {
1323
+ console.warn("havik-player: play() after the reload was refused", e);
1324
+ });
1325
+ }
1290
1326
  unwatchBufferedEnd() {
1291
- this.edgeTimer != null && clearInterval(this.edgeTimer), this.edgeTimer = null, this.onEdgeReset &&= (this.deps.video.removeEventListener("seeking", this.onEdgeReset), this.deps.video.removeEventListener("emptied", this.onEdgeReset), null), this.resetEndStale(), this.lastBufferedEnd = null, this.bufferedAdvancedAt = null;
1327
+ this.edgeTimer != null && clearInterval(this.edgeTimer), this.edgeTimer = null, this.onEdgeReset &&= (this.deps.video.removeEventListener("seeking", this.onEdgeReset), this.deps.video.removeEventListener("emptied", this.onEdgeReset), null), this.resetEndStale(), this.resetStuck(), this.lastBufferedEnd = null, this.bufferedAdvancedAt = null;
1292
1328
  }
1293
1329
  liveEdgeEstimate() {
1294
1330
  let e = this.deps.video;
1295
1331
  if (e.duration !== Infinity) return null;
1296
- let t = this.liveEnd(), n = jt(e, e.currentTime) ?? this.lastBufferedEnd;
1332
+ let t = this.liveEnd(), n = Ft(e, e.currentTime) ?? this.lastBufferedEnd;
1297
1333
  if (n != null) {
1298
1334
  let e = this.bufferedAdvancedAt, r = n + (e == null ? 0 : Math.max(0, (U() - e) / 1e3));
1299
1335
  (t == null || r > t) && (t = r);
@@ -1330,7 +1366,7 @@ var H = class {
1330
1366
  function U() {
1331
1367
  return typeof performance < "u" ? performance.now() : Date.now();
1332
1368
  }
1333
- function jt(e, t) {
1369
+ function Ft(e, t) {
1334
1370
  let n = e.buffered;
1335
1371
  if (!n || !Number.isFinite(t)) return null;
1336
1372
  for (let e = 0; e < n.length; e += 1) if (n.start(e) <= t + .1 && n.end(e) > t) {
@@ -1339,7 +1375,7 @@ function jt(e, t) {
1339
1375
  }
1340
1376
  return null;
1341
1377
  }
1342
- function Mt(e, t) {
1378
+ function It(e, t) {
1343
1379
  return t ? `${e}${e.includes("?") ? "&" : "?"}CMCD=${encodeURIComponent(`sid="${t}"`)}` : e;
1344
1380
  }
1345
1381
  //#endregion
@@ -1356,13 +1392,13 @@ function W(e, t = Date.now()) {
1356
1392
  }
1357
1393
  //#endregion
1358
1394
  //#region src/core/drm/xhrBody.ts
1359
- var Nt = 4096;
1360
- function Pt(e) {
1395
+ var Lt = 4096;
1396
+ function Rt(e) {
1361
1397
  if (!e || typeof e != "object") return;
1362
1398
  let { response: t } = e;
1363
1399
  if (typeof t == "string") return t;
1364
1400
  let n;
1365
- 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 > Nt)) try {
1401
+ 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 > Lt)) try {
1366
1402
  return new TextDecoder().decode(n);
1367
1403
  } catch {
1368
1404
  return;
@@ -1370,7 +1406,7 @@ function Pt(e) {
1370
1406
  }
1371
1407
  //#endregion
1372
1408
  //#region src/core/engine/prefetch.ts
1373
- var Ft = 15e3, It = 5e3, Lt = 6, Rt = 4e3, zt = 2;
1409
+ var zt = 15e3, Bt = 5e3, Vt = 6, Ht = 4e3, Ut = 2;
1374
1410
  function G(e) {
1375
1411
  try {
1376
1412
  let t = new URL(e);
@@ -1379,7 +1415,7 @@ function G(e) {
1379
1415
  return e;
1380
1416
  }
1381
1417
  }
1382
- var Bt = class {
1418
+ var Wt = class {
1383
1419
  cmcd;
1384
1420
  entries = /* @__PURE__ */ new Map();
1385
1421
  keyed = /* @__PURE__ */ new Set();
@@ -1411,17 +1447,17 @@ var Bt = class {
1411
1447
  let r = G(e);
1412
1448
  if (this.entries.has(r)) return;
1413
1449
  let i = this.perTrack.get(t) ?? 0;
1414
- i >= Lt || (this.perTrack.set(t, i + 1), this.start(r, this.withCmcd(e, t, n), !1, Ft), this.stats.started += 1);
1450
+ i >= Vt || (this.perTrack.set(t, i + 1), this.start(r, this.withCmcd(e, t, n), !1, zt), this.stats.started += 1);
1415
1451
  }
1416
1452
  prefetchHint(e, t) {
1417
1453
  let n = G(e);
1418
1454
  if (this.entries.has(n)) return;
1419
1455
  let r = this.hints.get(t) ?? [];
1420
- for (; r.length >= zt;) {
1456
+ for (; r.length >= Ut;) {
1421
1457
  let e = r.shift();
1422
1458
  e !== void 0 && this.drop(e) && (this.stats.hintDropped += 1);
1423
1459
  }
1424
- r.push(n), this.hints.set(t, r), this.start(n, this.withCmcd(e, t, "media"), !0, Rt), this.stats.hintStarted += 1;
1460
+ r.push(n), this.hints.set(t, r), this.start(n, this.withCmcd(e, t, "media"), !0, Ht), this.stats.hintStarted += 1;
1425
1461
  }
1426
1462
  dropHints(e) {
1427
1463
  for (let t of this.hints.get(e) ?? []) this.drop(t) && (this.stats.hintDropped += 1);
@@ -1474,14 +1510,14 @@ var Bt = class {
1474
1510
  }
1475
1511
  }
1476
1512
  };
1477
- function Vt(t, n) {
1513
+ function Gt(t, n) {
1478
1514
  let r = typeof n == "function" ? n : e.DefaultConfig?.loader;
1479
1515
  if (typeof r != "function") return;
1480
1516
  class i extends r {
1481
1517
  served = !1;
1482
1518
  cancelled = !1;
1483
1519
  entry;
1484
- stats = Ht();
1520
+ stats = Kt();
1485
1521
  load(e, n, r) {
1486
1522
  let i = e.rangeEnd ? void 0 : t.take(e.url);
1487
1523
  if (!i) {
@@ -1493,7 +1529,7 @@ function Vt(t, n) {
1493
1529
  i.hint ? t.stats.hintFailed += 1 : t.stats.failed += 1;
1494
1530
  }, s = () => {
1495
1531
  !this.cancelled && this.served && (i.abort.abort(), o(), this.served = !1, this.entry = void 0, super.load(e, n, r));
1496
- }, c = n?.loadPolicy, l = c?.maxTimeToFirstByteMs || It, u = setTimeout(s, l);
1532
+ }, c = n?.loadPolicy, l = c?.maxTimeToFirstByteMs || Bt, u = setTimeout(s, l);
1497
1533
  i.headers.then(() => {
1498
1534
  clearTimeout(u);
1499
1535
  let e = c?.maxLoadTimeMs;
@@ -1526,7 +1562,7 @@ function Vt(t, n) {
1526
1562
  }
1527
1563
  return i;
1528
1564
  }
1529
- function Ht() {
1565
+ function Kt() {
1530
1566
  return {
1531
1567
  aborted: !1,
1532
1568
  loaded: 0,
@@ -1550,10 +1586,10 @@ function Ht() {
1550
1586
  }
1551
1587
  };
1552
1588
  }
1553
- function Ut(e) {
1589
+ function qt(e) {
1554
1590
  return e === "audio" ? "audio" : "main";
1555
1591
  }
1556
- function Wt(e, t) {
1592
+ function Jt(e, t) {
1557
1593
  let n = [];
1558
1594
  t.initSegment?.url && !t.initSegment.data && n.push({
1559
1595
  url: t.initSegment.url,
@@ -1570,7 +1606,7 @@ function Wt(e, t) {
1570
1606
  });
1571
1607
  return n;
1572
1608
  }
1573
- function Gt(e) {
1609
+ function Yt(e) {
1574
1610
  let t = e, n = t?.preloadHint;
1575
1611
  if (!n || !t?.url || n.TYPE !== "PART" || n["BYTERANGE-START"] !== void 0 || n["BYTERANGE-LENGTH"] !== void 0) return null;
1576
1612
  let r = n.URI;
@@ -1588,15 +1624,15 @@ function K(e, t) {
1588
1624
  }
1589
1625
  //#endregion
1590
1626
  //#region src/core/engine/hls.ts
1591
- function Kt() {
1627
+ function Xt() {
1592
1628
  return e.isSupported();
1593
1629
  }
1594
- var qt = 3, Jt = 1e6, Yt = 20, Xt = 1e4, Zt = 3, Qt = 5, $t = 3, en = 1e4, tn = 3e4, nn = 12e3, rn = 3e3, an = .02, on = 6e3, sn = .5, cn = 3, ln = .5, un = 1.1, dn = .25, fn = 5e3, pn = 4e3, mn = 2, hn = 5, gn = 2, _n = 2, vn = 4, yn = 4, bn = 2e3, xn = 3e4, Sn = 1, Cn = 2, wn = .5, Tn = .6, En = 2, Dn = 1.5, On = 1e4, kn = 18e4, An = .5, jn = 2;
1595
- function Mn(e) {
1630
+ var Zt = 3, Qt = 1e6, $t = 20, en = 1e4, tn = 3, nn = 2, rn = 5, an = 3, on = 1e4, sn = 3e4, cn = 12e3, ln = 3e3, un = .02, dn = 6e3, fn = .5, pn = 3, mn = .5, hn = 1.1, gn = .25, _n = 5e3, vn = 4e3, yn = 2, bn = 5, xn = 2, Sn = 2, Cn = 4, wn = 4, Tn = 2e3, En = 3e4, Dn = 1, On = 2, kn = .5, An = .6, jn = 2, Mn = 1.5, Nn = 1e4, Pn = 18e4, Fn = .5, In = 2;
1631
+ function Ln(e) {
1596
1632
  let t = e.config ?? {};
1597
1633
  return t.capLevelToPlayerSize === !0 || t.capLevelOnFPSDrop === !0;
1598
1634
  }
1599
- var q = 8, Nn = 12e4, Pn = 6e4;
1635
+ var q = 8, Rn = 12e4, zn = 6e4;
1600
1636
  function J(e, t, n) {
1601
1637
  if (n <= 0 || t < 0 || t >= e.length) return t;
1602
1638
  let r = (t, n) => e[t].width === e[n].width && e[t].height === e[n].height, i = (t) => {
@@ -1609,11 +1645,11 @@ function J(e, t, n) {
1609
1645
  function Y() {
1610
1646
  return typeof self < "u" && self.devicePixelRatio > 0 ? self.devicePixelRatio : 1;
1611
1647
  }
1612
- var Fn = .2, In = 30, Ln = 2, Rn = 2, zn = 2, Bn = 9, Vn = .5, Hn = 64, Un = 1e4, Wn = 8, Gn = .6, Kn = .4, qn = .4, Jn = 2, Yn = 6e4, X = .5, Xn = 3.5, Zn = 300;
1613
- function Qn(e) {
1648
+ var Bn = .2, Vn = 30, Hn = 2, Un = 2, Wn = 2, Gn = 9, Kn = .5, qn = 64, Jn = 1e4, Yn = 8, Xn = .6, Zn = .4, Qn = .4, $n = 2, er = 6e4, X = .5, tr = 3.5, nr = 300;
1649
+ function rr(e) {
1614
1650
  return (e?.part ?? e?.frag)?.stats;
1615
1651
  }
1616
- function $n() {
1652
+ function ir() {
1617
1653
  if (typeof navigator > "u") return 0;
1618
1654
  try {
1619
1655
  let e = navigator.connection?.rtt;
@@ -1622,20 +1658,20 @@ function $n() {
1622
1658
  return 0;
1623
1659
  }
1624
1660
  }
1625
- function er(e) {
1661
+ function ar(e) {
1626
1662
  if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
1627
1663
  delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
1628
1664
  return;
1629
1665
  }
1630
- e.liveSyncDuration ??= zn;
1631
- let t = Math.max(12, e.liveSyncDuration + Bn);
1632
- (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), tr(e, e.liveSyncDuration);
1666
+ e.liveSyncDuration ??= Wn;
1667
+ let t = Math.max(12, e.liveSyncDuration + Gn);
1668
+ (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), or(e, e.liveSyncDuration);
1633
1669
  }
1634
- function tr(e, t) {
1670
+ function or(e, t) {
1635
1671
  let n = (e, t, n) => Math.min(n, Math.max(t, e));
1636
1672
  e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
1637
1673
  }
1638
- var nr = class {
1674
+ var sr = class {
1639
1675
  name = "hls";
1640
1676
  hls = null;
1641
1677
  deps;
@@ -1660,6 +1696,7 @@ var nr = class {
1660
1696
  ladderHeights;
1661
1697
  ladderTopBitrateKbps = 0;
1662
1698
  mediaRecoveries = 0;
1699
+ holeKicks = 0;
1663
1700
  networkRecoveries = 0;
1664
1701
  licenseRecoveries = 0;
1665
1702
  recoveryResetTimer = null;
@@ -1788,7 +1825,7 @@ var nr = class {
1788
1825
  }
1789
1826
  this.loadSuspended = !0;
1790
1827
  }
1791
- }, fn));
1828
+ }, _n));
1792
1829
  }
1793
1830
  resumeLoading() {
1794
1831
  if (this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.resetChaseWindow(), this.resetFrameDropWindow(), this.loadSuspended && (this.loadSuspended = !1, !this.stopped && this.hls)) {
@@ -1820,7 +1857,7 @@ var nr = class {
1820
1857
  let t = e.liveSyncPosition;
1821
1858
  if (typeof t != "number" || !Number.isFinite(t)) return;
1822
1859
  let { video: n } = this.deps;
1823
- n.currentTime <= t + Vn || (n.currentTime = t);
1860
+ n.currentTime <= t + Kn || (n.currentTime = t);
1824
1861
  }
1825
1862
  reanchorPartTimeline(e, t) {
1826
1863
  let { frag: n, part: r } = t;
@@ -1832,7 +1869,7 @@ var nr = class {
1832
1869
  let o = n.sn, s = i.fragments.find((e) => e && e.sn === o) ?? (i.fragmentHint?.sn === o ? i.fragmentHint : void 0);
1833
1870
  if (!s) return;
1834
1871
  let c = a.startPTS - s.start;
1835
- if (!(!Number.isFinite(c) || Math.abs(c) < an)) {
1872
+ if (!(!Number.isFinite(c) || Math.abs(c) < un)) {
1836
1873
  for (let e of i.fragments) e && e.setStart(e.start + c);
1837
1874
  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})`);
1838
1875
  }
@@ -1872,7 +1909,7 @@ var nr = class {
1872
1909
  return;
1873
1910
  }
1874
1911
  let l = this.hls, u, d = new Promise((e) => {
1875
- u = setTimeout(() => e(void 0), en);
1912
+ u = setTimeout(() => e(void 0), on);
1876
1913
  });
1877
1914
  return Promise.race([c().catch(() => void 0), d]).finally(() => clearTimeout(u)).then((e) => {
1878
1915
  if (!this.hls || this.hls !== l) throw Error("havik-player: engine torn down during refresh");
@@ -1890,28 +1927,28 @@ var nr = class {
1890
1927
  this.prefetch?.destroy(), this.prefetch = null;
1891
1928
  let p = !1, m = !1, h = u && this.deps.mediaDuringKey !== !1, g = this.deps.preloadHints !== !1;
1892
1929
  if ((h || g) && !f.fLoader) {
1893
- let e = new Bt({
1930
+ let e = new Wt({
1894
1931
  sessionId: n.analytics?.sid,
1895
1932
  contentId: n.matchUrn
1896
- }), t = Vt(e, f.loader);
1933
+ }), t = Gt(e, f.loader);
1897
1934
  t && (this.prefetch = e, p = h, m = g, f.fLoader = t);
1898
1935
  }
1899
- er(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;
1936
+ ar(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;
1900
1937
  let _ = new e(f);
1901
1938
  if (this.hls = _, this.armHoldBack(_, f), _.on(e.Events.FRAG_PARSED, (e, t) => this.reanchorPartTimeline(_, t)), _.on(e.Events.MANIFEST_PARSED, () => {
1902
1939
  this.mediaRecoveries = 0, this.networkRecoveries = 0, u && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && this.dropLevelsWidevineCannotPlay(_), this.measureLadder(_), this.applySizeCap(), this.loadSuspended = !1, t.paused && this.schedulePauseSuspend(), l({ type: "ready" });
1903
1940
  }), _.on(e.Events.FRAG_BUFFERED, (e, t) => {
1904
- 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(() => {
1905
- this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.starvationRebuilds = 0, this.recoveryResetTimer = null;
1906
- }, tn)));
1941
+ 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.holeKicks > 0) && !this.recoveryResetTimer && (this.recoveryResetTimer = setTimeout(() => {
1942
+ this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.starvationRebuilds = 0, this.holeKicks = 0, this.recoveryResetTimer = null;
1943
+ }, sn)));
1907
1944
  }), _.on(e.Events.FRAG_LOADING, (e, t) => {
1908
1945
  t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
1909
1946
  }), _.on(e.Events.FRAG_LOADED, (e, t) => {
1910
1947
  t?.frag?.type === "main" && (this.mainFragsSinceTick += 1, this.bwFragsLoaded += 1, this.maybeRememberBandwidth(!1));
1911
- let n = t?.frag, r = Qn(t), i = r?.loaded;
1948
+ let n = t?.frag, r = rr(t), i = r?.loaded;
1912
1949
  typeof i == "number" && (this.bytesLoaded += i), !this.reportedFirstBytes && typeof i == "number" && i > 0 && (this.reportedFirstBytes = !0, l({ type: "firstBytes" }));
1913
1950
  let a = r?.loading;
1914
- a && a.end > 0 && a.start > 0 && (this.requestCount += 1, this.requestTimesMs.push(Math.max(0, Math.round(a.end - a.start))), this.requestTimesMs.length > Hn && this.requestTimesMs.shift()), this.sampleHoldBack(t), this.sampleHeadroom(t);
1951
+ a && a.end > 0 && a.start > 0 && (this.requestCount += 1, this.requestTimesMs.push(Math.max(0, Math.round(a.end - a.start))), this.requestTimesMs.length > qn && this.requestTimesMs.shift()), this.sampleHoldBack(t), this.sampleHeadroom(t);
1915
1952
  let o = n?.url;
1916
1953
  if (o) {
1917
1954
  let e = ft(o);
@@ -1931,8 +1968,8 @@ var nr = class {
1931
1968
  let i = e;
1932
1969
  if (!i?.live || !i.partList?.length || typeof i.edge != "number") return;
1933
1970
  let a = n.buffered, o = a.length ? a.end(a.length - 1) : 0;
1934
- if (i.edge - o > qt * (i.partTarget || .5)) return;
1935
- let s = Gt(e);
1971
+ if (i.edge - o > Zt * (i.partTarget || .5)) return;
1972
+ let s = Yt(e);
1936
1973
  s && t.prefetchHint(s, r);
1937
1974
  };
1938
1975
  _.on(e.Events.LEVEL_LOADED, (e, t) => {
@@ -1944,15 +1981,15 @@ var nr = class {
1944
1981
  if (this.prefetch && p) {
1945
1982
  let t = this.prefetch;
1946
1983
  _.on(e.Events.KEY_LOADING, (e, n) => {
1947
- let r = n?.frag, i = Ut(r?.type);
1948
- if (r && t.isActive(i)) for (let e of Wt(_, r)) t.prefetch(e.url, i, e.kind);
1949
- }), _.on(e.Events.KEY_LOADED, (e, n) => t.keyUsable(Ut(n?.frag?.type)));
1984
+ let r = n?.frag, i = qt(r?.type);
1985
+ if (r && t.isActive(i)) for (let e of Jt(_, r)) t.prefetch(e.url, i, e.kind);
1986
+ }), _.on(e.Events.KEY_LOADED, (e, n) => t.keyUsable(qt(n?.frag?.type)));
1950
1987
  }
1951
1988
  _.on(e.Events.KEY_LOADED, () => {
1952
1989
  u && !this.reportedDrmReady && (this.reportedDrmReady = !0, l({ type: "drmReady" }));
1953
1990
  }), this.endWatchdog && clearInterval(this.endWatchdog), this.endWatchdog = setInterval(() => {
1954
1991
  this.checkEndStale(), this.checkStarvation(), this.updateCatchUpBand(), this.checkFutileChase(), this.adaptHoldBack(), this.reviewSizeCapHeadroom(), this.releaseStallCap(), this.checkFrameDrops();
1955
- }, rn), _.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1992
+ }, ln), _.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1956
1993
  this.renditionSwitches += 1;
1957
1994
  let n = this.lastLevelIndex >= 0 ? _.levels[this.lastLevelIndex] : void 0, r = _.levels[t.level];
1958
1995
  n && r && r.bitrate !== n.bitrate && (r.bitrate > n.bitrate ? this.upshifts += 1 : this.downshifts += 1), this.lastLevelIndex = t.level, this.armFrameDropGrace(), l({
@@ -2013,8 +2050,8 @@ var nr = class {
2013
2050
  let e = this.hls, t = this.sizeCapCover();
2014
2051
  if (!e || !t) return null;
2015
2052
  let { cover: n, deviceRatio: r } = t, i = this.deps.sizeCapHeadroom, a = i == null || Number.isNaN(i);
2016
- if (a && r >= Cn) return n;
2017
- if (a) return this.headroomUngated || this.headroomGranted ? J(e.levels, n, Sn) : n;
2053
+ if (a && r >= On) return n;
2054
+ if (a) return this.headroomUngated || this.headroomGranted ? J(e.levels, n, Dn) : n;
2018
2055
  let o = i === Infinity ? e.levels.length : Math.max(0, Math.floor(i));
2019
2056
  return J(e.levels, n, o);
2020
2057
  }
@@ -2031,11 +2068,11 @@ var nr = class {
2031
2068
  return;
2032
2069
  }
2033
2070
  this.capTimer && clearTimeout(this.capTimer);
2034
- let t = e > this.sizeCapLevel ? bn : xn;
2071
+ let t = e > this.sizeCapLevel ? Tn : En;
2035
2072
  this.capTimer = setTimeout(() => {
2036
2073
  this.capTimer = null;
2037
2074
  let n = this.computeSizeCapTarget();
2038
- n === e || t === xn && n != null && n < e ? this.applySizeCap() : this.onMediaResize();
2075
+ n === e || t === En && n != null && n < e ? this.applySizeCap() : this.onMediaResize();
2039
2076
  }, t);
2040
2077
  }
2041
2078
  measureLadder(e) {
@@ -2062,7 +2099,7 @@ var nr = class {
2062
2099
  }
2063
2100
  defaultHeadroomApplies() {
2064
2101
  let e = this.deps.sizeCapHeadroom;
2065
- return e != null && !Number.isNaN(e) || this.headroomUngated ? !1 : Y() < Cn;
2102
+ return e != null && !Number.isNaN(e) || this.headroomUngated ? !1 : Y() < On;
2066
2103
  }
2067
2104
  applySizeCapUnlessPending() {
2068
2105
  this.capTimer || this.applySizeCap();
@@ -2071,7 +2108,7 @@ var nr = class {
2071
2108
  if (!this.sizeCapManaged || !this.defaultHeadroomApplies() || e.frag?.type !== "main" || typeof e.frag.level != "number" || typeof document < "u" && document.hidden) return;
2072
2109
  let t = e.part ? e.part.duration : this.detectedLowLatency ? 0 : e.frag.duration;
2073
2110
  if (typeof t != "number" || !(t > 0)) return;
2074
- let n = Qn(e)?.loading;
2111
+ let n = rr(e)?.loading;
2075
2112
  if (!n || !(n.start > 0) || !(n.end >= n.start)) return;
2076
2113
  let r = this.hls?.levels?.[e.frag.level]?.bitrate;
2077
2114
  if (!(typeof r == "number" && r > 0)) return;
@@ -2084,7 +2121,7 @@ var nr = class {
2084
2121
  }
2085
2122
  onPlaybackStall(e) {
2086
2123
  let t = Date.now();
2087
- this.deps.video.seeking || !this.playingSince || t - this.playingSince < On || (this.lastStallAt = t, this.stepStallCapDown(t, e), this.headroomGranted && (this.headroomGranted = !1, this.applySizeCapUnlessPending()));
2124
+ this.deps.video.seeking || !this.playingSince || t - this.playingSince < Nn || (this.lastStallAt = t, this.stepStallCapDown(t, e), this.headroomGranted && (this.headroomGranted = !1, this.applySizeCapUnlessPending()));
2088
2125
  }
2089
2126
  stallCapLevel(e) {
2090
2127
  if (this.stallCapBitrate == null) return -1;
@@ -2095,7 +2132,7 @@ var nr = class {
2095
2132
  }
2096
2133
  stepStallCapDown(e, t) {
2097
2134
  let n = this.hls;
2098
- if (!n || !this.stallCapEnabled || Mn(n) || !n.autoLevelEnabled || typeof t == "number" && t >= An || this.isLiveEdgeWait(n, t ?? 0)) return;
2135
+ if (!n || !this.stallCapEnabled || Ln(n) || !n.autoLevelEnabled || typeof t == "number" && t >= Fn || this.isLiveEdgeWait(n, t ?? 0)) return;
2099
2136
  this.stallCapBitrate != null && (this.stallCapSince = e);
2100
2137
  let r = Math.max(n.levels[n.currentLevel]?.bitrate ?? 0, n.levels[n.loadLevel]?.bitrate ?? 0);
2101
2138
  if (!(r > 0)) return;
@@ -2106,7 +2143,7 @@ var nr = class {
2106
2143
  let n = e.latestLevelDetails;
2107
2144
  if (!n?.live) return !1;
2108
2145
  let r = n.partTarget || n.targetduration;
2109
- return !(r > 0) || !(n.edge > 0) ? !1 : n.edge - (this.deps.video.currentTime + t) < jn * r;
2146
+ return !(r > 0) || !(n.edge > 0) ? !1 : n.edge - (this.deps.video.currentTime + t) < In * r;
2110
2147
  }
2111
2148
  releaseStallCap() {
2112
2149
  let e = this.hls, t = Date.now(), n = this.stallCapTickAt;
@@ -2115,7 +2152,7 @@ var nr = class {
2115
2152
  n && (this.stallCapSince += t - Math.max(n, this.stallCapSince));
2116
2153
  return;
2117
2154
  }
2118
- if (t - this.stallCapSince < kn) return;
2155
+ if (t - this.stallCapSince < Pn) return;
2119
2156
  let r = this.stallCapBitrate, i = e.levels.map((e) => e.bitrate).filter((e) => e > r).sort((e, t) => e - t)[0];
2120
2157
  this.stallCapBitrate = i ?? null, this.stallCapSince = t, this.pushCap();
2121
2158
  }
@@ -2128,26 +2165,26 @@ var nr = class {
2128
2165
  this.headroomGranted = !1, this.headroomSamples = [];
2129
2166
  return;
2130
2167
  }
2131
- this.headroomSamples = this.headroomSamples.filter((t) => t.t >= e - Un);
2168
+ this.headroomSamples = this.headroomSamples.filter((t) => t.t >= e - Jn);
2132
2169
  let r = this.headroomEarned(n, e);
2133
- r !== this.headroomGranted && (r || (this.headroomRegrantAt = e + Pn), this.headroomGranted = r, this.applySizeCapUnlessPending());
2170
+ r !== this.headroomGranted && (r || (this.headroomRegrantAt = e + zn), this.headroomGranted = r, this.applySizeCapUnlessPending());
2134
2171
  }
2135
2172
  headroomEarned(e, t) {
2136
- if (t - this.lastStallAt < Nn || !this.headroomGranted && t < this.headroomRegrantAt || !this.headroomGranted && this.headroomSamples.length < q) return !1;
2173
+ if (t - this.lastStallAt < Rn || !this.headroomGranted && t < this.headroomRegrantAt || !this.headroomGranted && this.headroomSamples.length < q) return !1;
2137
2174
  let n = e.levels, r = this.sizeCapCoverLevel();
2138
2175
  if (r == null) return this.headroomGranted;
2139
- let i = J(n, r, Sn);
2176
+ let i = J(n, r, Dn);
2140
2177
  if (i === r) return this.headroomGranted;
2141
- let a = n[i]?.bitrate, o = e.bandwidthEstimate, s = this.headroomGranted ? Dn : En;
2178
+ let a = n[i]?.bitrate, o = e.bandwidthEstimate, s = this.headroomGranted ? Mn : jn;
2142
2179
  if (!(a > 0) || !(o >= s * a)) return !1;
2143
2180
  let c = (e) => K([...e].sort((e, t) => e - t), 90);
2144
2181
  if (this.headroomGranted) {
2145
2182
  let e = this.headroomSamples.filter((e) => e.bitrate >= a).map((e) => e.ratio);
2146
- return e.length < q || c(e) <= Tn;
2183
+ return e.length < q || c(e) <= An;
2147
2184
  }
2148
2185
  let l = [];
2149
2186
  for (let e of this.headroomSamples) e.bitrate < a && l.push(e.ratio * (a / e.bitrate));
2150
- return l.length >= q && c(l) <= wn;
2187
+ return l.length >= q && c(l) <= kn;
2151
2188
  }
2152
2189
  applySizeCap() {
2153
2190
  if (!this.sizeCapManaged) return;
@@ -2222,7 +2259,7 @@ var nr = class {
2222
2259
  let t = e.currentLevel >= 0 ? e.levels[e.currentLevel] : void 0;
2223
2260
  t && (n.levelHeight = t.height);
2224
2261
  let r = e.autoLevelCapping, i = r >= 0 ? e.levels[r] : void 0;
2225
- i && i.height > 0 && (n.maxLevelHeight = i.height, !Mn(e) && r === this.pushedCapLevel && this.pushedCapReason && (n.capReason = this.pushedCapReason)), t?.videoCodec && (n.videoCodec = t.videoCodec), t?.audioCodec && (n.audioCodec = t.audioCodec), t && typeof t.bitrate == "number" && (n.renditionBitrateKbps = Math.round(t.bitrate / 1e3)), typeof t?.details?.live == "boolean" && (n.isLive = t.details.live, typeof e.liveSyncPosition == "number" && (n.atLiveEdge = this.deps.video.currentTime >= e.liveSyncPosition - 1.5));
2262
+ i && i.height > 0 && (n.maxLevelHeight = i.height, !Ln(e) && r === this.pushedCapLevel && this.pushedCapReason && (n.capReason = this.pushedCapReason)), t?.videoCodec && (n.videoCodec = t.videoCodec), t?.audioCodec && (n.audioCodec = t.audioCodec), t && typeof t.bitrate == "number" && (n.renditionBitrateKbps = Math.round(t.bitrate / 1e3)), typeof t?.details?.live == "boolean" && (n.isLive = t.details.live, typeof e.liveSyncPosition == "number" && (n.atLiveEdge = this.deps.video.currentTime >= e.liveSyncPosition - 1.5));
2226
2263
  }
2227
2264
  return n;
2228
2265
  }
@@ -2250,7 +2287,7 @@ var nr = class {
2250
2287
  return;
2251
2288
  }
2252
2289
  let e = Date.now() - this.lastAdvanceMs;
2253
- !this.chaseSuspended && e > on && this.suspendChasing(), e > nn && this.emitEnded("stalled");
2290
+ !this.chaseSuspended && e > dn && this.suspendChasing(), e > cn && this.emitEnded("stalled");
2254
2291
  }
2255
2292
  resetStarvationWindow() {
2256
2293
  this.starvedTicks = 0, this.deadTicks = 0, this.mainFragsSinceTick = 0, this.recentMainFrags = [];
@@ -2265,7 +2302,7 @@ var nr = class {
2265
2302
  this.resetStarvationWindow();
2266
2303
  return;
2267
2304
  }
2268
- let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < on, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > pn, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = R(t), c = o - s, l = c !== this.starvedFrames;
2305
+ let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < dn, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > vn, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = R(t), c = o - s, l = c !== this.starvedFrames;
2269
2306
  if (this.starvedFrames = c, !a || l) {
2270
2307
  (t.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA || l) && this.clearKick(), this.resetStarvationWindow();
2271
2308
  return;
@@ -2274,12 +2311,12 @@ var nr = class {
2274
2311
  this.resetStarvationWindow();
2275
2312
  return;
2276
2313
  }
2277
- 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 > vn && this.recentMainFrags.shift(), this.clearRecoveryReset(), this.kickPending && this.kickTicks >= gn && (this.clearKick(), this.starvationRebuilds < _n)) {
2314
+ 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 >= xn && (this.clearKick(), this.starvationRebuilds < Sn)) {
2278
2315
  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");
2279
2316
  return;
2280
2317
  }
2281
- let u = i && this.starvedTicks >= mn, d = !i && this.deadTicks >= vn && this.recentMainFrags.reduce((e, t) => e + t, 0) >= yn;
2282
- if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >= hn))) {
2318
+ let u = i && this.starvedTicks >= yn, d = !i && this.deadTicks >= Cn && this.recentMainFrags.reduce((e, t) => e + t, 0) >= wn;
2319
+ if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >= bn))) {
2283
2320
  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");
2284
2321
  try {
2285
2322
  e.stopLoad(), e.startLoad(-1);
@@ -2294,7 +2331,7 @@ var nr = class {
2294
2331
  let t = e.latency, n = e.targetLatency;
2295
2332
  if (typeof t != "number" || typeof n != "number" || !Number.isFinite(t)) return;
2296
2333
  let r = t - n;
2297
- !this.catchUpEngaged && r > sn ? this.catchUpEngaged = !0 : this.catchUpEngaged && r <= sn && this.deps.video.playbackRate === 1 && (this.catchUpEngaged = !1);
2334
+ !this.catchUpEngaged && r > fn ? this.catchUpEngaged = !0 : this.catchUpEngaged && r <= fn && this.deps.video.playbackRate === 1 && (this.catchUpEngaged = !1);
2298
2335
  let i = this.catchUpEngaged ? this.catchUpRate : 1;
2299
2336
  e.config.maxLiveSyncPlaybackRate !== i && (e.config.maxLiveSyncPlaybackRate = i);
2300
2337
  }
@@ -2307,7 +2344,7 @@ var nr = class {
2307
2344
  }), n < 0 ? 0 : n;
2308
2345
  }
2309
2346
  armFrameDropGrace() {
2310
- this.fpsGraceTicks > 0 || (this.fpsGraceTicks = Rn, this.fpsBadTicks = 0);
2347
+ this.fpsGraceTicks > 0 || (this.fpsGraceTicks = Un, this.fpsBadTicks = 0);
2311
2348
  }
2312
2349
  resetFrameDropWindow() {
2313
2350
  this.fpsLastFrames = -1, this.fpsLastDropped = -1, this.fpsBadTicks = 0;
@@ -2319,7 +2356,7 @@ var nr = class {
2319
2356
  return;
2320
2357
  }
2321
2358
  let { totalFrames: n, dropped: r } = R(t), i = this.fpsLastFrames < 0, a = n - this.fpsLastFrames, o = r - this.fpsLastDropped;
2322
- if (this.fpsLastFrames = n, this.fpsLastDropped = r, i || a < In || o < 0) {
2359
+ if (this.fpsLastFrames = n, this.fpsLastDropped = r, i || a < Vn || o < 0) {
2323
2360
  this.fpsBadTicks = 0;
2324
2361
  return;
2325
2362
  }
@@ -2327,11 +2364,11 @@ var nr = class {
2327
2364
  --this.fpsGraceTicks, this.fpsBadTicks = 0;
2328
2365
  return;
2329
2366
  }
2330
- if (o <= Fn * a) {
2367
+ if (o <= Bn * a) {
2331
2368
  this.fpsBadTicks = 0;
2332
2369
  return;
2333
2370
  }
2334
- if (this.fpsBadTicks += 1, this.fpsBadTicks < Ln) return;
2371
+ if (this.fpsBadTicks += 1, this.fpsBadTicks < Hn) return;
2335
2372
  this.fpsBadTicks = 0;
2336
2373
  let s = e.levels, c = s[e.currentLevel];
2337
2374
  if (!c) return;
@@ -2348,22 +2385,22 @@ var nr = class {
2348
2385
  return;
2349
2386
  }
2350
2387
  let n = e.latency, r = t.playbackRate, i = e.config.maxLiveSyncPlaybackRate;
2351
- if (!(!t.paused && typeof i == "number" && i > 1 && r >= Math.min(un, i) && r <= i && typeof n == "number" && Number.isFinite(n))) {
2388
+ if (!(!t.paused && typeof i == "number" && i > 1 && r >= Math.min(hn, i) && r <= i && typeof n == "number" && Number.isFinite(n))) {
2352
2389
  this.resetChaseWindow();
2353
2390
  return;
2354
2391
  }
2355
- if (this.chaseLatency < 0 || this.chaseLatency - n >= ln) {
2392
+ if (this.chaseLatency < 0 || this.chaseLatency - n >= mn) {
2356
2393
  this.chaseLatency = n, this.chaseTicks = 0;
2357
2394
  return;
2358
2395
  }
2359
- if (this.chaseTicks += 1, this.chaseTicks < cn) return;
2396
+ if (this.chaseTicks += 1, this.chaseTicks < pn) return;
2360
2397
  this.resetChaseWindow();
2361
2398
  let a = typeof e.config.liveMaxLatencyDuration == "number" ? e.config.liveMaxLatencyDuration : e.maxLatency;
2362
2399
  if (!(a > 1)) return;
2363
2400
  let o = e.targetLatency;
2364
2401
  if (typeof o != "number" || !(o > 0)) return;
2365
2402
  let s = Math.min(n, a - 1);
2366
- s < o + dn || (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({
2403
+ s < o + gn || (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({
2367
2404
  type: "livetargetchange",
2368
2405
  change: {
2369
2406
  targetSeconds: s,
@@ -2380,13 +2417,13 @@ var nr = class {
2380
2417
  if (!this.holdBackEnabled || this.holdBackPriorChecked || !e || (this.holdBackPriorChecked = !0, this.holdBackApplied > 0 || this.holdBackStepCount > 0)) return;
2381
2418
  let t = e.targetLatency;
2382
2419
  if (this.holdBackBase < 0 && typeof t == "number" && t > 0 && (this.holdBackBase = t), this.holdBackBase < 0) return;
2383
- let n = $n();
2384
- if (!(n >= Zn)) return;
2420
+ let n = ir();
2421
+ if (!(n >= nr)) return;
2385
2422
  let r = typeof t == "number" && t > 0 ? t : this.holdBackBase, i = Math.min(this.holdBackCeiling(e), this.holdBackBase + X);
2386
2423
  i > r && (this.holdBackStepCount = 1, this.moveHoldBack(e, r, i, "connection-rtt", `connection rtt ${n} ms`));
2387
2424
  }
2388
2425
  holdBackCeiling(e) {
2389
- let t = this.deps.adaptiveHoldBackCeiling, n = typeof t == "number" && Number.isFinite(t) ? t : Xn, r = typeof e.config.liveMaxLatencyDuration == "number" ? e.config.liveMaxLatencyDuration : e.maxLatency;
2426
+ let t = this.deps.adaptiveHoldBackCeiling, n = typeof t == "number" && Number.isFinite(t) ? t : tr, r = typeof e.config.liveMaxLatencyDuration == "number" ? e.config.liveMaxLatencyDuration : e.maxLatency;
2390
2427
  return r > 1 ? Math.min(n, r - 1) : n;
2391
2428
  }
2392
2429
  sampleHoldBack(e) {
@@ -2395,7 +2432,7 @@ var nr = class {
2395
2432
  if (t !== "main" && t !== "audio") return;
2396
2433
  let n = e.part ? e.part.duration : this.detectedLowLatency ? 0 : e.frag?.duration;
2397
2434
  if (typeof n != "number" || !(n > 0)) return;
2398
- let r = Qn(e)?.loading;
2435
+ let r = rr(e)?.loading;
2399
2436
  if (!r || !(r.start > 0) || !(r.end > 0)) return;
2400
2437
  let i = r.first > r.start ? r.first : r.end, a = Math.max(0, i - r.start) / (n * 1e3);
2401
2438
  this.holdBackSamples.push({
@@ -2410,20 +2447,20 @@ var nr = class {
2410
2447
  this.holdBackSlowTicks = 0, this.holdBackFastSince = 0, this.holdBackSamples = [];
2411
2448
  return;
2412
2449
  }
2413
- let t = Date.now(), n = t - Un, r = this.holdBackSamples, i = 0;
2450
+ let t = Date.now(), n = t - Jn, r = this.holdBackSamples, i = 0;
2414
2451
  for (; i < r.length && r[i].t < n;) i += 1;
2415
2452
  i > 0 && r.splice(0, i);
2416
2453
  let a = e.targetLatency;
2417
2454
  if (typeof a != "number" || !(a > 0)) return;
2418
2455
  this.holdBackBase < 0 && (this.holdBackBase = a);
2419
2456
  let o = this.holdBackBase, s = this.holdBackCeiling(e);
2420
- if (r.length < Wn) {
2457
+ if (r.length < Yn) {
2421
2458
  this.holdBackSlowTicks = 0;
2422
2459
  return;
2423
2460
  }
2424
2461
  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);
2425
- if (l >= Gn && u >= Kn) {
2426
- if (this.holdBackFastSince = 0, this.holdBackSlowTicks += 1, this.holdBackSlowTicks < Jn) return;
2462
+ if (l >= Xn && u >= Zn) {
2463
+ if (this.holdBackFastSince = 0, this.holdBackSlowTicks += 1, this.holdBackSlowTicks < $n) return;
2427
2464
  this.holdBackSlowTicks = 0;
2428
2465
  let t = Math.ceil((s - o) / X), n = Math.ceil((a - o) / X - 1e-9);
2429
2466
  if (n > this.holdBackStepCount && (this.holdBackStepCount = n), this.holdBackStepCount >= t) return;
@@ -2433,7 +2470,7 @@ var nr = class {
2433
2470
  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)`);
2434
2471
  return;
2435
2472
  }
2436
- if (this.holdBackSlowTicks = 0, l >= qn) {
2473
+ if (this.holdBackSlowTicks = 0, l >= Qn) {
2437
2474
  this.holdBackFastSince = 0;
2438
2475
  return;
2439
2476
  }
@@ -2441,7 +2478,7 @@ var nr = class {
2441
2478
  this.holdBackFastSince = t;
2442
2479
  return;
2443
2480
  }
2444
- if (t - this.holdBackFastSince < Yn || (this.holdBackFastSince = t, this.holdBackStepCount === 0 || a !== this.holdBackApplied)) return;
2481
+ if (t - this.holdBackFastSince < er || (this.holdBackFastSince = t, this.holdBackStepCount === 0 || a !== this.holdBackApplied)) return;
2445
2482
  --this.holdBackStepCount;
2446
2483
  let f = Math.max(o, o + this.holdBackStepCount * X);
2447
2484
  f >= a || this.moveHoldBack(e, a, f, "fast-fetches", `part fetches fast again (p95 first byte ${d} ms per 1 s of media)`);
@@ -2484,12 +2521,12 @@ var nr = class {
2484
2521
  } catch {}
2485
2522
  }
2486
2523
  startEstimate() {
2487
- return this.deps.rememberBandwidth === !1 ? Jt : Be() ?? Jt;
2524
+ return this.deps.rememberBandwidth === !1 ? Qt : Be() ?? Qt;
2488
2525
  }
2489
2526
  maybeRememberBandwidth(e) {
2490
- if (this.deps.rememberBandwidth === !1 || this.bwFragsLoaded < Yt) return;
2527
+ if (this.deps.rememberBandwidth === !1 || this.bwFragsLoaded < $t) return;
2491
2528
  let t = Date.now();
2492
- if (!e && t - this.bwSavedAt < Xt) return;
2529
+ if (!e && t - this.bwSavedAt < en) return;
2493
2530
  let n = this.hls?.bandwidthEstimate;
2494
2531
  typeof n != "number" || !Number.isFinite(n) || n <= 0 || (this.bwSavedAt = t, Ve(n));
2495
2532
  }
@@ -2509,7 +2546,7 @@ var nr = class {
2509
2546
  scheduleLicenseRetry(t) {
2510
2547
  if (t.details !== e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED && t.details !== e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED) return !1;
2511
2548
  let n = t.response?.code ?? 0;
2512
- if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= $t) return !1;
2549
+ if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= an) return !1;
2513
2550
  this.licenseRecoveries += 1;
2514
2551
  let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
2515
2552
  return this.licenseRetryTimer = setTimeout(() => {
@@ -2545,7 +2582,7 @@ var nr = class {
2545
2582
  this.offlineHold = !0, this.networkRetryTimer &&= (clearTimeout(this.networkRetryTimer), null);
2546
2583
  return;
2547
2584
  }
2548
- if (this.networkRecoveries < Qt) {
2585
+ if (this.networkRecoveries < rn) {
2549
2586
  this.networkRecoveries += 1;
2550
2587
  let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
2551
2588
  this.networkRetryTimer = setTimeout(() => {
@@ -2562,7 +2599,16 @@ var nr = class {
2562
2599
  return;
2563
2600
  }
2564
2601
  if (t.type === e.ErrorTypes.MEDIA_ERROR) {
2565
- if (this.mediaRecoveries < Zt) {
2602
+ if (t.details != null && (t.details === e.ErrorDetails.BUFFER_SEEK_OVER_HOLE || t.details === e.ErrorDetails.BUFFER_STALLED_ERROR) && this.sawLive && this.holeKicks < nn && this.mediaRecoveries < tn) {
2603
+ this.holeKicks += 1, this.mediaRecoveries += 1, console.warn("havik-player: playback stuck at a buffer hole hls.js could not nudge across — re-syncing to the live edge");
2604
+ try {
2605
+ n.stopLoad(), n.startLoad(-1);
2606
+ return;
2607
+ } catch (e) {
2608
+ console.warn("havik-player: live re-sync after a buffer hole failed", e);
2609
+ }
2610
+ }
2611
+ if (this.mediaRecoveries < tn) {
2566
2612
  this.mediaRecoveries += 1, n.recoverMediaError();
2567
2613
  return;
2568
2614
  }
@@ -2583,18 +2629,21 @@ var nr = class {
2583
2629
  }
2584
2630
  }
2585
2631
  toError(t, r = "PLAYBACK") {
2586
- 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(Pt(t.networkDetails)) : void 0;
2587
- return new n(r === "DRM" ? a(o, l) : r === "NETWORK" && o === 0 ? "NETWORK" : o === 401 ? "UNAUTHORIZED" : o === 403 ? "FORBIDDEN" : "INTERNAL", o, `${r} error: ${s}${c ? ` (${c})` : ""}`, l ? { serverCode: l } : void 0);
2632
+ 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(Rt(t.networkDetails)) : void 0;
2633
+ return new n(r === "DRM" ? a(o, l) : r === "NETWORK" && o === 0 ? "NETWORK" : o === 401 ? "UNAUTHORIZED" : o === 403 ? "FORBIDDEN" : "INTERNAL", o, `${r} error: ${s}${c ? ` (${c})` : ""}`, {
2634
+ detail: s,
2635
+ ...l ? { serverCode: l } : {}
2636
+ });
2588
2637
  }
2589
- }, rr = 15e3, ir = 6e5, ar = 3e5, or = 100, Z = 1, sr = 1e3, cr = 3e4, lr = 1e3, ur = {
2638
+ }, cr = 15e3, lr = 6e5, ur = 3e5, dr = 100, Z = 1, fr = 1e3, pr = 3e4, mr = 1e3, hr = {
2590
2639
  inline: "PRESENTATION_INLINE",
2591
2640
  fullscreen: "PRESENTATION_FULLSCREEN",
2592
2641
  pip: "PRESENTATION_PIP"
2593
- }, dr = {
2642
+ }, gr = {
2594
2643
  widevine: "KEY_SYSTEM_WIDEVINE",
2595
2644
  fairplay: "KEY_SYSTEM_FAIRPLAY",
2596
2645
  none: "KEY_SYSTEM_NONE"
2597
- }, fr = /* @__PURE__ */ new Set([
2646
+ }, _r = /* @__PURE__ */ new Set([
2598
2647
  "INVALID_URN",
2599
2648
  "NOT_FOUND",
2600
2649
  "GEO_DENIED",
@@ -2605,7 +2654,7 @@ var nr = class {
2605
2654
  "FORBIDDEN",
2606
2655
  "RATE_LIMITED",
2607
2656
  "TIMEOUT"
2608
- ]), pr = {
2657
+ ]), vr = {
2609
2658
  idle: "PLAYER_STATE_IDLE",
2610
2659
  waiting: "PLAYER_STATE_WAITING",
2611
2660
  loading: "PLAYER_STATE_LOADING",
@@ -2615,7 +2664,7 @@ var nr = class {
2615
2664
  ended: "PLAYER_STATE_ENDED",
2616
2665
  error: "PLAYER_STATE_ERROR"
2617
2666
  };
2618
- function mr(e) {
2667
+ function yr(e) {
2619
2668
  try {
2620
2669
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
2621
2670
  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);
@@ -2623,8 +2672,8 @@ function mr(e) {
2623
2672
  return;
2624
2673
  }
2625
2674
  }
2626
- var hr = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), gr = /* @__PURE__ */ new Set(), _r = /* @__PURE__ */ new Set(), vr = /* @__PURE__ */ new Set(), yr = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
2627
- function br(e) {
2675
+ var br = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), xr = /* @__PURE__ */ new Set(), Sr = /* @__PURE__ */ new Set(), Cr = /* @__PURE__ */ new Set(), wr = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
2676
+ function Tr(e) {
2628
2677
  if (e.disposition !== "enforce" || e.effectiveDirective !== "connect-src") return;
2629
2678
  let t;
2630
2679
  try {
@@ -2633,19 +2682,19 @@ function br(e) {
2633
2682
  } catch {
2634
2683
  return;
2635
2684
  }
2636
- if (gr.has(t) && !_r.has(t)) {
2637
- _r.add(t), _(yr(new URL(t).origin));
2638
- for (let e of vr) e.noteCspBlocked();
2685
+ if (xr.has(t) && !Sr.has(t)) {
2686
+ Sr.add(t), _(wr(new URL(t).origin));
2687
+ for (let e of Cr) e.noteCspBlocked();
2639
2688
  }
2640
2689
  }
2641
- function xr(e, t) {
2690
+ function Er(e, t) {
2642
2691
  return _(t, e), { disabledReason: t };
2643
2692
  }
2644
- function Sr(e, t, n) {
2645
- if (n) return t && !hr(t, n) && _(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
2693
+ function Dr(e, t, n) {
2694
+ if (n) return t && !br(t, n) && _(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
2646
2695
  if (t) return { endpoint: t };
2647
- if (t !== void 0) return xr("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).");
2648
- let r = mr(e);
2696
+ if (t !== void 0) return Er("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).");
2697
+ let r = yr(e);
2649
2698
  if (r) return { endpoint: r };
2650
2699
  let i;
2651
2700
  try {
@@ -2653,9 +2702,9 @@ function Sr(e, t, n) {
2653
2702
  } catch {
2654
2703
  i = e;
2655
2704
  }
2656
- return xr(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).`);
2705
+ return Er(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).`);
2657
2706
  }
2658
- var Cr = class {
2707
+ var Or = class {
2659
2708
  opts;
2660
2709
  url;
2661
2710
  cspKey;
@@ -2737,8 +2786,8 @@ var Cr = class {
2737
2786
  constructor(e) {
2738
2787
  this.opts = {
2739
2788
  ...e,
2740
- heartbeatMs: e.heartbeatMs ?? rr,
2741
- idleMs: e.idleMs ?? ir
2789
+ heartbeatMs: e.heartbeatMs ?? cr,
2790
+ idleMs: e.idleMs ?? lr
2742
2791
  }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`;
2743
2792
  try {
2744
2793
  let e = new URL(this.url);
@@ -2755,11 +2804,11 @@ var Cr = class {
2755
2804
  sdkVersion: this.opts.sdkVersion,
2756
2805
  ...this.opts.environment
2757
2806
  };
2758
- this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), sr), 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 && gr.add(this.cspKey), document.addEventListener("securitypolicyviolation", br), vr.add(this), this.noteCspBlocked(), this.onVisibility());
2807
+ this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), fr), 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 && xr.add(this.cspKey), document.addEventListener("securitypolicyviolation", Tr), Cr.add(this), this.noteCspBlocked(), this.onVisibility());
2759
2808
  }
2760
2809
  checkSuspension(e) {
2761
2810
  if (this.endedFlag || !this.opts.idleMs) return !1;
2762
- let t = Math.max(this.opts.idleMs, ar);
2811
+ let t = Math.max(this.opts.idleMs, ur);
2763
2812
  return this.lastTickMs > 0 && e - this.lastTickMs >= t ? (this.endIdle(this.lastTickMs), !0) : (this.lastTickMs = e, !1);
2764
2813
  }
2765
2814
  syncIdle(e) {
@@ -2780,14 +2829,14 @@ var Cr = class {
2780
2829
  } else e === "buffering" && (this.isBuffering = !1, this.stallIsSeekInduced = !1);
2781
2830
  this.syncBuffering(), t === "paused" && (this.pauseCount += 1), this.syncIdle(t);
2782
2831
  let a = {
2783
- fromState: pr[e] ?? "PLAYER_STATE_UNSPECIFIED",
2784
- toState: pr[t] ?? "PLAYER_STATE_UNSPECIFIED",
2785
- presentation: ur[this.presentation]
2832
+ fromState: vr[e] ?? "PLAYER_STATE_UNSPECIFIED",
2833
+ toState: vr[t] ?? "PLAYER_STATE_UNSPECIFIED",
2834
+ presentation: hr[this.presentation]
2786
2835
  }, o = this.causeOf(e, t, i);
2787
2836
  o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
2788
2837
  }
2789
2838
  seekInduced(e) {
2790
- return this.lastSeekMs >= 0 && e - this.lastSeekMs < lr;
2839
+ return this.lastSeekMs >= 0 && e - this.lastSeekMs < mr;
2791
2840
  }
2792
2841
  causeOf(e, t, n) {
2793
2842
  if (t === "error") return "TRANSITION_CAUSE_ERROR";
@@ -2861,7 +2910,7 @@ var Cr = class {
2861
2910
  let r = n ?? "";
2862
2911
  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";
2863
2912
  if (e === "DRM_CLIENT" || e === "DRM_DEVICE_REVOKED") return "ERROR_STAGE_DRM";
2864
- if (fr.has(e)) return "ERROR_STAGE_RESOLVE";
2913
+ if (_r.has(e)) return "ERROR_STAGE_RESOLVE";
2865
2914
  let i = t ?? "";
2866
2915
  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";
2867
2916
  }
@@ -2893,7 +2942,7 @@ var Cr = class {
2893
2942
  let l = t.droppedFrames ?? 0;
2894
2943
  l > this.lastDropped && (o.droppedFramesDelta = l - this.lastDropped), this.lastDropped = l;
2895
2944
  let u = t.renditionSwitches ?? 0;
2896
- 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 = pr[t.state] ?? "PLAYER_STATE_UNSPECIFIED";
2945
+ 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 = vr[t.state] ?? "PLAYER_STATE_UNSPECIFIED";
2897
2946
  let d = this.mutedTotalMs;
2898
2947
  this.mutedSinceMs !== null && (d += e - this.mutedSinceMs), d > this.lastHbMutedMs && (o.mutedMsDelta = Math.round(d - this.lastHbMutedMs)), this.lastHbMutedMs = d;
2899
2948
  let f = t.upshifts ?? 0;
@@ -2915,13 +2964,13 @@ var Cr = class {
2915
2964
  maybeEmitMediaInfo(e) {
2916
2965
  if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
2917
2966
  let t = {};
2918
- e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = dr[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);
2967
+ e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = gr[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);
2919
2968
  let n = JSON.stringify(t);
2920
2969
  n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
2921
2970
  }
2922
2971
  end(e, t) {
2923
2972
  if (this.endedFlag) return;
2924
- this.endedFlag = !0, vr.delete(this);
2973
+ this.endedFlag = !0, Cr.delete(this);
2925
2974
  let n = t ?? this.now();
2926
2975
  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));
2927
2976
  let r = this.safeSample();
@@ -2935,7 +2984,7 @@ var Cr = class {
2935
2984
  let c = r?.positionMs ?? 0;
2936
2985
  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));
2937
2986
  let l = e !== "END_REASON_ENDED";
2938
- l && this.lastStallMs > 0 && n - this.lastStallMs <= cr && (i.abandonAfterStallMs = Math.max(Z, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= cr && (i.abandonAfterErrorMs = Math.max(Z, 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);
2987
+ l && this.lastStallMs > 0 && n - this.lastStallMs <= pr && (i.abandonAfterStallMs = Math.max(Z, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= pr && (i.abandonAfterErrorMs = Math.max(Z, 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);
2939
2988
  let u = r?.decodedFrames ?? this.lastDecoded;
2940
2989
  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");
2941
2990
  }
@@ -2955,13 +3004,13 @@ var Cr = class {
2955
3004
  };
2956
3005
  }
2957
3006
  noteCspBlocked() {
2958
- this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(yr(new URL(this.url).origin)));
3007
+ this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(wr(new URL(this.url).origin)));
2959
3008
  }
2960
3009
  cspBlocked() {
2961
- return !!this.cspKey && _r.has(this.cspKey);
3010
+ return !!this.cspKey && Sr.has(this.cspKey);
2962
3011
  }
2963
3012
  enqueue(e) {
2964
- if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > or;) {
3013
+ if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > dr;) {
2965
3014
  let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
2966
3015
  this.pending.splice(e >= 0 ? e : 0, 1);
2967
3016
  }
@@ -2988,7 +3037,7 @@ var Cr = class {
2988
3037
  };
2989
3038
  //#endregion
2990
3039
  //#region src/core/environment.ts
2991
- function wr() {
3040
+ function kr() {
2992
3041
  if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
2993
3042
  let e;
2994
3043
  try {
@@ -3013,14 +3062,14 @@ function wr() {
3013
3062
  integration: "INTEGRATION_KIND_IFRAME"
3014
3063
  };
3015
3064
  }
3016
- function Tr() {
3065
+ function Ar() {
3017
3066
  if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
3018
3067
  let e = navigator.userAgentData, t = e?.platform ?? "";
3019
3068
  if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
3020
3069
  let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
3021
3070
  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";
3022
3071
  }
3023
- function Er() {
3072
+ function jr() {
3024
3073
  let e = typeof navigator < "u" ? navigator.connection : void 0;
3025
3074
  if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
3026
3075
  let t = {
@@ -3039,17 +3088,17 @@ function Er() {
3039
3088
  }
3040
3089
  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;
3041
3090
  }
3042
- function Dr(e) {
3043
- let t = wr(), n = {
3044
- deviceClass: Tr(),
3091
+ function Mr(e) {
3092
+ let t = kr(), n = {
3093
+ deviceClass: Ar(),
3045
3094
  integration: t.integration,
3046
- ...Er()
3095
+ ...jr()
3047
3096
  };
3048
3097
  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);
3049
3098
  let r = e.getBoundingClientRect?.();
3050
3099
  return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
3051
3100
  }
3052
- async function Or() {
3101
+ async function Nr() {
3053
3102
  let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
3054
3103
  if (!e?.getHighEntropyValues) return {};
3055
3104
  try {
@@ -3061,7 +3110,7 @@ async function Or() {
3061
3110
  }
3062
3111
  //#endregion
3063
3112
  //#region src/core/version.ts
3064
- var kr = "2.27.2", Ar = {
3113
+ var Pr = "2.28.1", Fr = {
3065
3114
  accent: "#E1B600",
3066
3115
  accentText: "#1B1C23",
3067
3116
  background: "#0e0f13",
@@ -3072,7 +3121,7 @@ var kr = "2.27.2", Ar = {
3072
3121
  border: "rgba(255, 255, 255, 0.1)",
3073
3122
  radius: "8px",
3074
3123
  fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
3075
- }, jr = {
3124
+ }, Ir = {
3076
3125
  accent: "--havik-accent",
3077
3126
  accentText: "--havik-accent-text",
3078
3127
  background: "--havik-bg",
@@ -3084,26 +3133,26 @@ var kr = "2.27.2", Ar = {
3084
3133
  radius: "--havik-radius",
3085
3134
  fontFamily: "--havik-font"
3086
3135
  };
3087
- function Mr(e, t) {
3136
+ function Lr(e, t) {
3088
3137
  let n = {
3089
- ...Ar,
3138
+ ...Fr,
3090
3139
  ...t
3091
3140
  };
3092
- for (let t of Object.keys(jr)) e.style.setProperty(jr[t], n[t]);
3141
+ for (let t of Object.keys(Ir)) e.style.setProperty(Ir[t], n[t]);
3093
3142
  n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
3094
3143
  }
3095
3144
  //#endregion
3096
3145
  //#region src/ui/styles.ts
3097
- var Nr = "havik-player-styles", Pr = "\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", Fr = /* @__PURE__ */ new WeakMap();
3098
- function Ir(e = document, t) {
3099
- let n = e.querySelector(`style#${Nr}`);
3100
- if (n && (!t || Fr.get(n) === t)) return;
3146
+ var Rr = "havik-player-styles", zr = "\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", Br = /* @__PURE__ */ new WeakMap();
3147
+ function Vr(e = document, t) {
3148
+ let n = e.querySelector(`style#${Rr}`);
3149
+ if (n && (!t || Br.get(n) === t)) return;
3101
3150
  let r = e.createElement("style");
3102
- r.id = Nr, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = Pr, Fr.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
3151
+ r.id = Rr, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = zr, Br.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
3103
3152
  }
3104
3153
  //#endregion
3105
3154
  //#region src/ui/icons.ts
3106
- var Lr = {
3155
+ var Hr = {
3107
3156
  play: "<path d=\"M8 5v14l11-7z\"/>",
3108
3157
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
3109
3158
  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\"/>",
@@ -3117,11 +3166,11 @@ var Lr = {
3117
3166
  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\"/>"
3118
3167
  };
3119
3168
  function Q(e) {
3120
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${Lr[e]}</svg>`;
3169
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${Hr[e]}</svg>`;
3121
3170
  }
3122
3171
  //#endregion
3123
3172
  //#region src/ui/controls.ts
3124
- var Rr = 3e3, zr = 250, Br = /* @__PURE__ */ new Set([
3173
+ var Ur = 3e3, Wr = 250, Gr = /* @__PURE__ */ new Set([
3125
3174
  "GONE",
3126
3175
  "NOT_FOUND",
3127
3176
  "GEO_DENIED",
@@ -3129,15 +3178,19 @@ var Rr = 3e3, zr = 250, Br = /* @__PURE__ */ new Set([
3129
3178
  "UNAUTHORIZED",
3130
3179
  "DRM_DEVICE_REVOKED"
3131
3180
  ]);
3132
- function Vr(e) {
3181
+ function Kr(e) {
3133
3182
  let t = "This match isn't available in your country";
3134
3183
  return e ? `${t} (${e})` : t;
3135
3184
  }
3136
- var Hr = "This device can't play protected video";
3137
- function Ur(e) {
3138
- return new Wr(e);
3185
+ var qr = "This device can't play protected video";
3186
+ function Jr(e) {
3187
+ return `This browser can't play protected video. Allow protected content in its site settings, or use ${e ? "Chrome, Edge or Safari" : "Chrome or Edge"} (Firefox asks when you retry)`;
3188
+ }
3189
+ var Yr = "Protected video can't be shown on this display. Use the built-in screen, or an HDCP-capable monitor and cable";
3190
+ function Xr(e) {
3191
+ return new Zr(e);
3139
3192
  }
3140
- var Wr = class {
3193
+ var Zr = class {
3141
3194
  root;
3142
3195
  video;
3143
3196
  player;
@@ -3186,7 +3239,7 @@ var Wr = class {
3186
3239
  scrubbing = !1;
3187
3240
  offFns = [];
3188
3241
  constructor(e) {
3189
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Rr, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.streamEnded = e.streamEnded ?? (() => !1), this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, this.offlineMessage = e.offlineMessage ?? "No internet connection — waiting to reconnect", Ir(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");
3242
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Ur, 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, this.offlineMessage = e.offlineMessage ?? "No internet connection — waiting to reconnect", Vr(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");
3190
3243
  let t = (e, t, n) => {
3191
3244
  let r = this.doc.createElement(e);
3192
3245
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -3272,7 +3325,7 @@ var Wr = class {
3272
3325
  let t = this.surfaceWasHidden;
3273
3326
  this.tapTimer = setTimeout(() => {
3274
3327
  this.tapTimer = void 0, this.toggleBar(t);
3275
- }, zr);
3328
+ }, Wr);
3276
3329
  }
3277
3330
  toggleBar(e) {
3278
3331
  e ? this.poke() : this.hideIfIdle();
@@ -3299,7 +3352,20 @@ var Wr = class {
3299
3352
  this.renderStatus(t);
3300
3353
  }
3301
3354
  statusMessageFor(e) {
3302
- return e === "ended" ? this.isGone() ? this.endedMessage : this.player.endedReason === "interrupted" ? this.interruptedMessage : this.endedMessage : e === "offline" ? this.offlineMessage : e === "waiting" ? this.hasPlayed ? "Reconnecting — please stand by" : "Starting soon…" : e === "error" ? this.errorMessage === void 0 ? this.lastError?.code === "GEO_DENIED" ? Vr(this.lastError.country) : this.lastError?.code === "DRM_DEVICE_REVOKED" ? Hr : this.lastError?.message ?? "Playback unavailable" : this.errorMessage : null;
3355
+ if (e === "ended") return this.isGone() ? this.endedMessage : this.player.endedReason === "interrupted" ? this.interruptedMessage : this.endedMessage;
3356
+ if (e === "offline") return this.offlineMessage;
3357
+ if (e === "waiting") return this.hasPlayed ? "Reconnecting — please stand by" : "Starting soon…";
3358
+ if (e === "error") {
3359
+ if (this.errorMessage !== void 0) return this.errorMessage;
3360
+ if (this.lastError?.code === "GEO_DENIED") return Kr(this.lastError.country);
3361
+ if (this.lastError?.code === "DRM_DEVICE_REVOKED") return qr;
3362
+ if (this.lastError?.code === "DRM_CLIENT") {
3363
+ if (this.lastError.detail === "keySystemNoAccess") return Jr(!!this.player.descriptor?.drm?.fairplay?.licenseUrl);
3364
+ if (this.lastError.detail === "keySystemStatusOutputRestricted") return Yr;
3365
+ }
3366
+ return this.lastError?.message ?? "Playback unavailable";
3367
+ }
3368
+ return null;
3303
3369
  }
3304
3370
  renderStatus(e) {
3305
3371
  let t = this.statusMessageFor(e);
@@ -3314,7 +3380,7 @@ var Wr = class {
3314
3380
  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);
3315
3381
  } else {
3316
3382
  let r = this.doc.createElement("div");
3317
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !Br.has(this.lastError.code)) {
3383
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !Gr.has(this.lastError.code)) {
3318
3384
  let e = this.doc.createElement("button");
3319
3385
  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);
3320
3386
  }
@@ -3355,7 +3421,7 @@ var Wr = class {
3355
3421
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
3356
3422
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
3357
3423
  }
3358
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Gr(this.video.currentTime)} / ${Gr(this.video.duration)}`);
3424
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Qr(this.video.currentTime)} / ${Qr(this.video.duration)}`);
3359
3425
  }
3360
3426
  syncLive() {
3361
3427
  let e = this.player.getStats(), t = e.isLive !== !1, n = this.isGone(), r = this.player.state === "ended" || n, i = !t && !this.streamEnded() && !n;
@@ -3405,17 +3471,17 @@ var Wr = class {
3405
3471
  renderQualityMenu() {
3406
3472
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
3407
3473
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
3408
- for (let n of e) this.menu.append(this.menuItem(Kr(n), t === n.index, () => this.player.setQuality(n.index)));
3474
+ for (let n of e) this.menu.append(this.menuItem($r(n), t === n.index, () => this.player.setQuality(n.index)));
3409
3475
  }
3410
3476
  renderAudioMenu() {
3411
3477
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
3412
3478
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
3413
- for (let t of e) this.menu.append(this.menuItem(qr(t), t.default, () => this.player.setAudioTrack(t.id)));
3479
+ for (let t of e) this.menu.append(this.menuItem(ei(t), t.default, () => this.player.setAudioTrack(t.id)));
3414
3480
  }
3415
3481
  renderCcMenu() {
3416
3482
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
3417
3483
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
3418
- for (let t of e) this.menu.append(this.menuItem(Jr(t), !1, () => this.player.setTextTrack(t.id)));
3484
+ for (let t of e) this.menu.append(this.menuItem(ti(t), !1, () => this.player.setTextTrack(t.id)));
3419
3485
  }
3420
3486
  onTracksChanged() {
3421
3487
  this.openMenu && this.renderMenu(this.openMenu);
@@ -3520,23 +3586,23 @@ var Wr = class {
3520
3586
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
3521
3587
  }
3522
3588
  };
3523
- function Gr(e) {
3589
+ function Qr(e) {
3524
3590
  (!Number.isFinite(e) || e < 0) && (e = 0);
3525
3591
  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");
3526
3592
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
3527
3593
  }
3528
- function Kr(e) {
3594
+ function $r(e) {
3529
3595
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
3530
3596
  }
3531
- function qr(e) {
3597
+ function ei(e) {
3532
3598
  return e.name || e.lang || `Track ${e.id}`;
3533
3599
  }
3534
- function Jr(e) {
3600
+ function ti(e) {
3535
3601
  return e.name || e.lang || `Track ${e.id}`;
3536
3602
  }
3537
3603
  //#endregion
3538
3604
  //#region src/managed/index.ts
3539
- var Yr = 6e3, Xr = 45e3, Zr = 2e3, Qr = 3, $r = 5e3, ei = 2e3, ti = 48e4, ni = "keySystemLicenseRequestFailed", ri = /* @__PURE__ */ new Set([
3605
+ var ni = 6e3, ri = 45e3, ii = 2e3, ai = 3, oi = 6e4, si = 2, ci = 6e5, li = 5e3, ui = 2e3, di = 48e4, fi = "keySystemLicenseRequestFailed", pi = /* @__PURE__ */ new Set([
3540
3606
  "GONE",
3541
3607
  "NOT_FOUND",
3542
3608
  "INVALID_URN",
@@ -3544,27 +3610,27 @@ var Yr = 6e3, Xr = 45e3, Zr = 2e3, Qr = 3, $r = 5e3, ei = 2e3, ti = 48e4, ni = "
3544
3610
  "GEO_DENIED",
3545
3611
  "DRM_DEVICE_REVOKED"
3546
3612
  ]);
3547
- function ii(e) {
3548
- return !ri.has(e);
3613
+ function mi(e) {
3614
+ return !pi.has(e);
3549
3615
  }
3550
- function ai(e) {
3616
+ function hi(e) {
3551
3617
  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)})`);
3552
3618
  if (e.env !== void 0) return lt(e.env);
3553
3619
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
3554
3620
  return e.baseUrl;
3555
3621
  }
3556
- async function oi(e) {
3622
+ async function gi(e) {
3557
3623
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
3558
- let t = ai(e);
3624
+ let t = hi(e);
3559
3625
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
3560
3626
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
3561
- let r = new si({
3627
+ let r = new _i({
3562
3628
  ...e,
3563
3629
  baseUrl: t
3564
3630
  });
3565
3631
  return await r.start(), r;
3566
3632
  }
3567
- var si = class {
3633
+ var _i = class {
3568
3634
  opts;
3569
3635
  abort = new AbortController();
3570
3636
  listeners = {};
@@ -3587,6 +3653,10 @@ var si = class {
3587
3653
  destroyed = !1;
3588
3654
  loadGen = 0;
3589
3655
  liveStateSub = null;
3656
+ liveStateLast = null;
3657
+ liveStateAliveAt = 0;
3658
+ staleEndReloads = 0;
3659
+ staleEndWindowAt = 0;
3590
3660
  endedHandled = !1;
3591
3661
  _endedReason = null;
3592
3662
  rejoinTimer = null;
@@ -3664,7 +3734,7 @@ var si = class {
3664
3734
  setTimeout(() => {
3665
3735
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
3666
3736
  phase: i ? "unavailable" : "tooEarly",
3667
- retryInMs: Xr,
3737
+ retryInMs: ri,
3668
3738
  attempt: 1,
3669
3739
  elapsedMs: 0,
3670
3740
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -3703,13 +3773,13 @@ var si = class {
3703
3773
  !a && !p() && h();
3704
3774
  }, e);
3705
3775
  };
3706
- _(Yr), this.armingSub = D({
3776
+ _(ni), this.armingSub = D({
3707
3777
  eventsBaseUrl: t,
3708
3778
  matchUrn: this.opts.matchUrn,
3709
3779
  credential: this.opts.credential,
3710
- onAlive: () => _(Xr),
3780
+ onAlive: () => _(ri),
3711
3781
  onState: (e) => {
3712
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Zr);
3782
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * ii);
3713
3783
  },
3714
3784
  onError: () => h()
3715
3785
  });
@@ -3754,19 +3824,19 @@ var si = class {
3754
3824
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
3755
3825
  let i = e.analytics?.sid;
3756
3826
  if (!i) return;
3757
- let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = Sr(this.opts.baseUrl, a, e.analytics?.endpoint);
3827
+ let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = Dr(this.opts.baseUrl, a, e.analytics?.endpoint);
3758
3828
  if (!o) {
3759
3829
  s && setTimeout(() => {
3760
3830
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, s));
3761
3831
  }, 0);
3762
3832
  return;
3763
3833
  }
3764
- let c = this.opts.video, l = new Cr({
3834
+ let c = this.opts.video, l = new Or({
3765
3835
  endpoint: o,
3766
3836
  sid: i,
3767
3837
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
3768
- sdkVersion: kr,
3769
- environment: Dr(c),
3838
+ sdkVersion: Pr,
3839
+ environment: Mr(c),
3770
3840
  idleMs: typeof this.opts.analytics == "object" ? this.opts.analytics.idleMs : void 0,
3771
3841
  onIdle: () => this.onAnalyticsIdle(t),
3772
3842
  onBlocked: (e) => setTimeout(() => {
@@ -3787,7 +3857,7 @@ var si = class {
3787
3857
  let u = () => l.noteSeek(), d = () => l.noteVolume(c.muted, c.volume);
3788
3858
  c.addEventListener("seeking", u), c.addEventListener("volumechange", d), this.detachMediaListeners = () => {
3789
3859
  c.removeEventListener("seeking", u), c.removeEventListener("volumechange", d);
3790
- }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), Or().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
3860
+ }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), Nr().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
3791
3861
  }
3792
3862
  currentPresentation() {
3793
3863
  let e = this.opts.video;
@@ -3811,6 +3881,7 @@ var si = class {
3811
3881
  adaptiveHoldBack: this.opts.adaptiveHoldBack,
3812
3882
  adaptiveHoldBackCeiling: this.opts.adaptiveHoldBackCeiling,
3813
3883
  stallCap: this.opts.stallCap,
3884
+ stuckReload: this.opts.stuckReload,
3814
3885
  mediaDuringKey: this.opts.mediaDuringKey,
3815
3886
  preloadHints: this.opts.preloadHints,
3816
3887
  debug: this.opts.debug,
@@ -3820,9 +3891,9 @@ var si = class {
3820
3891
  freshLicenseUrl: () => this.refreshLicense()
3821
3892
  }, o = this.opts.engine ?? "auto";
3822
3893
  if (o === "native") return new H(a);
3823
- if (o === "hls") return new nr(a);
3824
- if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && B(this.opts.video) && V() || !e.drmEnabled && At() && B(this.opts.video)) return new H(a);
3825
- if (Kt()) return new nr(a);
3894
+ if (o === "hls") return new sr(a);
3895
+ if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && B(this.opts.video) && V() || !e.drmEnabled && Pt() && B(this.opts.video)) return new H(a);
3896
+ if (Xt()) return new sr(a);
3826
3897
  if (B(this.opts.video)) return new H(a);
3827
3898
  throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
3828
3899
  }
@@ -3856,6 +3927,7 @@ var si = class {
3856
3927
  this.emit("texttrackchange", e.id);
3857
3928
  break;
3858
3929
  case "ended":
3930
+ if (e.reason === "stalled" && this.reloadStaleEnd()) break;
3859
3931
  this.onStreamEnded();
3860
3932
  break;
3861
3933
  case "error": {
@@ -3875,17 +3947,17 @@ var si = class {
3875
3947
  if (this.opts.liveStateEvents === !1) return;
3876
3948
  let t = this.opts.eventsBaseUrl ?? O(this.opts.baseUrl);
3877
3949
  if (!t) return;
3878
- this.closeLiveState();
3950
+ this.closeLiveState(), this.liveStateLast = null, this.liveStateAliveAt = 0;
3879
3951
  let i = 0, a = !1;
3880
3952
  this.liveStateSub = D({
3881
3953
  eventsBaseUrl: t,
3882
3954
  matchUrn: this.opts.matchUrn,
3883
3955
  credential: this.opts.credential,
3884
3956
  onState: (t) => {
3885
- this.destroyed || e !== this.loadGen || (t.state === "gone" ? this.onStreamEnded(!0, t.reason ?? "match_ended") : t.state === "ended" ? this.onStreamEnded(!1, t.reason) : t.state === "live" && this.endedHandled && this.scheduleRejoin(e));
3957
+ this.destroyed || e !== this.loadGen || (this.liveStateLast = t.state, this.liveStateAliveAt = Date.now(), t.state === "gone" ? this.onStreamEnded(!0, t.reason ?? "match_ended") : t.state === "ended" ? this.onStreamEnded(!1, t.reason) : t.state === "live" && this.endedHandled && this.scheduleRejoin(e));
3886
3958
  },
3887
3959
  onAlive: () => {
3888
- i = 0, a = !1;
3960
+ i = 0, a = !1, !this.destroyed && e === this.loadGen && (this.liveStateAliveAt = Date.now());
3889
3961
  },
3890
3962
  onError: (o) => {
3891
3963
  if (!(this.destroyed || e !== this.loadGen || a) && !this.endedHandled) {
@@ -3893,7 +3965,7 @@ var si = class {
3893
3965
  a = !0, this.emit("warning", new n(r(o.status), o.status, `live-state events answered HTTP ${o.status} for ${this.opts.matchUrn}: the match is ` + (o.terminal ? "gone, so the subscription stopped" : "not in the catalog (ended and dropped, or not listed yet), so it retries every 5 min") + " — end-of-stream detection falls back to the HLS staleness watchdog."));
3894
3966
  return;
3895
3967
  }
3896
- i += 1, i >= Qr && (a = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} after ${i} 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.`)));
3968
+ i += 1, i >= ai && (a = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} after ${i} 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.`)));
3897
3969
  }
3898
3970
  }
3899
3971
  });
@@ -3901,6 +3973,18 @@ var si = class {
3901
3973
  closeLiveState() {
3902
3974
  this.liveStateSub?.close(), this.liveStateSub = null;
3903
3975
  }
3976
+ reloadStaleEnd() {
3977
+ if (this.destroyed || this.endedHandled || this._state === "error" || this._state === "ended") return !1;
3978
+ let e = Date.now();
3979
+ if (this.liveStateLast !== "live" || e - this.liveStateAliveAt > oi || (e - this.staleEndWindowAt > ci && (this.staleEndReloads = 0, this.staleEndWindowAt = e), this.staleEndReloads >= si)) return !1;
3980
+ this.staleEndReloads += 1;
3981
+ let t = new n("UNAVAILABLE", 0, `live playlist stopped advancing for 12 s while the match is live (live-state push heard ${Math.round((e - this.liveStateAliveAt) / 1e3)} s ago): reloading instead of ending (${this.staleEndReloads} of ${si} this window)`);
3982
+ this.analyticsSession?.noteError(t.code, t.message, !1, 0, Math.round(this.opts.video.currentTime * 1e3), "staleEndReload");
3983
+ let r = this.loadGen;
3984
+ return this.emit("warning", t), Promise.resolve().then(() => {
3985
+ if (!(this.destroyed || r !== this.loadGen) && this._state !== "error" && this._state !== "ended") return this.reload().catch(() => void 0);
3986
+ }), !0;
3987
+ }
3904
3988
  watchConnectivity() {
3905
3989
  this.connectivity || (this.connectivity = ut(), this.connectivity.onChange((e) => {
3906
3990
  if (this.destroyed) return;
@@ -3910,7 +3994,7 @@ var si = class {
3910
3994
  }
3911
3995
  if (this.emit("online"), this._state !== "error" || !this._lastError) return;
3912
3996
  let t = this._lastError.code;
3913
- ii(t) && (t === "NETWORK" || this.failedWhileOffline) && this.retry().catch(() => void 0);
3997
+ mi(t) && (t === "NETWORK" || this.failedWhileOffline) && this.retry().catch(() => void 0);
3914
3998
  }));
3915
3999
  }
3916
4000
  watchReturn() {
@@ -3920,7 +4004,7 @@ var si = class {
3920
4004
  return;
3921
4005
  }
3922
4006
  let e = this.hiddenAtMs;
3923
- this.hiddenAtMs = null, e != null && Date.now() - e >= $r && this.resyncLiveState();
4007
+ this.hiddenAtMs = null, e != null && Date.now() - e >= li && this.resyncLiveState();
3924
4008
  }, this.onReturnPageShow = (e) => {
3925
4009
  e.persisted && this.resyncLiveState();
3926
4010
  }, document.addEventListener("visibilitychange", this.onReturnVisible), typeof window < "u" && window.addEventListener("pageshow", this.onReturnPageShow));
@@ -3952,7 +4036,7 @@ var si = class {
3952
4036
  scheduleRejoin(e) {
3953
4037
  this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
3954
4038
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
3955
- }, Math.random() * Zr));
4039
+ }, Math.random() * ii));
3956
4040
  }
3957
4041
  cancelRejoin() {
3958
4042
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -3976,7 +4060,7 @@ var si = class {
3976
4060
  }
3977
4061
  t.controls = !1;
3978
4062
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
3979
- r && r.insertBefore(n, t), n.appendChild(t), Mr(n, this.opts.theme), this.container = n, this.controlBar = Ur({
4063
+ r && r.insertBefore(n, t), n.appendChild(t), Lr(n, this.opts.theme), this.container = n, this.controlBar = Xr({
3980
4064
  root: n,
3981
4065
  video: t,
3982
4066
  player: this,
@@ -4015,7 +4099,7 @@ var si = class {
4015
4099
  }
4016
4100
  startStats() {
4017
4101
  this.stopStats();
4018
- let e = Math.max(500, this.opts.statsIntervalMs ?? ei);
4102
+ let e = Math.max(500, this.opts.statsIntervalMs ?? ui);
4019
4103
  this.statsTimer = setInterval(() => {
4020
4104
  if (!this.engine) return;
4021
4105
  let e = this.engine.getStats();
@@ -4026,14 +4110,14 @@ var si = class {
4026
4110
  this.statsTimer && clearInterval(this.statsTimer), this.statsTimer = void 0;
4027
4111
  }
4028
4112
  isStaleLicenseDenial(e) {
4029
- return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== ni || e.error.httpStatus !== 403 || e.error.code !== "FORBIDDEN" || e.error.serverCode === "license_refused" ? !1 : W(this._descriptor?.drm?.widevine?.licenseUrl);
4113
+ return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== fi || e.error.httpStatus !== 403 || e.error.code !== "FORBIDDEN" || e.error.serverCode === "license_refused" ? !1 : W(this._descriptor?.drm?.widevine?.licenseUrl);
4030
4114
  }
4031
4115
  scheduleLicenseRefresh() {
4032
4116
  if (this.stopRefresh(), !this._descriptor?.drmEnabled) return;
4033
4117
  typeof document < "u" && this.engine?.name === "hls" && (this.onRefocus = () => {
4034
4118
  document.visibilityState !== "visible" || this.destroyed || W(this._descriptor?.drm?.widevine?.licenseUrl) && this.refreshLicense();
4035
4119
  }, document.addEventListener("visibilitychange", this.onRefocus));
4036
- let e = this.opts.licenseRefreshMs ?? ti;
4120
+ let e = this.opts.licenseRefreshMs ?? di;
4037
4121
  e && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
4038
4122
  }
4039
4123
  stopRefresh() {
@@ -4203,7 +4287,7 @@ function $(e) {
4203
4287
  }
4204
4288
  //#endregion
4205
4289
  //#region src/embed/host.ts
4206
- function ci(e) {
4290
+ function vi(e) {
4207
4291
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
4208
4292
  if (!e.src) throw Error("mountEmbed: opts.src is required");
4209
4293
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -4262,15 +4346,15 @@ function ci(e) {
4262
4346
  }
4263
4347
  //#endregion
4264
4348
  //#region src/embed/page.ts
4265
- var li = /^[A-Za-z0-9:_.-]{1,128}$/;
4266
- function ui(e) {
4349
+ var yi = /^[A-Za-z0-9:_.-]{1,128}$/;
4350
+ function bi(e) {
4267
4351
  if (typeof e == "boolean") return e;
4268
4352
  if (typeof e == "object" && e) {
4269
4353
  let t = e.endpoint;
4270
4354
  return typeof t == "string" ? { endpoint: t } : {};
4271
4355
  }
4272
4356
  }
4273
- function di() {
4357
+ function xi() {
4274
4358
  let e = window.HAVIK_EMBED_CONFIG;
4275
4359
  if (e) return {
4276
4360
  baseUrl: e.baseUrl ?? "",
@@ -4280,7 +4364,7 @@ function di() {
4280
4364
  autoplay: e.autoplay ?? !0,
4281
4365
  muted: e.muted ?? !0,
4282
4366
  waitForLive: e.waitForLive ?? !0,
4283
- analytics: ui(e.analytics)
4367
+ analytics: bi(e.analytics)
4284
4368
  };
4285
4369
  let t = new URLSearchParams(location.search);
4286
4370
  return {
@@ -4293,9 +4377,9 @@ function di() {
4293
4377
  waitForLive: !0
4294
4378
  };
4295
4379
  }
4296
- async function fi() {
4297
- let e = di();
4298
- mi();
4380
+ async function Si() {
4381
+ let e = xi();
4382
+ wi();
4299
4383
  let t = document.createElement("video");
4300
4384
  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);
4301
4385
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -4351,12 +4435,12 @@ async function fi() {
4351
4435
  message: e.message,
4352
4436
  ...e.country === void 0 ? {} : { country: e.country }
4353
4437
  })), e.on("autoplayblocked", () => {
4354
- i({ type: "oddin:autoplayblocked" }), pi(t, e);
4438
+ i({ type: "oddin:autoplayblocked" }), Ci(t, e);
4355
4439
  });
4356
4440
  }, s = async (r) => {
4357
4441
  a?.destroy(), a = null;
4358
4442
  try {
4359
- let n = await oi({
4443
+ let n = await gi({
4360
4444
  video: t,
4361
4445
  baseUrl: e.baseUrl,
4362
4446
  matchUrn: r,
@@ -4419,7 +4503,7 @@ async function fi() {
4419
4503
  a?.retry();
4420
4504
  break;
4421
4505
  case "oddin:load":
4422
- typeof e.matchUrn == "string" && li.test(e.matchUrn) && s(e.matchUrn);
4506
+ typeof e.matchUrn == "string" && yi.test(e.matchUrn) && s(e.matchUrn);
4423
4507
  break;
4424
4508
  case "oddin:destroy": c();
4425
4509
  }
@@ -4431,18 +4515,18 @@ async function fi() {
4431
4515
  protocol: 1
4432
4516
  });
4433
4517
  }
4434
- function pi(e, t) {
4518
+ function Ci(e, t) {
4435
4519
  if (document.getElementById("havik-tap")) return;
4436
4520
  let n = document.createElement("button");
4437
4521
  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", () => {
4438
4522
  n.remove(), t.play();
4439
4523
  }), document.body.appendChild(n);
4440
4524
  }
4441
- function mi() {
4525
+ function wi() {
4442
4526
  let e = document.createElement("style");
4443
4527
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
4444
4528
  }
4445
4529
  //#endregion
4446
- export { E as LiveStateHttpError, Ar as ODDIN_THEME, n as PlaybackError, Mr as applyTheme, lt as baseUrlForEnv, fi as bootEmbed, ke as clearDeviceId, He as clearRememberedBandwidth, oi as createPlayer, O as deriveEventsBaseUrl, at as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ve as fetchCatalog, be as fetchTournament, Se as flattenMatches, C as fullscreenSupported, Oe as getDeviceId, st as isLowLatencyManifest, $ as isOddinMessage, je as licenseRequestHeaders, ci as mountEmbed, fe as pollToLive, y as resolveStream, D as subscribeLiveState, S as videoFullscreenActive, we as watchStatus };
4530
+ export { E as LiveStateHttpError, Fr as ODDIN_THEME, n as PlaybackError, Lr as applyTheme, lt as baseUrlForEnv, Si as bootEmbed, ke as clearDeviceId, He as clearRememberedBandwidth, gi as createPlayer, O as deriveEventsBaseUrl, at as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ve as fetchCatalog, be as fetchTournament, Se as flattenMatches, C as fullscreenSupported, Oe as getDeviceId, st as isLowLatencyManifest, $ as isOddinMessage, je as licenseRequestHeaders, vi as mountEmbed, fe as pollToLive, y as resolveStream, D as subscribeLiveState, S as videoFullscreenActive, we as watchStatus };
4447
4531
 
4448
4532
  //# sourceMappingURL=index.js.map