@oddin-gg/havik-player 2.22.0 → 2.22.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
@@ -1494,21 +1494,21 @@ function pn(e, t, n) {
1494
1494
  for (let t = 0; t < n && a + 1 < e.length; t++) a = i(a + 1);
1495
1495
  return a;
1496
1496
  }
1497
- var mn = .2, hn = 30, gn = 2, _n = 2, vn = 9, yn = .5, bn = 64;
1498
- function xn(e) {
1497
+ var mn = .2, hn = 30, gn = 2, _n = 2, vn = 2, yn = 9, bn = .5, xn = 64;
1498
+ function Sn(e) {
1499
1499
  if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
1500
1500
  delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
1501
1501
  return;
1502
1502
  }
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);
1503
+ e.liveSyncDuration ??= vn;
1504
+ let t = Math.max(12, e.liveSyncDuration + yn);
1505
+ (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Cn(e, e.liveSyncDuration);
1506
1506
  }
1507
- function Sn(e, t) {
1507
+ function Cn(e, t) {
1508
1508
  let n = (e, t, n) => Math.min(n, Math.max(t, e));
1509
1509
  e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
1510
1510
  }
1511
- var Cn = class {
1511
+ var wn = class {
1512
1512
  name = "hls";
1513
1513
  hls = null;
1514
1514
  deps;
@@ -1584,6 +1584,7 @@ var Cn = class {
1584
1584
  fpsLastFrames = -1;
1585
1585
  fpsLastDropped = -1;
1586
1586
  fpsBadTicks = 0;
1587
+ fpsGraceTicks = 0;
1587
1588
  fpsDownCaps = 0;
1588
1589
  sizeCapManaged = !0;
1589
1590
  dprMedia = null;
@@ -1660,7 +1661,7 @@ var Cn = class {
1660
1661
  let t = e.liveSyncPosition;
1661
1662
  if (typeof t != "number" || !Number.isFinite(t)) return;
1662
1663
  let { video: n } = this.deps;
1663
- n.currentTime <= t + yn || (n.currentTime = t);
1664
+ n.currentTime <= t + bn || (n.currentTime = t);
1664
1665
  }
1665
1666
  reanchorPartTimeline(e, t) {
1666
1667
  let { frag: n, part: r } = t;
@@ -1734,7 +1735,7 @@ var Cn = class {
1734
1735
  }), t = Mt(e);
1735
1736
  t && (this.prefetch = e, f.fLoader = t);
1736
1737
  }
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;
1738
+ Sn(f), this.catchUpRate = typeof f.maxLiveSyncPlaybackRate == "number" ? f.maxLiveSyncPlaybackRate : 1, this.catchUpEngaged = !1, this.catchUpRate > 1 && (f.maxLiveSyncPlaybackRate = 1), this.sizeCapManaged = f.capLevelToPlayerSize !== !0, this.fpsCapManaged = !(c && "capLevelOnFPSDrop" in c), this.loadStarted = f.autoStartLoad !== !1;
1738
1739
  let p = new e(f);
1739
1740
  if (this.hls = p, p.on(e.Events.FRAG_PARSED, (e, t) => this.reanchorPartTimeline(p, t)), p.on(e.Events.MANIFEST_PARSED, () => {
1740
1741
  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" });
@@ -1749,7 +1750,7 @@ var Cn = class {
1749
1750
  let n = t?.frag?.stats, r = n?.loaded;
1750
1751
  typeof r == "number" && (this.bytesLoaded += r), !this.reportedFirstBytes && typeof r == "number" && r > 0 && (this.reportedFirstBytes = !0, l({ type: "firstBytes" }));
1751
1752
  let i = n?.loading;
1752
- i && i.end > 0 && i.start > 0 && (this.requestCount += 1, this.requestTimesMs.push(Math.max(0, Math.round(i.end - i.start))), this.requestTimesMs.length > bn && this.requestTimesMs.shift());
1753
+ i && i.end > 0 && i.start > 0 && (this.requestCount += 1, this.requestTimesMs.push(Math.max(0, Math.round(i.end - i.start))), this.requestTimesMs.length > xn && this.requestTimesMs.shift());
1753
1754
  let a = t?.frag?.url;
1754
1755
  if (a) {
1755
1756
  let e = ot(a);
@@ -1777,7 +1778,7 @@ var Cn = class {
1777
1778
  }, Kt), p.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1778
1779
  this.renditionSwitches += 1;
1779
1780
  let n = this.lastLevelIndex >= 0 ? p.levels[this.lastLevelIndex] : void 0, r = p.levels[t.level];
1780
- n && r && r.bitrate !== n.bitrate && (r.bitrate > n.bitrate ? this.upshifts += 1 : this.downshifts += 1), this.lastLevelIndex = t.level, l({
1781
+ n && r && r.bitrate !== n.bitrate && (r.bitrate > n.bitrate ? this.upshifts += 1 : this.downshifts += 1), this.lastLevelIndex = t.level, this.armFrameDropGrace(), l({
1781
1782
  type: "qualitychange",
1782
1783
  index: t.level
1783
1784
  });
@@ -2027,6 +2028,9 @@ var Cn = class {
2027
2028
  e.bitrate <= this.fpsCapBitrate && i && (n = r);
2028
2029
  }), n < 0 ? 0 : n;
2029
2030
  }
2031
+ armFrameDropGrace() {
2032
+ this.fpsGraceTicks > 0 || (this.fpsGraceTicks = _n, this.fpsBadTicks = 0);
2033
+ }
2030
2034
  resetFrameDropWindow() {
2031
2035
  this.fpsLastFrames = -1, this.fpsLastDropped = -1, this.fpsBadTicks = 0;
2032
2036
  }
@@ -2041,6 +2045,10 @@ var Cn = class {
2041
2045
  this.fpsBadTicks = 0;
2042
2046
  return;
2043
2047
  }
2048
+ if (this.fpsGraceTicks > 0) {
2049
+ --this.fpsGraceTicks, this.fpsBadTicks = 0;
2050
+ return;
2051
+ }
2044
2052
  if (o <= mn * a) {
2045
2053
  this.fpsBadTicks = 0;
2046
2054
  return;
@@ -2144,7 +2152,7 @@ var Cn = class {
2144
2152
  } catch (t) {
2145
2153
  console.warn(`havik-player: hls.js destroy() threw during ${e}`, t);
2146
2154
  }
2147
- this.hls = null, this.startHls();
2155
+ this.hls = null, this.startHls(), this.armFrameDropGrace();
2148
2156
  }
2149
2157
  }
2150
2158
  onError(t) {
@@ -2203,19 +2211,19 @@ var Cn = class {
2203
2211
  let o = t.response?.code ?? 0, s = t.details ?? t.type, c = t.reason ?? t.error?.message ?? "", l = r === "DRM" && o === 403 && t.details === e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED ? i(Et(t.networkDetails)) : void 0;
2204
2212
  return new n(r === "DRM" ? a(o, l) : o === 401 ? "UNAUTHORIZED" : o === 403 ? "FORBIDDEN" : "INTERNAL", o, `${r} error: ${s}${c ? ` (${c})` : ""}`, l ? { serverCode: l } : void 0);
2205
2213
  }
2206
- }, wn = 15e3, Tn = 6e5, En = 3e5, Dn = 100, q = 1, On = 1e3, kn = 3e4, An = 1e3, jn = {
2214
+ }, Tn = 15e3, En = 6e5, Dn = 3e5, On = 100, q = 1, kn = 1e3, An = 3e4, jn = 1e3, Mn = {
2207
2215
  inline: "PRESENTATION_INLINE",
2208
2216
  fullscreen: "PRESENTATION_FULLSCREEN",
2209
2217
  pip: "PRESENTATION_PIP"
2210
- }, Mn = {
2218
+ }, Nn = {
2211
2219
  widevine: "KEY_SYSTEM_WIDEVINE",
2212
2220
  fairplay: "KEY_SYSTEM_FAIRPLAY",
2213
2221
  none: "KEY_SYSTEM_NONE"
2214
2222
  };
2215
- function Nn(e, t) {
2223
+ function Pn(e, t) {
2216
2224
  return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
2217
2225
  }
2218
- var Pn = /* @__PURE__ */ new Set([
2226
+ var Fn = /* @__PURE__ */ new Set([
2219
2227
  "INVALID_URN",
2220
2228
  "NOT_FOUND",
2221
2229
  "GEO_DENIED",
@@ -2236,7 +2244,7 @@ var Pn = /* @__PURE__ */ new Set([
2236
2244
  ended: "PLAYER_STATE_ENDED",
2237
2245
  error: "PLAYER_STATE_ERROR"
2238
2246
  };
2239
- function Fn(e) {
2247
+ function In(e) {
2240
2248
  try {
2241
2249
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
2242
2250
  return !n || r.length === 0 || n !== "feed" && !n.startsWith("feed-") ? void 0 : (t.hostname = [n.replace(/^feed/, "beacons"), ...r].join("."), t.pathname = "", t.search = "", t.origin);
@@ -2244,8 +2252,8 @@ function Fn(e) {
2244
2252
  return;
2245
2253
  }
2246
2254
  }
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) {
2255
+ var Ln = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), Rn = /* @__PURE__ */ new Set(), Y = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), zn = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
2256
+ function Bn(e) {
2249
2257
  if (e.disposition !== "enforce" || e.effectiveDirective !== "connect-src") return;
2250
2258
  let t;
2251
2259
  try {
@@ -2254,19 +2262,19 @@ function zn(e) {
2254
2262
  } catch {
2255
2263
  return;
2256
2264
  }
2257
- if (Ln.has(t) && !Y.has(t)) {
2258
- Y.add(t), _(Rn(new URL(t).origin));
2265
+ if (Rn.has(t) && !Y.has(t)) {
2266
+ Y.add(t), _(zn(new URL(t).origin));
2259
2267
  for (let e of X) e.noteCspBlocked();
2260
2268
  }
2261
2269
  }
2262
2270
  function Z(e, t) {
2263
2271
  return _(t, e), { disabledReason: t };
2264
2272
  }
2265
- function Bn(e, t, n) {
2266
- if (n) return t && !In(t, n) && _(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
2273
+ function Vn(e, t, n) {
2274
+ if (n) return t && !Ln(t, n) && _(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
2267
2275
  if (t) return { endpoint: t };
2268
2276
  if (t !== void 0) return Z("empty-endpoint", "analytics is disabled: no QoE beacons will be sent. analytics.endpoint was supplied but empty. Pass a real beacons origin, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, or omit the option to derive it from baseUrl (or analytics: false to opt out).");
2269
- let r = Fn(e);
2277
+ let r = In(e);
2270
2278
  if (r) return { endpoint: r };
2271
2279
  let i;
2272
2280
  try {
@@ -2276,7 +2284,7 @@ function Bn(e, t, n) {
2276
2284
  }
2277
2285
  return Z(i, `analytics is disabled: no QoE beacons will be sent. The beacons endpoint cannot be derived from the baseUrl host "${i}" — the convention is feed[-dev].<domain> → beacons[-dev].<domain>. Pass an explicit endpoint, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, to createPlayer (or analytics: false to opt out deliberately).`);
2278
2286
  }
2279
- var Vn = class {
2287
+ var Hn = class {
2280
2288
  opts;
2281
2289
  url;
2282
2290
  cspKey;
@@ -2358,8 +2366,8 @@ var Vn = class {
2358
2366
  constructor(e) {
2359
2367
  this.opts = {
2360
2368
  ...e,
2361
- heartbeatMs: e.heartbeatMs ?? wn,
2362
- idleMs: e.idleMs ?? Tn
2369
+ heartbeatMs: e.heartbeatMs ?? Tn,
2370
+ idleMs: e.idleMs ?? En
2363
2371
  }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`;
2364
2372
  try {
2365
2373
  let e = new URL(this.url);
@@ -2376,11 +2384,11 @@ var Vn = class {
2376
2384
  sdkVersion: this.opts.sdkVersion,
2377
2385
  ...this.opts.environment
2378
2386
  };
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());
2387
+ this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), kn), this.lastTickMs = this.now(), this.timer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs), typeof window < "u" && window.addEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.addEventListener("visibilitychange", this.onVisibility), document.addEventListener("freeze", this.onFreeze), document.addEventListener("resume", this.onResume), this.cspKey && Rn.add(this.cspKey), document.addEventListener("securitypolicyviolation", Bn), X.add(this), this.noteCspBlocked(), this.onVisibility());
2380
2388
  }
2381
2389
  checkSuspension(e) {
2382
2390
  if (this.endedFlag || !this.opts.idleMs) return !1;
2383
- let t = Math.max(this.opts.idleMs, En);
2391
+ let t = Math.max(this.opts.idleMs, Dn);
2384
2392
  return this.lastTickMs > 0 && e - this.lastTickMs >= t ? (this.endIdle(this.lastTickMs), !0) : (this.lastTickMs = e, !1);
2385
2393
  }
2386
2394
  syncIdle(e) {
@@ -2403,12 +2411,12 @@ var Vn = class {
2403
2411
  let a = {
2404
2412
  fromState: J[e] ?? "PLAYER_STATE_UNSPECIFIED",
2405
2413
  toState: J[t] ?? "PLAYER_STATE_UNSPECIFIED",
2406
- presentation: jn[this.presentation]
2414
+ presentation: Mn[this.presentation]
2407
2415
  }, o = this.causeOf(e, t, i);
2408
2416
  o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
2409
2417
  }
2410
2418
  seekInduced(e) {
2411
- return this.lastSeekMs >= 0 && e - this.lastSeekMs < An;
2419
+ return this.lastSeekMs >= 0 && e - this.lastSeekMs < jn;
2412
2420
  }
2413
2421
  causeOf(e, t, n) {
2414
2422
  if (t === "error") return "TRANSITION_CAUSE_ERROR";
@@ -2482,7 +2490,7 @@ var Vn = class {
2482
2490
  let r = n ?? "";
2483
2491
  if (r !== "") return /^fragLoad/i.test(r) ? this.sawFirstBytes ? "ERROR_STAGE_SEGMENT" : "ERROR_STAGE_FIRST_SEGMENT" : /^(manifest|level|audioTrack|subtitleTrack)/i.test(r) ? "ERROR_STAGE_MANIFEST" : /key|licen[cs]e/i.test(r) ? "ERROR_STAGE_DRM" : /parsing|append|buffer|remux|decrypt|media/i.test(r) ? "ERROR_STAGE_DECODE" : "ERROR_STAGE_OTHER";
2484
2492
  if (e === "DRM_CLIENT" || e === "DRM_DEVICE_REVOKED") return "ERROR_STAGE_DRM";
2485
- if (Pn.has(e)) return "ERROR_STAGE_RESOLVE";
2493
+ if (Fn.has(e)) return "ERROR_STAGE_RESOLVE";
2486
2494
  let i = t ?? "";
2487
2495
  return /frag|segment/i.test(i) ? this.sawFirstBytes ? "ERROR_STAGE_SEGMENT" : "ERROR_STAGE_FIRST_SEGMENT" : /manifest|level|playlist/i.test(i) ? "ERROR_STAGE_MANIFEST" : /key|licen[cs]e/i.test(i) ? "ERROR_STAGE_DRM" : /buffer|decode|media|append/i.test(i) ? "ERROR_STAGE_DECODE" : "ERROR_STAGE_OTHER";
2488
2496
  }
@@ -2529,14 +2537,14 @@ var Vn = class {
2529
2537
  if (this.lastRequestCount = g, _ > 0) {
2530
2538
  o.requestCountDelta = _;
2531
2539
  let e = t.requestTimesMs ?? [], n = e.slice(Math.max(0, e.length - _)).sort((e, t) => e - t);
2532
- n.length > 0 && (o.requestTimeP50Ms = Nn(n, 50), o.requestTimeP95Ms = Nn(n, 95));
2540
+ n.length > 0 && (o.requestTimeP50Ms = Pn(n, 50), o.requestTimeP95Ms = Pn(n, 95));
2533
2541
  }
2534
2542
  typeof t.latencySeconds == "number" && typeof t.targetLatencySeconds == "number" && (o.driftMs = Math.round((t.latencySeconds - t.targetLatencySeconds) * 1e3)), typeof t.playbackRate == "number" && t.playbackRate > 0 && (o.playbackRate = t.playbackRate), t.qualityPinned === !0 && (o.qualityPinned = !0), t.cdnHost && (o.cdnHost = t.cdnHost), this.maybeEmitMediaInfo(t), this.enqueue({ heartbeat: o }), this.flush(), s > 0 && this.noteFirstBytes();
2535
2543
  }
2536
2544
  maybeEmitMediaInfo(e) {
2537
2545
  if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
2538
2546
  let t = {};
2539
- e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = Mn[e.keySystem]), e.drmSecurityLevel && (t.drmSecurityLevel = e.drmSecurityLevel), e.ladderHeights?.length && (t.ladderHeights = e.ladderHeights), e.ladderTopBitrateKbps && (t.ladderTopBitrateKbps = e.ladderTopBitrateKbps), e.lowLatency && (t.lowLatency = !0);
2547
+ e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = Nn[e.keySystem]), e.drmSecurityLevel && (t.drmSecurityLevel = e.drmSecurityLevel), e.ladderHeights?.length && (t.ladderHeights = e.ladderHeights), e.ladderTopBitrateKbps && (t.ladderTopBitrateKbps = e.ladderTopBitrateKbps), e.lowLatency && (t.lowLatency = !0);
2540
2548
  let n = JSON.stringify(t);
2541
2549
  n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
2542
2550
  }
@@ -2556,7 +2564,7 @@ var Vn = class {
2556
2564
  let c = r?.positionMs ?? 0;
2557
2565
  c > 0 && (i.exitPositionMs = Math.round(c)), this.activeTotalMs > 0 && (i.activeWatchTimeMs = Math.round(this.activeTotalMs)), this.mutedTotalMs > 0 && (i.mutedTimeMs = Math.round(this.mutedTotalMs));
2558
2566
  let l = e !== "END_REASON_ENDED";
2559
- l && this.lastStallMs > 0 && n - this.lastStallMs <= kn && (i.abandonAfterStallMs = Math.max(q, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= kn && (i.abandonAfterErrorMs = Math.max(q, Math.round(n - this.lastErrorMs))), this.fullscreenTotalMs > 0 && (i.fullscreenTimeMs = Math.round(this.fullscreenTotalMs)), this.pipTotalMs > 0 && (i.pipTimeMs = Math.round(this.pipTotalMs)), this.volumeChanges > 0 && (i.volumeChanges = this.volumeChanges), this.muteChanges > 0 && (i.muteChanges = this.muteChanges);
2567
+ l && this.lastStallMs > 0 && n - this.lastStallMs <= An && (i.abandonAfterStallMs = Math.max(q, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= An && (i.abandonAfterErrorMs = Math.max(q, Math.round(n - this.lastErrorMs))), this.fullscreenTotalMs > 0 && (i.fullscreenTimeMs = Math.round(this.fullscreenTotalMs)), this.pipTotalMs > 0 && (i.pipTimeMs = Math.round(this.pipTotalMs)), this.volumeChanges > 0 && (i.volumeChanges = this.volumeChanges), this.muteChanges > 0 && (i.muteChanges = this.muteChanges);
2560
2568
  let u = r?.decodedFrames ?? this.lastDecoded;
2561
2569
  u > 0 && (i.decodedFramesTotal = u), e === "END_REASON_STOPPED" && this.startupTimeMs === 0 && this.lastErrorStage === null && (e = "END_REASON_ABORTED", i.reason = e), this.startupTimeMs === 0 && e !== "END_REASON_ABORTED" && (i.failedStage = this.lastErrorStage ?? (this.manifestLoadMs === 0 ? "ERROR_STAGE_MANIFEST" : this.firstFragLoadMs === 0 ? "ERROR_STAGE_FIRST_SEGMENT" : "ERROR_STAGE_OTHER")), this.pending.push(this.wrap({ sessionEnd: i })), this.flush(e === "END_REASON_PAGEHIDE");
2562
2570
  }
@@ -2576,13 +2584,13 @@ var Vn = class {
2576
2584
  };
2577
2585
  }
2578
2586
  noteCspBlocked() {
2579
- this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(Rn(new URL(this.url).origin)));
2587
+ this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(zn(new URL(this.url).origin)));
2580
2588
  }
2581
2589
  cspBlocked() {
2582
2590
  return !!this.cspKey && Y.has(this.cspKey);
2583
2591
  }
2584
2592
  enqueue(e) {
2585
- if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > Dn;) {
2593
+ if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > On;) {
2586
2594
  let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
2587
2595
  this.pending.splice(e >= 0 ? e : 0, 1);
2588
2596
  }
@@ -2609,7 +2617,7 @@ var Vn = class {
2609
2617
  };
2610
2618
  //#endregion
2611
2619
  //#region src/core/environment.ts
2612
- function Hn() {
2620
+ function Un() {
2613
2621
  if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
2614
2622
  let e;
2615
2623
  try {
@@ -2634,14 +2642,14 @@ function Hn() {
2634
2642
  integration: "INTEGRATION_KIND_IFRAME"
2635
2643
  };
2636
2644
  }
2637
- function Un() {
2645
+ function Wn() {
2638
2646
  if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
2639
2647
  let e = navigator.userAgentData, t = e?.platform ?? "";
2640
2648
  if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
2641
2649
  let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
2642
2650
  return e?.mobile ?? (typeof navigator.maxTouchPoints == "number" && navigator.maxTouchPoints > 1 && n > 0 && n < 900) ? n >= 600 ? "DEVICE_CLASS_TABLET" : "DEVICE_CLASS_PHONE" : e || n >= 900 || /mac|win|linux|chrome os/i.test(t) ? "DEVICE_CLASS_DESKTOP" : "DEVICE_CLASS_OTHER";
2643
2651
  }
2644
- function Wn() {
2652
+ function Gn() {
2645
2653
  let e = typeof navigator < "u" ? navigator.connection : void 0;
2646
2654
  if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
2647
2655
  let t = {
@@ -2660,17 +2668,17 @@ function Wn() {
2660
2668
  }
2661
2669
  return typeof e.downlink == "number" && e.downlink > 0 && (i.downlinkKbps = Math.round(e.downlink * 1e3)), typeof e.rtt == "number" && e.rtt > 0 && (i.rttMs = Math.round(e.rtt)), e.saveData && (i.saveData = !0), i;
2662
2670
  }
2663
- function Gn(e) {
2664
- let t = Hn(), n = {
2665
- deviceClass: Un(),
2671
+ function Kn(e) {
2672
+ let t = Un(), n = {
2673
+ deviceClass: Wn(),
2666
2674
  integration: t.integration,
2667
- ...Wn()
2675
+ ...Gn()
2668
2676
  };
2669
2677
  t.host && (n.host = t.host), typeof screen < "u" && screen.width > 0 && (n.screenWidth = screen.width, n.screenHeight = screen.height), typeof window < "u" && window.devicePixelRatio > 0 && (n.devicePixelRatio = Math.round(window.devicePixelRatio * 100) / 100);
2670
2678
  let r = e.getBoundingClientRect?.();
2671
2679
  return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
2672
2680
  }
2673
- async function Kn() {
2681
+ async function qn() {
2674
2682
  let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
2675
2683
  if (!e?.getHighEntropyValues) return {};
2676
2684
  try {
@@ -2682,7 +2690,7 @@ async function Kn() {
2682
2690
  }
2683
2691
  //#endregion
2684
2692
  //#region src/core/version.ts
2685
- var qn = "2.22.0", Jn = {
2693
+ var Jn = "2.22.1", Yn = {
2686
2694
  accent: "#E1B600",
2687
2695
  accentText: "#1B1C23",
2688
2696
  background: "#0e0f13",
@@ -2693,7 +2701,7 @@ var qn = "2.22.0", Jn = {
2693
2701
  border: "rgba(255, 255, 255, 0.1)",
2694
2702
  radius: "8px",
2695
2703
  fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
2696
- }, Yn = {
2704
+ }, Xn = {
2697
2705
  accent: "--havik-accent",
2698
2706
  accentText: "--havik-accent-text",
2699
2707
  background: "--havik-bg",
@@ -2705,26 +2713,26 @@ var qn = "2.22.0", Jn = {
2705
2713
  radius: "--havik-radius",
2706
2714
  fontFamily: "--havik-font"
2707
2715
  };
2708
- function Xn(e, t) {
2716
+ function Zn(e, t) {
2709
2717
  let n = {
2710
- ...Jn,
2718
+ ...Yn,
2711
2719
  ...t
2712
2720
  };
2713
- for (let t of Object.keys(Yn)) e.style.setProperty(Yn[t], n[t]);
2721
+ for (let t of Object.keys(Xn)) e.style.setProperty(Xn[t], n[t]);
2714
2722
  n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
2715
2723
  }
2716
2724
  //#endregion
2717
2725
  //#region src/ui/styles.ts
2718
- var Zn = "havik-player-styles", Qn = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer;\n touch-action:manipulation}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n/* Absolutely centred, NOT a flex sibling of .havik-center: the hidden center\n button (.havik-gone) still occupies its 74px in the row, which pushed an\n in-flow spinner 37px right of centre. */\n.havik-spinner{position:absolute;inset:0;margin:auto;width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n", $n = /* @__PURE__ */ new WeakMap();
2719
- function er(e = document, t) {
2720
- let n = e.querySelector(`style#${Zn}`);
2721
- if (n && (!t || $n.get(n) === t)) return;
2726
+ var Qn = "havik-player-styles", $n = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer;\n touch-action:manipulation}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n/* Absolutely centred, NOT a flex sibling of .havik-center: the hidden center\n button (.havik-gone) still occupies its 74px in the row, which pushed an\n in-flow spinner 37px right of centre. */\n.havik-spinner{position:absolute;inset:0;margin:auto;width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n", er = /* @__PURE__ */ new WeakMap();
2727
+ function tr(e = document, t) {
2728
+ let n = e.querySelector(`style#${Qn}`);
2729
+ if (n && (!t || er.get(n) === t)) return;
2722
2730
  let r = e.createElement("style");
2723
- r.id = Zn, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = Qn, $n.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
2731
+ r.id = Qn, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = $n, er.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
2724
2732
  }
2725
2733
  //#endregion
2726
2734
  //#region src/ui/icons.ts
2727
- var tr = {
2735
+ var nr = {
2728
2736
  play: "<path d=\"M8 5v14l11-7z\"/>",
2729
2737
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
2730
2738
  volumeHigh: "<path d=\"M3 10v4h4l5 5V5L7 10H3zm13.5 2a4.5 4.5 0 0 0-2.5-4v8a4.5 4.5 0 0 0 2.5-4zm-2.5-9v2.06A7 7 0 0 1 14 19v2a9 9 0 0 0 0-18z\"/>",
@@ -2738,11 +2746,11 @@ var tr = {
2738
2746
  retry: "<path d=\"M17.65 6.35A8 8 0 1 0 19.73 13h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z\"/>"
2739
2747
  };
2740
2748
  function Q(e) {
2741
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${tr[e]}</svg>`;
2749
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${nr[e]}</svg>`;
2742
2750
  }
2743
2751
  //#endregion
2744
2752
  //#region src/ui/controls.ts
2745
- var nr = 3e3, rr = 250, ir = /* @__PURE__ */ new Set([
2753
+ var rr = 3e3, ir = 250, ar = /* @__PURE__ */ new Set([
2746
2754
  "GONE",
2747
2755
  "NOT_FOUND",
2748
2756
  "GEO_DENIED",
@@ -2750,15 +2758,15 @@ var nr = 3e3, rr = 250, ir = /* @__PURE__ */ new Set([
2750
2758
  "UNAUTHORIZED",
2751
2759
  "DRM_DEVICE_REVOKED"
2752
2760
  ]);
2753
- function ar(e) {
2761
+ function or(e) {
2754
2762
  let t = "This match isn't available in your country";
2755
2763
  return e ? `${t} (${e})` : t;
2756
2764
  }
2757
- var or = "This device can't play protected video";
2758
- function sr(e) {
2759
- return new cr(e);
2765
+ var sr = "This device can't play protected video";
2766
+ function cr(e) {
2767
+ return new lr(e);
2760
2768
  }
2761
- var cr = class {
2769
+ var lr = class {
2762
2770
  root;
2763
2771
  video;
2764
2772
  player;
@@ -2806,7 +2814,7 @@ var cr = class {
2806
2814
  scrubbing = !1;
2807
2815
  offFns = [];
2808
2816
  constructor(e) {
2809
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? nr, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.streamEnded = e.streamEnded ?? (() => !1), this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, er(this.doc, e.cspNonce), this.root.classList.add("havik-player"), this.root.hasAttribute("tabindex") || (this.root.tabIndex = 0), this.root.setAttribute("role", "region"), this.root.setAttribute("aria-label", "Video player");
2817
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? rr, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.streamEnded = e.streamEnded ?? (() => !1), this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, tr(this.doc, e.cspNonce), this.root.classList.add("havik-player"), this.root.hasAttribute("tabindex") || (this.root.tabIndex = 0), this.root.setAttribute("role", "region"), this.root.setAttribute("aria-label", "Video player");
2810
2818
  let t = (e, t, n) => {
2811
2819
  let r = this.doc.createElement(e);
2812
2820
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -2892,7 +2900,7 @@ var cr = class {
2892
2900
  let t = this.surfaceWasHidden;
2893
2901
  this.tapTimer = setTimeout(() => {
2894
2902
  this.tapTimer = void 0, this.toggleBar(t);
2895
- }, rr);
2903
+ }, ir);
2896
2904
  }
2897
2905
  toggleBar(e) {
2898
2906
  e ? this.poke() : this.hideIfIdle();
@@ -2912,7 +2920,7 @@ var cr = class {
2912
2920
  this.statusOverlays && (e === "ended" ? t = "ended" : e === "error" ? t = "error" : e === "waiting" ? t = "waiting" : !this.hasPlayed && (e === "idle" || e === "paused") && (t = "preplay")), this.renderStatus(t);
2913
2921
  }
2914
2922
  statusMessageFor(e) {
2915
- return e === "ended" ? this.player.endedReason === "interrupted" ? this.interruptedMessage : this.endedMessage : e === "waiting" ? this.hasPlayed ? "Reconnecting — please stand by" : "Starting soon…" : e === "error" ? this.errorMessage === void 0 ? this.lastError?.code === "GEO_DENIED" ? ar(this.lastError.country) : this.lastError?.code === "DRM_DEVICE_REVOKED" ? or : this.lastError?.message ?? "Playback unavailable" : this.errorMessage : null;
2923
+ return e === "ended" ? this.player.endedReason === "interrupted" ? this.interruptedMessage : this.endedMessage : e === "waiting" ? this.hasPlayed ? "Reconnecting — please stand by" : "Starting soon…" : e === "error" ? this.errorMessage === void 0 ? this.lastError?.code === "GEO_DENIED" ? or(this.lastError.country) : this.lastError?.code === "DRM_DEVICE_REVOKED" ? sr : this.lastError?.message ?? "Playback unavailable" : this.errorMessage : null;
2916
2924
  }
2917
2925
  renderStatus(e) {
2918
2926
  let t = this.statusMessageFor(e);
@@ -2927,7 +2935,7 @@ var cr = class {
2927
2935
  e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = Q("play"), e.addEventListener("click", () => void this.player.play()), n.append(e);
2928
2936
  } else {
2929
2937
  let r = this.doc.createElement("div");
2930
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !ir.has(this.lastError.code)) {
2938
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !ar.has(this.lastError.code)) {
2931
2939
  let e = this.doc.createElement("button");
2932
2940
  e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${Q("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), n.append(e);
2933
2941
  }
@@ -2968,7 +2976,7 @@ var cr = class {
2968
2976
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
2969
2977
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
2970
2978
  }
2971
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${lr(this.video.currentTime)} / ${lr(this.video.duration)}`);
2979
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${ur(this.video.currentTime)} / ${ur(this.video.duration)}`);
2972
2980
  }
2973
2981
  syncLive() {
2974
2982
  let e = this.player.getStats(), t = e.isLive !== !1, n = this.player.state === "ended", r = !t && !this.streamEnded();
@@ -3018,17 +3026,17 @@ var cr = class {
3018
3026
  renderQualityMenu() {
3019
3027
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
3020
3028
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
3021
- for (let n of e) this.menu.append(this.menuItem(ur(n), t === n.index, () => this.player.setQuality(n.index)));
3029
+ for (let n of e) this.menu.append(this.menuItem(dr(n), t === n.index, () => this.player.setQuality(n.index)));
3022
3030
  }
3023
3031
  renderAudioMenu() {
3024
3032
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
3025
3033
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
3026
- for (let t of e) this.menu.append(this.menuItem(dr(t), t.default, () => this.player.setAudioTrack(t.id)));
3034
+ for (let t of e) this.menu.append(this.menuItem(fr(t), t.default, () => this.player.setAudioTrack(t.id)));
3027
3035
  }
3028
3036
  renderCcMenu() {
3029
3037
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
3030
3038
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
3031
- for (let t of e) this.menu.append(this.menuItem(fr(t), !1, () => this.player.setTextTrack(t.id)));
3039
+ for (let t of e) this.menu.append(this.menuItem(pr(t), !1, () => this.player.setTextTrack(t.id)));
3032
3040
  }
3033
3041
  onTracksChanged() {
3034
3042
  this.openMenu && this.renderMenu(this.openMenu);
@@ -3133,41 +3141,41 @@ var cr = class {
3133
3141
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
3134
3142
  }
3135
3143
  };
3136
- function lr(e) {
3144
+ function ur(e) {
3137
3145
  (!Number.isFinite(e) || e < 0) && (e = 0);
3138
3146
  let t = Math.floor(e % 60), n = Math.floor(e / 60 % 60), r = Math.floor(e / 3600), i = r > 0 ? String(n).padStart(2, "0") : String(n), a = String(t).padStart(2, "0");
3139
3147
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
3140
3148
  }
3141
- function ur(e) {
3149
+ function dr(e) {
3142
3150
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
3143
3151
  }
3144
- function dr(e) {
3152
+ function fr(e) {
3145
3153
  return e.name || e.lang || `Track ${e.id}`;
3146
3154
  }
3147
- function fr(e) {
3155
+ function pr(e) {
3148
3156
  return e.name || e.lang || `Track ${e.id}`;
3149
3157
  }
3150
3158
  //#endregion
3151
3159
  //#region src/managed/index.ts
3152
- var pr = 6e3, mr = 45e3, hr = 2e3, gr = 3, _r = 5e3, vr = 2e3, yr = 48e4, br = "keySystemLicenseRequestFailed";
3153
- function xr(e) {
3160
+ var mr = 6e3, hr = 45e3, gr = 2e3, _r = 3, vr = 5e3, yr = 2e3, br = 48e4, xr = "keySystemLicenseRequestFailed";
3161
+ function Sr(e) {
3154
3162
  if (e.env !== void 0 && e.baseUrl !== void 0) throw new n("INTERNAL", 0, `createPlayer: pass env OR baseUrl, not both (got env=${JSON.stringify(e.env)} and baseUrl=${JSON.stringify(e.baseUrl)})`);
3155
3163
  if (e.env !== void 0) return at(e.env);
3156
3164
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
3157
3165
  return e.baseUrl;
3158
3166
  }
3159
- async function Sr(e) {
3167
+ async function Cr(e) {
3160
3168
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
3161
- let t = xr(e);
3169
+ let t = Sr(e);
3162
3170
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
3163
3171
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
3164
- let r = new Cr({
3172
+ let r = new wr({
3165
3173
  ...e,
3166
3174
  baseUrl: t
3167
3175
  });
3168
3176
  return await r.start(), r;
3169
3177
  }
3170
- var Cr = class {
3178
+ var wr = class {
3171
3179
  opts;
3172
3180
  abort = new AbortController();
3173
3181
  listeners = {};
@@ -3262,7 +3270,7 @@ var Cr = class {
3262
3270
  setTimeout(() => {
3263
3271
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
3264
3272
  phase: i ? "unavailable" : "tooEarly",
3265
- retryInMs: mr,
3273
+ retryInMs: hr,
3266
3274
  attempt: 1,
3267
3275
  elapsedMs: 0,
3268
3276
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -3301,13 +3309,13 @@ var Cr = class {
3301
3309
  !a && !p() && h();
3302
3310
  }, e);
3303
3311
  };
3304
- _(pr), this.armingSub = E({
3312
+ _(mr), this.armingSub = E({
3305
3313
  eventsBaseUrl: t,
3306
3314
  matchUrn: this.opts.matchUrn,
3307
3315
  credential: this.opts.credential,
3308
- onAlive: () => _(mr),
3316
+ onAlive: () => _(hr),
3309
3317
  onState: (e) => {
3310
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * hr);
3318
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * gr);
3311
3319
  },
3312
3320
  onError: () => h()
3313
3321
  });
@@ -3352,19 +3360,19 @@ var Cr = class {
3352
3360
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
3353
3361
  let i = e.analytics?.sid;
3354
3362
  if (!i) return;
3355
- let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = Bn(this.opts.baseUrl, a, e.analytics?.endpoint);
3363
+ let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = Vn(this.opts.baseUrl, a, e.analytics?.endpoint);
3356
3364
  if (!o) {
3357
3365
  s && setTimeout(() => {
3358
3366
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, s));
3359
3367
  }, 0);
3360
3368
  return;
3361
3369
  }
3362
- let c = this.opts.video, l = new Vn({
3370
+ let c = this.opts.video, l = new Hn({
3363
3371
  endpoint: o,
3364
3372
  sid: i,
3365
3373
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
3366
- sdkVersion: qn,
3367
- environment: Gn(c),
3374
+ sdkVersion: Jn,
3375
+ environment: Kn(c),
3368
3376
  idleMs: typeof this.opts.analytics == "object" ? this.opts.analytics.idleMs : void 0,
3369
3377
  onIdle: () => this.onAnalyticsIdle(t),
3370
3378
  onBlocked: (e) => setTimeout(() => {
@@ -3385,7 +3393,7 @@ var Cr = class {
3385
3393
  let u = () => l.noteSeek(), d = () => l.noteVolume(c.muted, c.volume);
3386
3394
  c.addEventListener("seeking", u), c.addEventListener("volumechange", d), this.detachMediaListeners = () => {
3387
3395
  c.removeEventListener("seeking", u), c.removeEventListener("volumechange", d);
3388
- }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), Kn().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
3396
+ }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), qn().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
3389
3397
  }
3390
3398
  currentPresentation() {
3391
3399
  let e = this.opts.video;
@@ -3414,9 +3422,9 @@ var Cr = class {
3414
3422
  freshLicenseUrl: () => this.refreshLicense()
3415
3423
  }, o = this.opts.engine ?? "auto";
3416
3424
  if (o === "native") return new W(a);
3417
- if (o === "hls") return new Cn(a);
3425
+ if (o === "hls") return new wn(a);
3418
3426
  if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && H(this.opts.video) && U() || !e.drmEnabled && St() && H(this.opts.video)) return new W(a);
3419
- if (It()) return new Cn(a);
3427
+ if (It()) return new wn(a);
3420
3428
  if (H(this.opts.video)) return new W(a);
3421
3429
  throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
3422
3430
  }
@@ -3479,7 +3487,7 @@ var Cr = class {
3479
3487
  r = 0, i = !1;
3480
3488
  },
3481
3489
  onError: () => {
3482
- this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= gr && (i = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} after ${r} attempts — end-of-stream detection degraded to the HLS staleness watchdog. Check the events host, the api-key's allowed origins, and CSP connect-src.`))));
3490
+ this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= _r && (i = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} after ${r} attempts — end-of-stream detection degraded to the HLS staleness watchdog. Check the events host, the api-key's allowed origins, and CSP connect-src.`))));
3483
3491
  }
3484
3492
  });
3485
3493
  }
@@ -3493,7 +3501,7 @@ var Cr = class {
3493
3501
  return;
3494
3502
  }
3495
3503
  let e = this.hiddenAtMs;
3496
- this.hiddenAtMs = null, e != null && Date.now() - e >= _r && this.resyncLiveState();
3504
+ this.hiddenAtMs = null, e != null && Date.now() - e >= vr && this.resyncLiveState();
3497
3505
  }, this.onReturnPageShow = (e) => {
3498
3506
  e.persisted && this.resyncLiveState();
3499
3507
  }, document.addEventListener("visibilitychange", this.onReturnVisible), typeof window < "u" && window.addEventListener("pageshow", this.onReturnPageShow));
@@ -3525,7 +3533,7 @@ var Cr = class {
3525
3533
  scheduleRejoin(e) {
3526
3534
  this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
3527
3535
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
3528
- }, Math.random() * hr));
3536
+ }, Math.random() * gr));
3529
3537
  }
3530
3538
  cancelRejoin() {
3531
3539
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -3549,7 +3557,7 @@ var Cr = class {
3549
3557
  }
3550
3558
  t.controls = !1;
3551
3559
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
3552
- r && r.insertBefore(n, t), n.appendChild(t), Xn(n, this.opts.theme), this.container = n, this.controlBar = sr({
3560
+ r && r.insertBefore(n, t), n.appendChild(t), Zn(n, this.opts.theme), this.container = n, this.controlBar = cr({
3553
3561
  root: n,
3554
3562
  video: t,
3555
3563
  player: this,
@@ -3587,7 +3595,7 @@ var Cr = class {
3587
3595
  }
3588
3596
  startStats() {
3589
3597
  this.stopStats();
3590
- let e = Math.max(500, this.opts.statsIntervalMs ?? vr);
3598
+ let e = Math.max(500, this.opts.statsIntervalMs ?? yr);
3591
3599
  this.statsTimer = setInterval(() => {
3592
3600
  if (!this.engine) return;
3593
3601
  let e = this.engine.getStats();
@@ -3598,14 +3606,14 @@ var Cr = class {
3598
3606
  this.statsTimer && clearInterval(this.statsTimer), this.statsTimer = void 0;
3599
3607
  }
3600
3608
  isStaleLicenseDenial(e) {
3601
- return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== br || e.error.httpStatus !== 403 || e.error.code !== "FORBIDDEN" || e.error.serverCode === "license_refused" ? !1 : K(this._descriptor?.drm?.widevine?.licenseUrl);
3609
+ return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== xr || e.error.httpStatus !== 403 || e.error.code !== "FORBIDDEN" || e.error.serverCode === "license_refused" ? !1 : K(this._descriptor?.drm?.widevine?.licenseUrl);
3602
3610
  }
3603
3611
  scheduleLicenseRefresh() {
3604
3612
  if (this.stopRefresh(), !this._descriptor?.drmEnabled) return;
3605
3613
  typeof document < "u" && this.engine?.name === "hls" && (this.onRefocus = () => {
3606
3614
  document.visibilityState !== "visible" || this.destroyed || K(this._descriptor?.drm?.widevine?.licenseUrl) && this.refreshLicense();
3607
3615
  }, document.addEventListener("visibilitychange", this.onRefocus));
3608
- let e = this.opts.licenseRefreshMs ?? yr;
3616
+ let e = this.opts.licenseRefreshMs ?? br;
3609
3617
  e && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
3610
3618
  }
3611
3619
  stopRefresh() {
@@ -3775,7 +3783,7 @@ function $(e) {
3775
3783
  }
3776
3784
  //#endregion
3777
3785
  //#region src/embed/host.ts
3778
- function wr(e) {
3786
+ function Tr(e) {
3779
3787
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
3780
3788
  if (!e.src) throw Error("mountEmbed: opts.src is required");
3781
3789
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -3834,15 +3842,15 @@ function wr(e) {
3834
3842
  }
3835
3843
  //#endregion
3836
3844
  //#region src/embed/page.ts
3837
- var Tr = /^[A-Za-z0-9:_.-]{1,128}$/;
3838
- function Er(e) {
3845
+ var Er = /^[A-Za-z0-9:_.-]{1,128}$/;
3846
+ function Dr(e) {
3839
3847
  if (typeof e == "boolean") return e;
3840
3848
  if (typeof e == "object" && e) {
3841
3849
  let t = e.endpoint;
3842
3850
  return typeof t == "string" ? { endpoint: t } : {};
3843
3851
  }
3844
3852
  }
3845
- function Dr() {
3853
+ function Or() {
3846
3854
  let e = window.HAVIK_EMBED_CONFIG;
3847
3855
  if (e) return {
3848
3856
  baseUrl: e.baseUrl ?? "",
@@ -3852,7 +3860,7 @@ function Dr() {
3852
3860
  autoplay: e.autoplay ?? !0,
3853
3861
  muted: e.muted ?? !0,
3854
3862
  waitForLive: e.waitForLive ?? !0,
3855
- analytics: Er(e.analytics)
3863
+ analytics: Dr(e.analytics)
3856
3864
  };
3857
3865
  let t = new URLSearchParams(location.search);
3858
3866
  return {
@@ -3865,9 +3873,9 @@ function Dr() {
3865
3873
  waitForLive: !0
3866
3874
  };
3867
3875
  }
3868
- async function Or() {
3869
- let e = Dr();
3870
- Ar();
3876
+ async function kr() {
3877
+ let e = Or();
3878
+ jr();
3871
3879
  let t = document.createElement("video");
3872
3880
  t.setAttribute("playsinline", ""), t.muted = e.muted ?? !0, t.style.cssText = "width:100%;height:100%;object-fit:contain;background:#000;aspect-ratio:auto", document.body.appendChild(t);
3873
3881
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -3923,12 +3931,12 @@ async function Or() {
3923
3931
  message: e.message,
3924
3932
  ...e.country === void 0 ? {} : { country: e.country }
3925
3933
  })), e.on("autoplayblocked", () => {
3926
- i({ type: "oddin:autoplayblocked" }), kr(t, e);
3934
+ i({ type: "oddin:autoplayblocked" }), Ar(t, e);
3927
3935
  });
3928
3936
  }, s = async (r) => {
3929
3937
  a?.destroy(), a = null;
3930
3938
  try {
3931
- let n = await Sr({
3939
+ let n = await Cr({
3932
3940
  video: t,
3933
3941
  baseUrl: e.baseUrl,
3934
3942
  matchUrn: r,
@@ -3991,7 +3999,7 @@ async function Or() {
3991
3999
  a?.retry();
3992
4000
  break;
3993
4001
  case "oddin:load":
3994
- typeof e.matchUrn == "string" && Tr.test(e.matchUrn) && s(e.matchUrn);
4002
+ typeof e.matchUrn == "string" && Er.test(e.matchUrn) && s(e.matchUrn);
3995
4003
  break;
3996
4004
  case "oddin:destroy": c();
3997
4005
  }
@@ -4003,18 +4011,18 @@ async function Or() {
4003
4011
  protocol: 1
4004
4012
  });
4005
4013
  }
4006
- function kr(e, t) {
4014
+ function Ar(e, t) {
4007
4015
  if (document.getElementById("havik-tap")) return;
4008
4016
  let n = document.createElement("button");
4009
4017
  n.id = "havik-tap", n.textContent = "▶ Tap to play", n.style.cssText = "position:fixed;inset:0;width:100%;height:100%;border:0;background:rgba(0,0,0,.55);color:#fff;font:600 18px system-ui,sans-serif;cursor:pointer;z-index:9", n.addEventListener("click", () => {
4010
4018
  n.remove(), t.play();
4011
4019
  }), document.body.appendChild(n);
4012
4020
  }
4013
- function Ar() {
4021
+ function jr() {
4014
4022
  let e = document.createElement("style");
4015
4023
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
4016
4024
  }
4017
4025
  //#endregion
4018
- export { Jn as ODDIN_THEME, n as PlaybackError, Xn as applyTheme, at as baseUrlForEnv, Or as bootEmbed, Ee as clearDeviceId, Re as clearRememberedBandwidth, Sr as createPlayer, D as deriveEventsBaseUrl, tt as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ve as fetchCatalog, be as fetchTournament, Se as flattenMatches, C as fullscreenSupported, A as getDeviceId, rt as isLowLatencyManifest, $ as isOddinMessage, Oe as licenseRequestHeaders, wr as mountEmbed, fe as pollToLive, y as resolveStream, E as subscribeLiveState, S as videoFullscreenActive, we as watchStatus };
4026
+ export { Yn as ODDIN_THEME, n as PlaybackError, Zn as applyTheme, at as baseUrlForEnv, kr as bootEmbed, Ee as clearDeviceId, Re as clearRememberedBandwidth, Cr as createPlayer, D as deriveEventsBaseUrl, tt as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ve as fetchCatalog, be as fetchTournament, Se as flattenMatches, C as fullscreenSupported, A as getDeviceId, rt as isLowLatencyManifest, $ as isOddinMessage, Oe as licenseRequestHeaders, Tr as mountEmbed, fe as pollToLive, y as resolveStream, E as subscribeLiveState, S as videoFullscreenActive, we as watchStatus };
4019
4027
 
4020
4028
  //# sourceMappingURL=index.js.map