@oddin-gg/havik-player 2.21.0 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1484,21 +1484,31 @@ function Ft(e, t) {
1484
1484
  function It() {
1485
1485
  return e.isSupported();
1486
1486
  }
1487
- var Lt = 1e6, Rt = 20, zt = 1e4, Bt = 3, Vt = 5, Ht = 3, Ut = 1e4, Wt = 3e4, Gt = 12e3, Kt = 3e3, qt = .02, Jt = 6e3, Yt = .5, Xt = 3, Zt = .5, Qt = 1.1, $t = .25, en = 5e3, tn = 4e3, nn = 2, rn = 5, an = 2, on = 2, sn = 4, cn = 4, ln = 2e3, un = 3e4, dn = .2, fn = 30, pn = 2, mn = 2, hn = 9, gn = .5, _n = 64;
1488
- function vn(e) {
1487
+ var Lt = 1e6, Rt = 20, zt = 1e4, Bt = 3, Vt = 5, Ht = 3, Ut = 1e4, Wt = 3e4, Gt = 12e3, Kt = 3e3, qt = .02, Jt = 6e3, Yt = .5, Xt = 3, Zt = .5, Qt = 1.1, $t = .25, en = 5e3, tn = 4e3, nn = 2, rn = 5, an = 2, on = 2, sn = 4, cn = 4, ln = 2e3, un = 3e4, dn = 1, fn = 2;
1488
+ function pn(e, t, n) {
1489
+ if (n <= 0 || t < 0 || t >= e.length) return t;
1490
+ let r = (t, n) => e[t].width === e[n].width && e[t].height === e[n].height, i = (t) => {
1491
+ for (; t + 1 < e.length && r(t + 1, t);) t++;
1492
+ return t;
1493
+ }, a = i(t);
1494
+ for (let t = 0; t < n && a + 1 < e.length; t++) a = i(a + 1);
1495
+ return a;
1496
+ }
1497
+ var mn = .2, hn = 30, gn = 2, _n = 2, vn = 9, yn = .5, bn = 64;
1498
+ function xn(e) {
1489
1499
  if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
1490
1500
  delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
1491
1501
  return;
1492
1502
  }
1493
- e.liveSyncDuration ??= mn;
1494
- let t = Math.max(12, e.liveSyncDuration + hn);
1495
- (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), yn(e, e.liveSyncDuration);
1503
+ e.liveSyncDuration ??= _n;
1504
+ let t = Math.max(12, e.liveSyncDuration + vn);
1505
+ (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Sn(e, e.liveSyncDuration);
1496
1506
  }
1497
- function yn(e, t) {
1507
+ function Sn(e, t) {
1498
1508
  let n = (e, t, n) => Math.min(n, Math.max(t, e));
1499
1509
  e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
1500
1510
  }
1501
- var bn = class {
1511
+ var Cn = class {
1502
1512
  name = "hls";
1503
1513
  hls = null;
1504
1514
  deps;
@@ -1650,7 +1660,7 @@ var bn = class {
1650
1660
  let t = e.liveSyncPosition;
1651
1661
  if (typeof t != "number" || !Number.isFinite(t)) return;
1652
1662
  let { video: n } = this.deps;
1653
- n.currentTime <= t + gn || (n.currentTime = t);
1663
+ n.currentTime <= t + yn || (n.currentTime = t);
1654
1664
  }
1655
1665
  reanchorPartTimeline(e, t) {
1656
1666
  let { frag: n, part: r } = t;
@@ -1724,7 +1734,7 @@ var bn = class {
1724
1734
  }), t = Mt(e);
1725
1735
  t && (this.prefetch = e, f.fLoader = t);
1726
1736
  }
1727
- vn(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;
1737
+ xn(f), this.catchUpRate = typeof f.maxLiveSyncPlaybackRate == "number" ? f.maxLiveSyncPlaybackRate : 1, this.catchUpEngaged = !1, this.catchUpRate > 1 && (f.maxLiveSyncPlaybackRate = 1), this.sizeCapManaged = f.capLevelToPlayerSize !== !0, this.fpsCapManaged = !(c && "capLevelOnFPSDrop" in c), this.loadStarted = f.autoStartLoad !== !1;
1728
1738
  let p = new e(f);
1729
1739
  if (this.hls = p, p.on(e.Events.FRAG_PARSED, (e, t) => this.reanchorPartTimeline(p, t)), p.on(e.Events.MANIFEST_PARSED, () => {
1730
1740
  this.mediaRecoveries = 0, this.networkRecoveries = 0, u && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && this.dropLevelsWidevineCannotPlay(p), this.measureLadder(p), this.applySizeCap(), this.loadSuspended = !1, t.paused && this.schedulePauseSuspend(), l({ type: "ready" });
@@ -1739,7 +1749,7 @@ var bn = class {
1739
1749
  let n = t?.frag?.stats, r = n?.loaded;
1740
1750
  typeof r == "number" && (this.bytesLoaded += r), !this.reportedFirstBytes && typeof r == "number" && r > 0 && (this.reportedFirstBytes = !0, l({ type: "firstBytes" }));
1741
1751
  let i = n?.loading;
1742
- i && i.end > 0 && i.start > 0 && (this.requestCount += 1, this.requestTimesMs.push(Math.max(0, Math.round(i.end - i.start))), this.requestTimesMs.length > _n && this.requestTimesMs.shift());
1752
+ i && i.end > 0 && i.start > 0 && (this.requestCount += 1, this.requestTimesMs.push(Math.max(0, Math.round(i.end - i.start))), this.requestTimesMs.length > bn && this.requestTimesMs.shift());
1743
1753
  let a = t?.frag?.url;
1744
1754
  if (a) {
1745
1755
  let e = ot(a);
@@ -1812,8 +1822,12 @@ var bn = class {
1812
1822
  if (!e || e.levels.length === 0) return null;
1813
1823
  let { video: n } = this.deps, r = n.getBoundingClientRect(), i = r.width, a = r.height;
1814
1824
  if (!i && !a && (i = n.width || 0, a = n.height || 0), !(i > 0) || !(a > 0)) return null;
1815
- let o = e.config ?? {}, s = 1;
1816
- return !o.ignoreDevicePixelRatio && typeof self < "u" && self.devicePixelRatio > 0 && (s = self.devicePixelRatio), s = Math.min(s, o.maxDevicePixelRatio ?? Infinity), t.getMaxLevelByMediaSize(e.levels, i * s, a * s);
1825
+ let o = e.config ?? {}, s = typeof self < "u" && self.devicePixelRatio > 0 ? self.devicePixelRatio : 1, c = o.ignoreDevicePixelRatio ? 1 : s;
1826
+ c = Math.min(c, o.maxDevicePixelRatio ?? Infinity);
1827
+ let l = t.getMaxLevelByMediaSize(e.levels, i * c, a * c), u = this.deps.sizeCapHeadroom;
1828
+ if (u == null && s >= fn) return l;
1829
+ let d = u ?? dn, f = Number.isNaN(d) ? dn : d === Infinity ? e.levels.length : Math.max(0, Math.floor(d));
1830
+ return pn(e.levels, l, f);
1817
1831
  }
1818
1832
  onMediaResize() {
1819
1833
  if (!this.sizeCapManaged) return;
@@ -2023,15 +2037,15 @@ var bn = class {
2023
2037
  return;
2024
2038
  }
2025
2039
  let { totalFrames: n, dropped: r } = B(t), i = this.fpsLastFrames < 0, a = n - this.fpsLastFrames, o = r - this.fpsLastDropped;
2026
- if (this.fpsLastFrames = n, this.fpsLastDropped = r, i || a < fn || o < 0) {
2040
+ if (this.fpsLastFrames = n, this.fpsLastDropped = r, i || a < hn || o < 0) {
2027
2041
  this.fpsBadTicks = 0;
2028
2042
  return;
2029
2043
  }
2030
- if (o <= dn * a) {
2044
+ if (o <= mn * a) {
2031
2045
  this.fpsBadTicks = 0;
2032
2046
  return;
2033
2047
  }
2034
- if (this.fpsBadTicks += 1, this.fpsBadTicks < pn) return;
2048
+ if (this.fpsBadTicks += 1, this.fpsBadTicks < gn) return;
2035
2049
  this.fpsBadTicks = 0;
2036
2050
  let s = e.levels, c = s[e.currentLevel];
2037
2051
  if (!c) return;
@@ -2189,19 +2203,19 @@ var bn = class {
2189
2203
  let o = t.response?.code ?? 0, s = t.details ?? t.type, c = t.reason ?? t.error?.message ?? "", l = r === "DRM" && o === 403 && t.details === e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED ? i(Et(t.networkDetails)) : void 0;
2190
2204
  return new n(r === "DRM" ? a(o, l) : o === 401 ? "UNAUTHORIZED" : o === 403 ? "FORBIDDEN" : "INTERNAL", o, `${r} error: ${s}${c ? ` (${c})` : ""}`, l ? { serverCode: l } : void 0);
2191
2205
  }
2192
- }, xn = 15e3, Sn = 6e5, Cn = 3e5, wn = 100, q = 1, Tn = 1e3, En = 3e4, Dn = 1e3, On = {
2206
+ }, wn = 15e3, Tn = 6e5, En = 3e5, Dn = 100, q = 1, On = 1e3, kn = 3e4, An = 1e3, jn = {
2193
2207
  inline: "PRESENTATION_INLINE",
2194
2208
  fullscreen: "PRESENTATION_FULLSCREEN",
2195
2209
  pip: "PRESENTATION_PIP"
2196
- }, kn = {
2210
+ }, Mn = {
2197
2211
  widevine: "KEY_SYSTEM_WIDEVINE",
2198
2212
  fairplay: "KEY_SYSTEM_FAIRPLAY",
2199
2213
  none: "KEY_SYSTEM_NONE"
2200
2214
  };
2201
- function An(e, t) {
2215
+ function Nn(e, t) {
2202
2216
  return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
2203
2217
  }
2204
- var jn = /* @__PURE__ */ new Set([
2218
+ var Pn = /* @__PURE__ */ new Set([
2205
2219
  "INVALID_URN",
2206
2220
  "NOT_FOUND",
2207
2221
  "GEO_DENIED",
@@ -2222,7 +2236,7 @@ var jn = /* @__PURE__ */ new Set([
2222
2236
  ended: "PLAYER_STATE_ENDED",
2223
2237
  error: "PLAYER_STATE_ERROR"
2224
2238
  };
2225
- function Mn(e) {
2239
+ function Fn(e) {
2226
2240
  try {
2227
2241
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
2228
2242
  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);
@@ -2230,8 +2244,8 @@ function Mn(e) {
2230
2244
  return;
2231
2245
  }
2232
2246
  }
2233
- var Nn = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), Pn = /* @__PURE__ */ new Set(), Y = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), Fn = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
2234
- function In(e) {
2247
+ var In = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), Ln = /* @__PURE__ */ new Set(), Y = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), Rn = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
2248
+ function zn(e) {
2235
2249
  if (e.disposition !== "enforce" || e.effectiveDirective !== "connect-src") return;
2236
2250
  let t;
2237
2251
  try {
@@ -2240,19 +2254,19 @@ function In(e) {
2240
2254
  } catch {
2241
2255
  return;
2242
2256
  }
2243
- if (Pn.has(t) && !Y.has(t)) {
2244
- Y.add(t), _(Fn(new URL(t).origin));
2257
+ if (Ln.has(t) && !Y.has(t)) {
2258
+ Y.add(t), _(Rn(new URL(t).origin));
2245
2259
  for (let e of X) e.noteCspBlocked();
2246
2260
  }
2247
2261
  }
2248
- function Ln(e, t) {
2262
+ function Z(e, t) {
2249
2263
  return _(t, e), { disabledReason: t };
2250
2264
  }
2251
- function Rn(e, t, n) {
2252
- if (n) return t && !Nn(t, n) && _(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
2265
+ function Bn(e, t, n) {
2266
+ if (n) return t && !In(t, n) && _(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
2253
2267
  if (t) return { endpoint: t };
2254
- if (t !== void 0) return Ln("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).");
2255
- let r = Mn(e);
2268
+ if (t !== void 0) return Z("empty-endpoint", "analytics is disabled: no QoE beacons will be sent. analytics.endpoint was supplied but empty. Pass a real beacons origin, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, or omit the option to derive it from baseUrl (or analytics: false to opt out).");
2269
+ let r = Fn(e);
2256
2270
  if (r) return { endpoint: r };
2257
2271
  let i;
2258
2272
  try {
@@ -2260,9 +2274,9 @@ function Rn(e, t, n) {
2260
2274
  } catch {
2261
2275
  i = e;
2262
2276
  }
2263
- return Ln(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).`);
2277
+ return Z(i, `analytics is disabled: no QoE beacons will be sent. The beacons endpoint cannot be derived from the baseUrl host "${i}" — the convention is feed[-dev].<domain> → beacons[-dev].<domain>. Pass an explicit endpoint, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, to createPlayer (or analytics: false to opt out deliberately).`);
2264
2278
  }
2265
- var zn = class {
2279
+ var Vn = class {
2266
2280
  opts;
2267
2281
  url;
2268
2282
  cspKey;
@@ -2344,8 +2358,8 @@ var zn = class {
2344
2358
  constructor(e) {
2345
2359
  this.opts = {
2346
2360
  ...e,
2347
- heartbeatMs: e.heartbeatMs ?? xn,
2348
- idleMs: e.idleMs ?? Sn
2361
+ heartbeatMs: e.heartbeatMs ?? wn,
2362
+ idleMs: e.idleMs ?? Tn
2349
2363
  }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`;
2350
2364
  try {
2351
2365
  let e = new URL(this.url);
@@ -2362,11 +2376,11 @@ var zn = class {
2362
2376
  sdkVersion: this.opts.sdkVersion,
2363
2377
  ...this.opts.environment
2364
2378
  };
2365
- this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), Tn), this.lastTickMs = this.now(), this.timer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs), typeof window < "u" && window.addEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.addEventListener("visibilitychange", this.onVisibility), document.addEventListener("freeze", this.onFreeze), document.addEventListener("resume", this.onResume), this.cspKey && Pn.add(this.cspKey), document.addEventListener("securitypolicyviolation", In), X.add(this), this.noteCspBlocked(), this.onVisibility());
2379
+ this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), On), this.lastTickMs = this.now(), this.timer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs), typeof window < "u" && window.addEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.addEventListener("visibilitychange", this.onVisibility), document.addEventListener("freeze", this.onFreeze), document.addEventListener("resume", this.onResume), this.cspKey && Ln.add(this.cspKey), document.addEventListener("securitypolicyviolation", zn), X.add(this), this.noteCspBlocked(), this.onVisibility());
2366
2380
  }
2367
2381
  checkSuspension(e) {
2368
2382
  if (this.endedFlag || !this.opts.idleMs) return !1;
2369
- let t = Math.max(this.opts.idleMs, Cn);
2383
+ let t = Math.max(this.opts.idleMs, En);
2370
2384
  return this.lastTickMs > 0 && e - this.lastTickMs >= t ? (this.endIdle(this.lastTickMs), !0) : (this.lastTickMs = e, !1);
2371
2385
  }
2372
2386
  syncIdle(e) {
@@ -2389,12 +2403,12 @@ var zn = class {
2389
2403
  let a = {
2390
2404
  fromState: J[e] ?? "PLAYER_STATE_UNSPECIFIED",
2391
2405
  toState: J[t] ?? "PLAYER_STATE_UNSPECIFIED",
2392
- presentation: On[this.presentation]
2406
+ presentation: jn[this.presentation]
2393
2407
  }, o = this.causeOf(e, t, i);
2394
2408
  o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
2395
2409
  }
2396
2410
  seekInduced(e) {
2397
- return this.lastSeekMs >= 0 && e - this.lastSeekMs < Dn;
2411
+ return this.lastSeekMs >= 0 && e - this.lastSeekMs < An;
2398
2412
  }
2399
2413
  causeOf(e, t, n) {
2400
2414
  if (t === "error") return "TRANSITION_CAUSE_ERROR";
@@ -2468,7 +2482,7 @@ var zn = class {
2468
2482
  let r = n ?? "";
2469
2483
  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";
2470
2484
  if (e === "DRM_CLIENT" || e === "DRM_DEVICE_REVOKED") return "ERROR_STAGE_DRM";
2471
- if (jn.has(e)) return "ERROR_STAGE_RESOLVE";
2485
+ if (Pn.has(e)) return "ERROR_STAGE_RESOLVE";
2472
2486
  let i = t ?? "";
2473
2487
  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";
2474
2488
  }
@@ -2515,14 +2529,14 @@ var zn = class {
2515
2529
  if (this.lastRequestCount = g, _ > 0) {
2516
2530
  o.requestCountDelta = _;
2517
2531
  let e = t.requestTimesMs ?? [], n = e.slice(Math.max(0, e.length - _)).sort((e, t) => e - t);
2518
- n.length > 0 && (o.requestTimeP50Ms = An(n, 50), o.requestTimeP95Ms = An(n, 95));
2532
+ n.length > 0 && (o.requestTimeP50Ms = Nn(n, 50), o.requestTimeP95Ms = Nn(n, 95));
2519
2533
  }
2520
2534
  typeof t.latencySeconds == "number" && typeof t.targetLatencySeconds == "number" && (o.driftMs = Math.round((t.latencySeconds - t.targetLatencySeconds) * 1e3)), typeof t.playbackRate == "number" && t.playbackRate > 0 && (o.playbackRate = t.playbackRate), t.qualityPinned === !0 && (o.qualityPinned = !0), t.cdnHost && (o.cdnHost = t.cdnHost), this.maybeEmitMediaInfo(t), this.enqueue({ heartbeat: o }), this.flush(), s > 0 && this.noteFirstBytes();
2521
2535
  }
2522
2536
  maybeEmitMediaInfo(e) {
2523
2537
  if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
2524
2538
  let t = {};
2525
- e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = kn[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);
2539
+ e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = Mn[e.keySystem]), e.drmSecurityLevel && (t.drmSecurityLevel = e.drmSecurityLevel), e.ladderHeights?.length && (t.ladderHeights = e.ladderHeights), e.ladderTopBitrateKbps && (t.ladderTopBitrateKbps = e.ladderTopBitrateKbps), e.lowLatency && (t.lowLatency = !0);
2526
2540
  let n = JSON.stringify(t);
2527
2541
  n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
2528
2542
  }
@@ -2542,7 +2556,7 @@ var zn = class {
2542
2556
  let c = r?.positionMs ?? 0;
2543
2557
  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));
2544
2558
  let l = e !== "END_REASON_ENDED";
2545
- l && this.lastStallMs > 0 && n - this.lastStallMs <= En && (i.abandonAfterStallMs = Math.max(q, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= En && (i.abandonAfterErrorMs = Math.max(q, Math.round(n - this.lastErrorMs))), this.fullscreenTotalMs > 0 && (i.fullscreenTimeMs = Math.round(this.fullscreenTotalMs)), this.pipTotalMs > 0 && (i.pipTimeMs = Math.round(this.pipTotalMs)), this.volumeChanges > 0 && (i.volumeChanges = this.volumeChanges), this.muteChanges > 0 && (i.muteChanges = this.muteChanges);
2559
+ l && this.lastStallMs > 0 && n - this.lastStallMs <= kn && (i.abandonAfterStallMs = Math.max(q, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= kn && (i.abandonAfterErrorMs = Math.max(q, Math.round(n - this.lastErrorMs))), this.fullscreenTotalMs > 0 && (i.fullscreenTimeMs = Math.round(this.fullscreenTotalMs)), this.pipTotalMs > 0 && (i.pipTimeMs = Math.round(this.pipTotalMs)), this.volumeChanges > 0 && (i.volumeChanges = this.volumeChanges), this.muteChanges > 0 && (i.muteChanges = this.muteChanges);
2546
2560
  let u = r?.decodedFrames ?? this.lastDecoded;
2547
2561
  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");
2548
2562
  }
@@ -2562,13 +2576,13 @@ var zn = class {
2562
2576
  };
2563
2577
  }
2564
2578
  noteCspBlocked() {
2565
- this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(Fn(new URL(this.url).origin)));
2579
+ this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(Rn(new URL(this.url).origin)));
2566
2580
  }
2567
2581
  cspBlocked() {
2568
2582
  return !!this.cspKey && Y.has(this.cspKey);
2569
2583
  }
2570
2584
  enqueue(e) {
2571
- if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > wn;) {
2585
+ if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > Dn;) {
2572
2586
  let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
2573
2587
  this.pending.splice(e >= 0 ? e : 0, 1);
2574
2588
  }
@@ -2595,7 +2609,7 @@ var zn = class {
2595
2609
  };
2596
2610
  //#endregion
2597
2611
  //#region src/core/environment.ts
2598
- function Bn() {
2612
+ function Hn() {
2599
2613
  if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
2600
2614
  let e;
2601
2615
  try {
@@ -2620,14 +2634,14 @@ function Bn() {
2620
2634
  integration: "INTEGRATION_KIND_IFRAME"
2621
2635
  };
2622
2636
  }
2623
- function Vn() {
2637
+ function Un() {
2624
2638
  if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
2625
2639
  let e = navigator.userAgentData, t = e?.platform ?? "";
2626
2640
  if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
2627
2641
  let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
2628
2642
  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";
2629
2643
  }
2630
- function Hn() {
2644
+ function Wn() {
2631
2645
  let e = typeof navigator < "u" ? navigator.connection : void 0;
2632
2646
  if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
2633
2647
  let t = {
@@ -2646,17 +2660,17 @@ function Hn() {
2646
2660
  }
2647
2661
  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;
2648
2662
  }
2649
- function Un(e) {
2650
- let t = Bn(), n = {
2651
- deviceClass: Vn(),
2663
+ function Gn(e) {
2664
+ let t = Hn(), n = {
2665
+ deviceClass: Un(),
2652
2666
  integration: t.integration,
2653
- ...Hn()
2667
+ ...Wn()
2654
2668
  };
2655
2669
  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);
2656
2670
  let r = e.getBoundingClientRect?.();
2657
2671
  return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
2658
2672
  }
2659
- async function Wn() {
2673
+ async function Kn() {
2660
2674
  let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
2661
2675
  if (!e?.getHighEntropyValues) return {};
2662
2676
  try {
@@ -2668,7 +2682,7 @@ async function Wn() {
2668
2682
  }
2669
2683
  //#endregion
2670
2684
  //#region src/core/version.ts
2671
- var Gn = "2.21.0", Kn = {
2685
+ var qn = "2.22.0", Jn = {
2672
2686
  accent: "#E1B600",
2673
2687
  accentText: "#1B1C23",
2674
2688
  background: "#0e0f13",
@@ -2679,7 +2693,7 @@ var Gn = "2.21.0", Kn = {
2679
2693
  border: "rgba(255, 255, 255, 0.1)",
2680
2694
  radius: "8px",
2681
2695
  fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
2682
- }, qn = {
2696
+ }, Yn = {
2683
2697
  accent: "--havik-accent",
2684
2698
  accentText: "--havik-accent-text",
2685
2699
  background: "--havik-bg",
@@ -2691,26 +2705,26 @@ var Gn = "2.21.0", Kn = {
2691
2705
  radius: "--havik-radius",
2692
2706
  fontFamily: "--havik-font"
2693
2707
  };
2694
- function Jn(e, t) {
2708
+ function Xn(e, t) {
2695
2709
  let n = {
2696
- ...Kn,
2710
+ ...Jn,
2697
2711
  ...t
2698
2712
  };
2699
- for (let t of Object.keys(qn)) e.style.setProperty(qn[t], n[t]);
2713
+ for (let t of Object.keys(Yn)) e.style.setProperty(Yn[t], n[t]);
2700
2714
  n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
2701
2715
  }
2702
2716
  //#endregion
2703
2717
  //#region src/ui/styles.ts
2704
- var Z = "havik-player-styles", Yn = "\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", Xn = /* @__PURE__ */ new WeakMap();
2705
- function Zn(e = document, t) {
2706
- let n = e.querySelector(`style#${Z}`);
2707
- if (n && (!t || Xn.get(n) === t)) return;
2718
+ var Zn = "havik-player-styles", Qn = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer;\n touch-action:manipulation}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n/* Absolutely centred, NOT a flex sibling of .havik-center: the hidden center\n button (.havik-gone) still occupies its 74px in the row, which pushed an\n in-flow spinner 37px right of centre. */\n.havik-spinner{position:absolute;inset:0;margin:auto;width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n", $n = /* @__PURE__ */ new WeakMap();
2719
+ function er(e = document, t) {
2720
+ let n = e.querySelector(`style#${Zn}`);
2721
+ if (n && (!t || $n.get(n) === t)) return;
2708
2722
  let r = e.createElement("style");
2709
- r.id = Z, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = Yn, Xn.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
2723
+ r.id = Zn, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = Qn, $n.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
2710
2724
  }
2711
2725
  //#endregion
2712
2726
  //#region src/ui/icons.ts
2713
- var Qn = {
2727
+ var tr = {
2714
2728
  play: "<path d=\"M8 5v14l11-7z\"/>",
2715
2729
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
2716
2730
  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\"/>",
@@ -2724,11 +2738,11 @@ var Qn = {
2724
2738
  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\"/>"
2725
2739
  };
2726
2740
  function Q(e) {
2727
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${Qn[e]}</svg>`;
2741
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${tr[e]}</svg>`;
2728
2742
  }
2729
2743
  //#endregion
2730
2744
  //#region src/ui/controls.ts
2731
- var $n = 3e3, er = 250, tr = /* @__PURE__ */ new Set([
2745
+ var nr = 3e3, rr = 250, ir = /* @__PURE__ */ new Set([
2732
2746
  "GONE",
2733
2747
  "NOT_FOUND",
2734
2748
  "GEO_DENIED",
@@ -2736,15 +2750,15 @@ var $n = 3e3, er = 250, tr = /* @__PURE__ */ new Set([
2736
2750
  "UNAUTHORIZED",
2737
2751
  "DRM_DEVICE_REVOKED"
2738
2752
  ]);
2739
- function nr(e) {
2753
+ function ar(e) {
2740
2754
  let t = "This match isn't available in your country";
2741
2755
  return e ? `${t} (${e})` : t;
2742
2756
  }
2743
- var rr = "This device can't play protected video";
2744
- function ir(e) {
2745
- return new ar(e);
2757
+ var or = "This device can't play protected video";
2758
+ function sr(e) {
2759
+ return new cr(e);
2746
2760
  }
2747
- var ar = class {
2761
+ var cr = class {
2748
2762
  root;
2749
2763
  video;
2750
2764
  player;
@@ -2792,7 +2806,7 @@ var ar = class {
2792
2806
  scrubbing = !1;
2793
2807
  offFns = [];
2794
2808
  constructor(e) {
2795
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? $n, 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, Zn(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");
2809
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? nr, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.streamEnded = e.streamEnded ?? (() => !1), this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, er(this.doc, e.cspNonce), this.root.classList.add("havik-player"), this.root.hasAttribute("tabindex") || (this.root.tabIndex = 0), this.root.setAttribute("role", "region"), this.root.setAttribute("aria-label", "Video player");
2796
2810
  let t = (e, t, n) => {
2797
2811
  let r = this.doc.createElement(e);
2798
2812
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -2878,7 +2892,7 @@ var ar = class {
2878
2892
  let t = this.surfaceWasHidden;
2879
2893
  this.tapTimer = setTimeout(() => {
2880
2894
  this.tapTimer = void 0, this.toggleBar(t);
2881
- }, er);
2895
+ }, rr);
2882
2896
  }
2883
2897
  toggleBar(e) {
2884
2898
  e ? this.poke() : this.hideIfIdle();
@@ -2898,7 +2912,7 @@ var ar = class {
2898
2912
  this.statusOverlays && (e === "ended" ? t = "ended" : e === "error" ? t = "error" : e === "waiting" ? t = "waiting" : !this.hasPlayed && (e === "idle" || e === "paused") && (t = "preplay")), this.renderStatus(t);
2899
2913
  }
2900
2914
  statusMessageFor(e) {
2901
- return e === "ended" ? this.player.endedReason === "interrupted" ? this.interruptedMessage : this.endedMessage : e === "waiting" ? this.hasPlayed ? "Reconnecting — please stand by" : "Starting soon…" : e === "error" ? this.errorMessage === void 0 ? this.lastError?.code === "GEO_DENIED" ? nr(this.lastError.country) : this.lastError?.code === "DRM_DEVICE_REVOKED" ? rr : this.lastError?.message ?? "Playback unavailable" : this.errorMessage : null;
2915
+ return e === "ended" ? this.player.endedReason === "interrupted" ? this.interruptedMessage : this.endedMessage : e === "waiting" ? this.hasPlayed ? "Reconnecting — please stand by" : "Starting soon…" : e === "error" ? this.errorMessage === void 0 ? this.lastError?.code === "GEO_DENIED" ? ar(this.lastError.country) : this.lastError?.code === "DRM_DEVICE_REVOKED" ? or : this.lastError?.message ?? "Playback unavailable" : this.errorMessage : null;
2902
2916
  }
2903
2917
  renderStatus(e) {
2904
2918
  let t = this.statusMessageFor(e);
@@ -2913,7 +2927,7 @@ var ar = class {
2913
2927
  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);
2914
2928
  } else {
2915
2929
  let r = this.doc.createElement("div");
2916
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !tr.has(this.lastError.code)) {
2930
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !ir.has(this.lastError.code)) {
2917
2931
  let e = this.doc.createElement("button");
2918
2932
  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);
2919
2933
  }
@@ -2954,7 +2968,7 @@ var ar = class {
2954
2968
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
2955
2969
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
2956
2970
  }
2957
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${or(this.video.currentTime)} / ${or(this.video.duration)}`);
2971
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${lr(this.video.currentTime)} / ${lr(this.video.duration)}`);
2958
2972
  }
2959
2973
  syncLive() {
2960
2974
  let e = this.player.getStats(), t = e.isLive !== !1, n = this.player.state === "ended", r = !t && !this.streamEnded();
@@ -3004,17 +3018,17 @@ var ar = class {
3004
3018
  renderQualityMenu() {
3005
3019
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
3006
3020
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
3007
- for (let n of e) this.menu.append(this.menuItem(sr(n), t === n.index, () => this.player.setQuality(n.index)));
3021
+ for (let n of e) this.menu.append(this.menuItem(ur(n), t === n.index, () => this.player.setQuality(n.index)));
3008
3022
  }
3009
3023
  renderAudioMenu() {
3010
3024
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
3011
3025
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
3012
- for (let t of e) this.menu.append(this.menuItem(cr(t), t.default, () => this.player.setAudioTrack(t.id)));
3026
+ for (let t of e) this.menu.append(this.menuItem(dr(t), t.default, () => this.player.setAudioTrack(t.id)));
3013
3027
  }
3014
3028
  renderCcMenu() {
3015
3029
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
3016
3030
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
3017
- for (let t of e) this.menu.append(this.menuItem(lr(t), !1, () => this.player.setTextTrack(t.id)));
3031
+ for (let t of e) this.menu.append(this.menuItem(fr(t), !1, () => this.player.setTextTrack(t.id)));
3018
3032
  }
3019
3033
  onTracksChanged() {
3020
3034
  this.openMenu && this.renderMenu(this.openMenu);
@@ -3119,41 +3133,41 @@ var ar = class {
3119
3133
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
3120
3134
  }
3121
3135
  };
3122
- function or(e) {
3136
+ function lr(e) {
3123
3137
  (!Number.isFinite(e) || e < 0) && (e = 0);
3124
3138
  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");
3125
3139
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
3126
3140
  }
3127
- function sr(e) {
3141
+ function ur(e) {
3128
3142
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
3129
3143
  }
3130
- function cr(e) {
3144
+ function dr(e) {
3131
3145
  return e.name || e.lang || `Track ${e.id}`;
3132
3146
  }
3133
- function lr(e) {
3147
+ function fr(e) {
3134
3148
  return e.name || e.lang || `Track ${e.id}`;
3135
3149
  }
3136
3150
  //#endregion
3137
3151
  //#region src/managed/index.ts
3138
- var ur = 6e3, dr = 45e3, fr = 2e3, pr = 3, mr = 5e3, hr = 2e3, gr = 48e4, _r = "keySystemLicenseRequestFailed";
3139
- function vr(e) {
3152
+ var pr = 6e3, mr = 45e3, hr = 2e3, gr = 3, _r = 5e3, vr = 2e3, yr = 48e4, br = "keySystemLicenseRequestFailed";
3153
+ function xr(e) {
3140
3154
  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)})`);
3141
3155
  if (e.env !== void 0) return at(e.env);
3142
3156
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
3143
3157
  return e.baseUrl;
3144
3158
  }
3145
- async function yr(e) {
3159
+ async function Sr(e) {
3146
3160
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
3147
- let t = vr(e);
3161
+ let t = xr(e);
3148
3162
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
3149
3163
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
3150
- let r = new br({
3164
+ let r = new Cr({
3151
3165
  ...e,
3152
3166
  baseUrl: t
3153
3167
  });
3154
3168
  return await r.start(), r;
3155
3169
  }
3156
- var br = class {
3170
+ var Cr = class {
3157
3171
  opts;
3158
3172
  abort = new AbortController();
3159
3173
  listeners = {};
@@ -3248,7 +3262,7 @@ var br = class {
3248
3262
  setTimeout(() => {
3249
3263
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
3250
3264
  phase: i ? "unavailable" : "tooEarly",
3251
- retryInMs: dr,
3265
+ retryInMs: mr,
3252
3266
  attempt: 1,
3253
3267
  elapsedMs: 0,
3254
3268
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -3287,13 +3301,13 @@ var br = class {
3287
3301
  !a && !p() && h();
3288
3302
  }, e);
3289
3303
  };
3290
- _(ur), this.armingSub = E({
3304
+ _(pr), this.armingSub = E({
3291
3305
  eventsBaseUrl: t,
3292
3306
  matchUrn: this.opts.matchUrn,
3293
3307
  credential: this.opts.credential,
3294
- onAlive: () => _(dr),
3308
+ onAlive: () => _(mr),
3295
3309
  onState: (e) => {
3296
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * fr);
3310
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * hr);
3297
3311
  },
3298
3312
  onError: () => h()
3299
3313
  });
@@ -3338,19 +3352,19 @@ var br = class {
3338
3352
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
3339
3353
  let i = e.analytics?.sid;
3340
3354
  if (!i) return;
3341
- let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = Rn(this.opts.baseUrl, a, e.analytics?.endpoint);
3355
+ let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = Bn(this.opts.baseUrl, a, e.analytics?.endpoint);
3342
3356
  if (!o) {
3343
3357
  s && setTimeout(() => {
3344
3358
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, s));
3345
3359
  }, 0);
3346
3360
  return;
3347
3361
  }
3348
- let c = this.opts.video, l = new zn({
3362
+ let c = this.opts.video, l = new Vn({
3349
3363
  endpoint: o,
3350
3364
  sid: i,
3351
3365
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
3352
- sdkVersion: Gn,
3353
- environment: Un(c),
3366
+ sdkVersion: qn,
3367
+ environment: Gn(c),
3354
3368
  idleMs: typeof this.opts.analytics == "object" ? this.opts.analytics.idleMs : void 0,
3355
3369
  onIdle: () => this.onAnalyticsIdle(t),
3356
3370
  onBlocked: (e) => setTimeout(() => {
@@ -3371,7 +3385,7 @@ var br = class {
3371
3385
  let u = () => l.noteSeek(), d = () => l.noteVolume(c.muted, c.volume);
3372
3386
  c.addEventListener("seeking", u), c.addEventListener("volumechange", d), this.detachMediaListeners = () => {
3373
3387
  c.removeEventListener("seeking", u), c.removeEventListener("volumechange", d);
3374
- }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), Wn().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
3388
+ }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), Kn().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
3375
3389
  }
3376
3390
  currentPresentation() {
3377
3391
  let e = this.opts.video;
@@ -3391,6 +3405,7 @@ var br = class {
3391
3405
  lowLatency: this.opts.lowLatency === void 0 || this.opts.lowLatency === "auto" || this.opts.lowLatency,
3392
3406
  snapToLiveOnRefocus: this.opts.snapToLiveOnRefocus,
3393
3407
  rememberBandwidth: this.opts.rememberBandwidth,
3408
+ sizeCapHeadroom: this.opts.sizeCapHeadroom,
3394
3409
  mediaDuringKey: this.opts.mediaDuringKey,
3395
3410
  debug: this.opts.debug,
3396
3411
  deferLoad: r,
@@ -3399,9 +3414,9 @@ var br = class {
3399
3414
  freshLicenseUrl: () => this.refreshLicense()
3400
3415
  }, o = this.opts.engine ?? "auto";
3401
3416
  if (o === "native") return new W(a);
3402
- if (o === "hls") return new bn(a);
3417
+ if (o === "hls") return new Cn(a);
3403
3418
  if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && H(this.opts.video) && U() || !e.drmEnabled && St() && H(this.opts.video)) return new W(a);
3404
- if (It()) return new bn(a);
3419
+ if (It()) return new Cn(a);
3405
3420
  if (H(this.opts.video)) return new W(a);
3406
3421
  throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
3407
3422
  }
@@ -3464,7 +3479,7 @@ var br = class {
3464
3479
  r = 0, i = !1;
3465
3480
  },
3466
3481
  onError: () => {
3467
- this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= pr && (i = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} after ${r} attempts — end-of-stream detection degraded to the HLS staleness watchdog. Check the events host, the api-key's allowed origins, and CSP connect-src.`))));
3482
+ this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= gr && (i = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} after ${r} attempts — end-of-stream detection degraded to the HLS staleness watchdog. Check the events host, the api-key's allowed origins, and CSP connect-src.`))));
3468
3483
  }
3469
3484
  });
3470
3485
  }
@@ -3478,7 +3493,7 @@ var br = class {
3478
3493
  return;
3479
3494
  }
3480
3495
  let e = this.hiddenAtMs;
3481
- this.hiddenAtMs = null, e != null && Date.now() - e >= mr && this.resyncLiveState();
3496
+ this.hiddenAtMs = null, e != null && Date.now() - e >= _r && this.resyncLiveState();
3482
3497
  }, this.onReturnPageShow = (e) => {
3483
3498
  e.persisted && this.resyncLiveState();
3484
3499
  }, document.addEventListener("visibilitychange", this.onReturnVisible), typeof window < "u" && window.addEventListener("pageshow", this.onReturnPageShow));
@@ -3510,7 +3525,7 @@ var br = class {
3510
3525
  scheduleRejoin(e) {
3511
3526
  this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
3512
3527
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
3513
- }, Math.random() * fr));
3528
+ }, Math.random() * hr));
3514
3529
  }
3515
3530
  cancelRejoin() {
3516
3531
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -3534,7 +3549,7 @@ var br = class {
3534
3549
  }
3535
3550
  t.controls = !1;
3536
3551
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
3537
- r && r.insertBefore(n, t), n.appendChild(t), Jn(n, this.opts.theme), this.container = n, this.controlBar = ir({
3552
+ r && r.insertBefore(n, t), n.appendChild(t), Xn(n, this.opts.theme), this.container = n, this.controlBar = sr({
3538
3553
  root: n,
3539
3554
  video: t,
3540
3555
  player: this,
@@ -3572,7 +3587,7 @@ var br = class {
3572
3587
  }
3573
3588
  startStats() {
3574
3589
  this.stopStats();
3575
- let e = Math.max(500, this.opts.statsIntervalMs ?? hr);
3590
+ let e = Math.max(500, this.opts.statsIntervalMs ?? vr);
3576
3591
  this.statsTimer = setInterval(() => {
3577
3592
  if (!this.engine) return;
3578
3593
  let e = this.engine.getStats();
@@ -3583,14 +3598,14 @@ var br = class {
3583
3598
  this.statsTimer && clearInterval(this.statsTimer), this.statsTimer = void 0;
3584
3599
  }
3585
3600
  isStaleLicenseDenial(e) {
3586
- return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== _r || e.error.httpStatus !== 403 || e.error.code !== "FORBIDDEN" || e.error.serverCode === "license_refused" ? !1 : K(this._descriptor?.drm?.widevine?.licenseUrl);
3601
+ return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== br || e.error.httpStatus !== 403 || e.error.code !== "FORBIDDEN" || e.error.serverCode === "license_refused" ? !1 : K(this._descriptor?.drm?.widevine?.licenseUrl);
3587
3602
  }
3588
3603
  scheduleLicenseRefresh() {
3589
3604
  if (this.stopRefresh(), !this._descriptor?.drmEnabled) return;
3590
3605
  typeof document < "u" && this.engine?.name === "hls" && (this.onRefocus = () => {
3591
3606
  document.visibilityState !== "visible" || this.destroyed || K(this._descriptor?.drm?.widevine?.licenseUrl) && this.refreshLicense();
3592
3607
  }, document.addEventListener("visibilitychange", this.onRefocus));
3593
- let e = this.opts.licenseRefreshMs ?? gr;
3608
+ let e = this.opts.licenseRefreshMs ?? yr;
3594
3609
  e && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
3595
3610
  }
3596
3611
  stopRefresh() {
@@ -3760,7 +3775,7 @@ function $(e) {
3760
3775
  }
3761
3776
  //#endregion
3762
3777
  //#region src/embed/host.ts
3763
- function xr(e) {
3778
+ function wr(e) {
3764
3779
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
3765
3780
  if (!e.src) throw Error("mountEmbed: opts.src is required");
3766
3781
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -3819,15 +3834,15 @@ function xr(e) {
3819
3834
  }
3820
3835
  //#endregion
3821
3836
  //#region src/embed/page.ts
3822
- var Sr = /^[A-Za-z0-9:_.-]{1,128}$/;
3823
- function Cr(e) {
3837
+ var Tr = /^[A-Za-z0-9:_.-]{1,128}$/;
3838
+ function Er(e) {
3824
3839
  if (typeof e == "boolean") return e;
3825
3840
  if (typeof e == "object" && e) {
3826
3841
  let t = e.endpoint;
3827
3842
  return typeof t == "string" ? { endpoint: t } : {};
3828
3843
  }
3829
3844
  }
3830
- function wr() {
3845
+ function Dr() {
3831
3846
  let e = window.HAVIK_EMBED_CONFIG;
3832
3847
  if (e) return {
3833
3848
  baseUrl: e.baseUrl ?? "",
@@ -3837,7 +3852,7 @@ function wr() {
3837
3852
  autoplay: e.autoplay ?? !0,
3838
3853
  muted: e.muted ?? !0,
3839
3854
  waitForLive: e.waitForLive ?? !0,
3840
- analytics: Cr(e.analytics)
3855
+ analytics: Er(e.analytics)
3841
3856
  };
3842
3857
  let t = new URLSearchParams(location.search);
3843
3858
  return {
@@ -3850,9 +3865,9 @@ function wr() {
3850
3865
  waitForLive: !0
3851
3866
  };
3852
3867
  }
3853
- async function Tr() {
3854
- let e = wr();
3855
- Dr();
3868
+ async function Or() {
3869
+ let e = Dr();
3870
+ Ar();
3856
3871
  let t = document.createElement("video");
3857
3872
  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);
3858
3873
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -3908,12 +3923,12 @@ async function Tr() {
3908
3923
  message: e.message,
3909
3924
  ...e.country === void 0 ? {} : { country: e.country }
3910
3925
  })), e.on("autoplayblocked", () => {
3911
- i({ type: "oddin:autoplayblocked" }), Er(t, e);
3926
+ i({ type: "oddin:autoplayblocked" }), kr(t, e);
3912
3927
  });
3913
3928
  }, s = async (r) => {
3914
3929
  a?.destroy(), a = null;
3915
3930
  try {
3916
- let n = await yr({
3931
+ let n = await Sr({
3917
3932
  video: t,
3918
3933
  baseUrl: e.baseUrl,
3919
3934
  matchUrn: r,
@@ -3976,7 +3991,7 @@ async function Tr() {
3976
3991
  a?.retry();
3977
3992
  break;
3978
3993
  case "oddin:load":
3979
- typeof e.matchUrn == "string" && Sr.test(e.matchUrn) && s(e.matchUrn);
3994
+ typeof e.matchUrn == "string" && Tr.test(e.matchUrn) && s(e.matchUrn);
3980
3995
  break;
3981
3996
  case "oddin:destroy": c();
3982
3997
  }
@@ -3988,18 +4003,18 @@ async function Tr() {
3988
4003
  protocol: 1
3989
4004
  });
3990
4005
  }
3991
- function Er(e, t) {
4006
+ function kr(e, t) {
3992
4007
  if (document.getElementById("havik-tap")) return;
3993
4008
  let n = document.createElement("button");
3994
4009
  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", () => {
3995
4010
  n.remove(), t.play();
3996
4011
  }), document.body.appendChild(n);
3997
4012
  }
3998
- function Dr() {
4013
+ function Ar() {
3999
4014
  let e = document.createElement("style");
4000
4015
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
4001
4016
  }
4002
4017
  //#endregion
4003
- export { Kn as ODDIN_THEME, n as PlaybackError, Jn as applyTheme, at as baseUrlForEnv, Tr as bootEmbed, Ee as clearDeviceId, Re as clearRememberedBandwidth, yr as createPlayer, D as deriveEventsBaseUrl, tt as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ve as fetchCatalog, be as fetchTournament, Se as flattenMatches, C as fullscreenSupported, A as getDeviceId, rt as isLowLatencyManifest, $ as isOddinMessage, Oe as licenseRequestHeaders, xr as mountEmbed, fe as pollToLive, y as resolveStream, E as subscribeLiveState, S as videoFullscreenActive, we as watchStatus };
4018
+ export { Jn as ODDIN_THEME, n as PlaybackError, Xn as applyTheme, at as baseUrlForEnv, Or as bootEmbed, Ee as clearDeviceId, Re as clearRememberedBandwidth, Sr as createPlayer, D as deriveEventsBaseUrl, tt as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ve as fetchCatalog, be as fetchTournament, Se as flattenMatches, C as fullscreenSupported, A as getDeviceId, rt as isLowLatencyManifest, $ as isOddinMessage, Oe as licenseRequestHeaders, wr as mountEmbed, fe as pollToLive, y as resolveStream, E as subscribeLiveState, S as videoFullscreenActive, we as watchStatus };
4004
4019
 
4005
4020
  //# sourceMappingURL=index.js.map