@oddin-gg/havik-player 2.3.0 → 2.4.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
@@ -241,35 +241,35 @@ function ce(e, t) {
241
241
  }
242
242
  //#endregion
243
243
  //#region src/core/fullscreen.ts
244
- function le(e) {
244
+ function S(e) {
245
245
  return e.ownerDocument ?? document;
246
246
  }
247
- function ue(e) {
247
+ function le(e) {
248
248
  return e.fullscreenElement ?? e.webkitFullscreenElement ?? null;
249
249
  }
250
- function S(e, t) {
251
- let n = ue(le(e));
250
+ function C(e, t) {
251
+ let n = le(S(e));
252
252
  return n != null && (n === e || t != null && n === t) || e.webkitDisplayingFullscreen === !0;
253
253
  }
254
- async function C(e, t) {
254
+ async function w(e, t) {
255
255
  let n = t ?? e;
256
256
  if (typeof n.requestFullscreen == "function") return await n.requestFullscreen(), !0;
257
257
  if (typeof n.webkitRequestFullscreen == "function") return n.webkitRequestFullscreen(), !0;
258
258
  let r = e;
259
259
  return r.webkitSupportsFullscreen && typeof r.webkitEnterFullscreen == "function" ? (r.webkitEnterFullscreen(), !0) : !1;
260
260
  }
261
- async function w(e, t) {
261
+ async function T(e, t) {
262
262
  let n = e;
263
263
  if (n.webkitDisplayingFullscreen) {
264
264
  n.webkitExitFullscreen?.();
265
265
  return;
266
266
  }
267
- let r = le(e), i = ue(r);
267
+ let r = S(e), i = le(r);
268
268
  i != null && (i === e || t != null && i === t) && (typeof r.exitFullscreen == "function" ? await r.exitFullscreen() : r.webkitExitFullscreen?.());
269
269
  }
270
270
  //#endregion
271
271
  //#region src/core/catalog.ts
272
- async function de(e) {
272
+ async function ue(e) {
273
273
  let t = new URLSearchParams();
274
274
  e.status && t.set("status", Array.isArray(e.status) ? e.status.join(",") : e.status), e.sport && t.set("sport", e.sport);
275
275
  let i = t.toString(), a = c(e.baseUrl, "/v1/catalog" + (i ? `?${i}` : "")), o = new Headers();
@@ -287,12 +287,12 @@ async function de(e) {
287
287
  };
288
288
  if (!s.ok) throw new n(r(s.status), s.status, `catalog request failed (HTTP ${s.status})`);
289
289
  return {
290
- tournaments: fe(await s.json()),
290
+ tournaments: de(await s.json()),
291
291
  etag: s.headers.get("etag") ?? void 0,
292
292
  notModified: !1
293
293
  };
294
294
  }
295
- function fe(e) {
295
+ function de(e) {
296
296
  return (e.tournaments ?? []).map((e) => {
297
297
  let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
298
298
  return {
@@ -312,7 +312,7 @@ function fe(e) {
312
312
  };
313
313
  });
314
314
  }
315
- async function pe(e) {
315
+ async function fe(e) {
316
316
  let t = c(e.baseUrl, `/v1/tournaments/${encodeURIComponent(e.urn)}`), i = new Headers();
317
317
  e.etag && i.set("If-None-Match", e.etag);
318
318
  let a = await u(t, {
@@ -332,13 +332,13 @@ async function pe(e) {
332
332
  };
333
333
  if (!a.ok) throw new n(r(a.status), a.status, `tournament request failed (HTTP ${a.status})`);
334
334
  return {
335
- tournament: me(await a.json()),
335
+ tournament: pe(await a.json()),
336
336
  etag: a.headers.get("etag") ?? void 0,
337
337
  notModified: !1,
338
338
  notFound: !1
339
339
  };
340
340
  }
341
- function me(e) {
341
+ function pe(e) {
342
342
  let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
343
343
  return {
344
344
  urn: t,
@@ -356,12 +356,12 @@ function me(e) {
356
356
  }))
357
357
  };
358
358
  }
359
- function he(e) {
359
+ function me(e) {
360
360
  return e.tournaments.flatMap((e) => e.matches);
361
361
  }
362
362
  //#endregion
363
363
  //#region src/core/poll.ts
364
- function ge(e) {
364
+ function he(e) {
365
365
  let t = Math.max(2e3, e.intervalMs ?? 1e4), i = new AbortController(), a, o, s, l = !1, d = () => {
366
366
  l || (a = setTimeout(f, t));
367
367
  }, f = async () => {
@@ -397,7 +397,7 @@ function ge(e) {
397
397
  }
398
398
  //#endregion
399
399
  //#region src/core/events.ts
400
- function T(e) {
400
+ function E(e) {
401
401
  let t = Math.max(250, e.minBackoffMs ?? 1e3), n = Math.max(t, e.maxBackoffMs ?? 15e3), r = new AbortController(), i = !1, a = (e) => new Promise((t) => {
402
402
  if (r.signal.aborted) return t();
403
403
  let n = setTimeout(t, e);
@@ -464,7 +464,7 @@ function T(e) {
464
464
  i || (i = !0, r.abort());
465
465
  } };
466
466
  }
467
- function E(e) {
467
+ function D(e) {
468
468
  try {
469
469
  let t = new URL(e);
470
470
  return t.hostname = `events.${t.hostname}`, t.pathname = "", t.search = "", t.origin;
@@ -474,27 +474,27 @@ function E(e) {
474
474
  }
475
475
  //#endregion
476
476
  //#region src/core/drm/deviceId.ts
477
- var D = "havik.player.deviceId", O;
478
- function k() {
479
- if (O) return O;
477
+ var O = "havik.player.deviceId", k;
478
+ function A() {
479
+ if (k) return k;
480
480
  try {
481
- let e = globalThis.localStorage?.getItem(D);
482
- if (e) return O = e, e;
481
+ let e = globalThis.localStorage?.getItem(O);
482
+ if (e) return k = e, e;
483
483
  } catch {}
484
- let e = ve();
485
- O = e;
484
+ let e = _e();
485
+ k = e;
486
486
  try {
487
- globalThis.localStorage?.setItem(D, e);
487
+ globalThis.localStorage?.setItem(O, e);
488
488
  } catch {}
489
489
  return e;
490
490
  }
491
- function _e() {
492
- O = void 0;
491
+ function ge() {
492
+ k = void 0;
493
493
  try {
494
- globalThis.localStorage?.removeItem(D);
494
+ globalThis.localStorage?.removeItem(O);
495
495
  } catch {}
496
496
  }
497
- function ve() {
497
+ function _e() {
498
498
  let e = globalThis.crypto;
499
499
  if (e && typeof e.randomUUID == "function") return e.randomUUID();
500
500
  let t = /* @__PURE__ */ new Uint8Array(16);
@@ -506,21 +506,21 @@ function ve() {
506
506
  }
507
507
  //#endregion
508
508
  //#region src/core/drm/headers.ts
509
- function ye(e, t, n = {}) {
509
+ function ve(e, t, n = {}) {
510
510
  let r = {
511
511
  "x-api-key": t.apiKey,
512
512
  "X-Match-Urn": e.matchUrn,
513
- "X-Device-Id": n.deviceId ?? k(),
513
+ "X-Device-Id": n.deviceId ?? A(),
514
514
  "Content-Type": "application/octet-stream"
515
515
  };
516
516
  return n.userId && (r["X-User-Id"] = n.userId), r;
517
517
  }
518
518
  //#endregion
519
519
  //#region src/core/drm/cdm.ts
520
- var A = /* @__PURE__ */ new WeakMap();
521
- function be(e, t) {
522
- let n = A.get(e);
523
- n || (n = /* @__PURE__ */ new Map(), A.set(e, n));
520
+ var j = /* @__PURE__ */ new WeakMap();
521
+ function ye(e, t) {
522
+ let n = j.get(e);
523
+ n || (n = /* @__PURE__ */ new Map(), j.set(e, n));
524
524
  let r = n.get(t.keySystem);
525
525
  if (r) return r;
526
526
  let i = t.createMediaKeys().catch((e) => {
@@ -528,22 +528,22 @@ function be(e, t) {
528
528
  });
529
529
  return n.set(t.keySystem, i), i;
530
530
  }
531
- function j() {
531
+ function M() {
532
532
  return typeof navigator < "u" && typeof navigator.requestMediaKeySystemAccess == "function";
533
533
  }
534
- var M = ["SW_SECURE_DECODE", "SW_SECURE_CRYPTO"];
535
- function xe(e) {
534
+ var N = ["SW_SECURE_DECODE", "SW_SECURE_CRYPTO"];
535
+ function be(e) {
536
536
  return e.startsWith("com.widevine");
537
537
  }
538
- function N(e, t) {
539
- if (!xe(e)) return t;
538
+ function P(e, t) {
539
+ if (!be(e)) return t;
540
540
  let n = (e, t) => {
541
541
  let n = e.videoCapabilities;
542
542
  return !n?.length || !n.some((e) => e.robustness) ? !1 : n.every((e) => {
543
- let n = M.indexOf(e.robustness);
544
- return n === -1 || M.indexOf(t) > n;
543
+ let n = N.indexOf(e.robustness);
544
+ return n === -1 || N.indexOf(t) > n;
545
545
  });
546
- }, r = M.flatMap((e) => t.filter((t) => n(t, e)).map((t) => ({
546
+ }, r = N.flatMap((e) => t.filter((t) => n(t, e)).map((t) => ({
547
547
  ...t,
548
548
  videoCapabilities: t.videoCapabilities?.map((t) => ({
549
549
  ...t,
@@ -552,16 +552,16 @@ function N(e, t) {
552
552
  })));
553
553
  return [...t, ...r];
554
554
  }
555
- function Se(e) {
556
- return (t, n) => j() ? navigator.requestMediaKeySystemAccess(t, N(t, n)).then((t) => ({
555
+ function xe(e) {
556
+ return (t, n) => M() ? navigator.requestMediaKeySystemAccess(t, P(t, n)).then((t) => ({
557
557
  keySystem: t.keySystem,
558
558
  getConfiguration: () => t.getConfiguration(),
559
- createMediaKeys: () => be(e, t)
559
+ createMediaKeys: () => ye(e, t)
560
560
  })) : Promise.reject(/* @__PURE__ */ Error("navigator.requestMediaKeySystemAccess is unavailable — EME needs a secure context (https, or localhost)"));
561
561
  }
562
562
  //#endregion
563
563
  //#region src/core/drm/support.ts
564
- var Ce = 5e3, we = "video/mp4; codecs=\"avc1.42E01E\"", P = "audio/mp4; codecs=\"mp4a.40.2\"", Te = {
564
+ var Se = 5e3, Ce = "video/mp4; codecs=\"avc1.42E01E\"", we = "audio/mp4; codecs=\"mp4a.40.2\"", Te = {
565
565
  widevine: ["com.widevine.alpha"],
566
566
  fairplay: ["com.apple.fps"]
567
567
  };
@@ -573,24 +573,24 @@ function Ee(e) {
573
573
  "cenc"
574
574
  ],
575
575
  videoCapabilities: [{
576
- contentType: we,
576
+ contentType: Ce,
577
577
  robustness: "",
578
578
  encryptionScheme: "cbcs"
579
579
  }],
580
580
  audioCapabilities: [{
581
- contentType: P,
581
+ contentType: we,
582
582
  robustness: "",
583
583
  encryptionScheme: "cbcs"
584
584
  }]
585
- }] : N("com.widevine.alpha", [{
585
+ }] : P("com.widevine.alpha", [{
586
586
  initDataTypes: ["cenc"],
587
587
  videoCapabilities: [{
588
- contentType: we,
588
+ contentType: Ce,
589
589
  robustness: "SW_SECURE_DECODE",
590
590
  encryptionScheme: "cbcs"
591
591
  }],
592
592
  audioCapabilities: [{
593
- contentType: P,
593
+ contentType: we,
594
594
  robustness: "SW_SECURE_CRYPTO",
595
595
  encryptionScheme: "cbcs"
596
596
  }]
@@ -607,7 +607,7 @@ function F(e, t) {
607
607
  });
608
608
  }
609
609
  async function De(e = {}) {
610
- let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? Ce, r = {};
610
+ let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? Se, r = {};
611
611
  if (typeof window < "u" && window.isSecureContext === !1) return {
612
612
  verdict: "insecure-context",
613
613
  detail: r
@@ -672,7 +672,7 @@ var Ae = /\.(m3u8|ts|m4s|mp4|cmfv|cmfa)(\?|$)/i;
672
672
  function je(e) {
673
673
  if (typeof performance > "u" || typeof performance.getEntriesByType != "function") return "";
674
674
  let t = "", n = -1, r = performance.getEntriesByType("resource");
675
- for (let i of r) if (!(i.startTime < e) && !(i.initiatorType !== "video" && i.initiatorType !== "audio" && !Ae.test(i.name)) && i.startTime >= n) {
675
+ for (let i of r) if (!(i.startTime < e) && (i.initiatorType === "video" || i.initiatorType === "audio" || Ae.test(i.name)) && i.startTime >= n) {
676
676
  let e = L(i.name);
677
677
  e && (t = e, n = i.startTime);
678
678
  }
@@ -776,7 +776,7 @@ var V = class {
776
776
  });
777
777
  this.onEncrypted = (n) => {
778
778
  let i = n;
779
- !i.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV = Ne(i.initData), (async () => {
779
+ i.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = Ne(i.initData), (async () => {
780
780
  try {
781
781
  let n = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
782
782
  initDataTypes: [i.initDataType],
@@ -1034,7 +1034,7 @@ var U = class {
1034
1034
  videoEncryptionScheme: "cbcs",
1035
1035
  audioEncryptionScheme: "cbcs"
1036
1036
  },
1037
- ...j() ? { requestMediaKeySystemAccessFunc: Se(t) } : {},
1037
+ ...M() ? { requestMediaKeySystemAccessFunc: xe(t) } : {},
1038
1038
  licenseXhrSetup: (e, t) => {
1039
1039
  e.open("POST", this.licenseUrl ?? t, !0), e.setRequestHeader("x-api-key", r.apiKey), e.setRequestHeader("X-Match-Urn", n.matchUrn), e.setRequestHeader("X-Device-Id", i), a && e.setRequestHeader("X-User-Id", a), this.licenseRequested = !0;
1040
1040
  let o = performance.now();
@@ -1377,11 +1377,11 @@ var U = class {
1377
1377
  let r = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
1378
1378
  return new n(t === "DRM" ? i(r) : r === 401 ? "UNAUTHORIZED" : r === 403 ? "FORBIDDEN" : "INTERNAL", r, `${t} error: ${a}${o ? ` (${o})` : ""}`);
1379
1379
  }
1380
- }, tt = 15e3, nt = 100, W = 1, rt = 1e3, G = 3e4, it = {
1380
+ }, tt = 15e3, nt = 100, W = 1, rt = 1e3, G = 3e4, it = 1e3, at = {
1381
1381
  inline: "PRESENTATION_INLINE",
1382
1382
  fullscreen: "PRESENTATION_FULLSCREEN",
1383
1383
  pip: "PRESENTATION_PIP"
1384
- }, at = {
1384
+ }, ot = {
1385
1385
  widevine: "KEY_SYSTEM_WIDEVINE",
1386
1386
  fairplay: "KEY_SYSTEM_FAIRPLAY",
1387
1387
  none: "KEY_SYSTEM_NONE"
@@ -1389,7 +1389,7 @@ var U = class {
1389
1389
  function K(e, t) {
1390
1390
  return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
1391
1391
  }
1392
- var ot = /* @__PURE__ */ new Set([
1392
+ var st = /* @__PURE__ */ new Set([
1393
1393
  "INVALID_URN",
1394
1394
  "NOT_FOUND",
1395
1395
  "GONE",
@@ -1409,7 +1409,7 @@ var ot = /* @__PURE__ */ new Set([
1409
1409
  ended: "PLAYER_STATE_ENDED",
1410
1410
  error: "PLAYER_STATE_ERROR"
1411
1411
  };
1412
- function st(e) {
1412
+ function ct(e) {
1413
1413
  try {
1414
1414
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
1415
1415
  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);
@@ -1417,7 +1417,7 @@ function st(e) {
1417
1417
  return;
1418
1418
  }
1419
1419
  }
1420
- var ct = class {
1420
+ var lt = class {
1421
1421
  opts;
1422
1422
  url;
1423
1423
  now;
@@ -1435,6 +1435,8 @@ var ct = class {
1435
1435
  hiddenTotalMs = 0;
1436
1436
  lastHbPlayingMs = 0;
1437
1437
  lastHbHiddenMs = 0;
1438
+ lastHbRebufferMs = 0;
1439
+ lastHbRebufferCount = 0;
1438
1440
  rebufferCount = 0;
1439
1441
  rebufferTotalMs = 0;
1440
1442
  bufferingSinceMs = null;
@@ -1462,7 +1464,8 @@ var ct = class {
1462
1464
  timeToFirstStallMs = 0;
1463
1465
  lastStallMs = 0;
1464
1466
  lastErrorMs = 0;
1465
- lastSeekMs = 0;
1467
+ lastSeekMs = -1;
1468
+ stallIsSeekInduced = !1;
1466
1469
  lastErrorStage = null;
1467
1470
  sawFirstBytes = !1;
1468
1471
  mediaInfoSignature = "";
@@ -1500,17 +1503,25 @@ var ct = class {
1500
1503
  noteStateChange(e, t, n, r) {
1501
1504
  if (this.endedFlag || e === t) return;
1502
1505
  let i = this.now();
1503
- t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(W, i - this.startupAnchorMs)), this.firstAudioMs === 0 && !this.isMuted && (this.firstAudioMs = Math.max(W, i - this.startupAnchorMs)), this.isMuted && this.mutedSinceMs === null && (this.mutedSinceMs = i)) : this.playingSinceMs !== null && (this.playingTotalMs += i - this.playingSinceMs, this.playingSinceMs = null, this.mutedSinceMs !== null && (this.mutedTotalMs += i - this.mutedSinceMs, this.mutedSinceMs = null)), this.isPlaying = t === "playing", this.syncActive(), t === "waiting" ? this.waitingSinceMs = i : this.waitingSinceMs !== null && (this.waitingTotalMs += i - this.waitingSinceMs, this.waitingSinceMs = null), t === "buffering" && e === "playing" ? (this.isHidden || (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(W, Math.round(this.playingTotalMs)))) : e === "buffering" && (this.isBuffering = !1), this.syncBuffering(), t === "paused" && (this.pauseCount += 1);
1506
+ if (t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(W, i - this.startupAnchorMs)), this.firstAudioMs === 0 && !this.isMuted && (this.firstAudioMs = Math.max(W, i - this.startupAnchorMs)), this.isMuted && this.mutedSinceMs === null && (this.mutedSinceMs = i)) : this.playingSinceMs !== null && (this.playingTotalMs += i - this.playingSinceMs, this.playingSinceMs = null, this.mutedSinceMs !== null && (this.mutedTotalMs += i - this.mutedSinceMs, this.mutedSinceMs = null)), this.isPlaying = t === "playing", this.syncActive(), t === "waiting" ? this.waitingSinceMs = i : this.waitingSinceMs !== null && (this.waitingTotalMs += i - this.waitingSinceMs, this.waitingSinceMs = null), t === "buffering" && e === "playing") {
1507
+ this.stallIsSeekInduced = this.seekInduced(i);
1508
+ let e = !this.isHidden && !this.stallIsSeekInduced;
1509
+ e && (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, e && this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(W, Math.round(this.playingTotalMs)));
1510
+ } else e === "buffering" && (this.isBuffering = !1, this.stallIsSeekInduced = !1);
1511
+ this.syncBuffering(), t === "paused" && (this.pauseCount += 1);
1504
1512
  let a = {
1505
1513
  fromState: q[e] ?? "PLAYER_STATE_UNSPECIFIED",
1506
1514
  toState: q[t] ?? "PLAYER_STATE_UNSPECIFIED",
1507
- presentation: it[this.presentation]
1515
+ presentation: at[this.presentation]
1508
1516
  }, o = this.causeOf(e, t, i);
1509
1517
  o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
1510
1518
  }
1519
+ seekInduced(e) {
1520
+ return this.lastSeekMs >= 0 && e - this.lastSeekMs < it;
1521
+ }
1511
1522
  causeOf(e, t, n) {
1512
1523
  if (t === "error") return "TRANSITION_CAUSE_ERROR";
1513
- if (t === "buffering") return n - this.lastSeekMs < 1e3 ? "TRANSITION_CAUSE_SEEK" : "TRANSITION_CAUSE_NETWORK_STALL";
1524
+ if (t === "buffering") return this.seekInduced(n) ? "TRANSITION_CAUSE_SEEK" : "TRANSITION_CAUSE_NETWORK_STALL";
1514
1525
  if (t === "paused" || t === "playing" && e === "paused") return "TRANSITION_CAUSE_USER";
1515
1526
  }
1516
1527
  syncActive() {
@@ -1518,7 +1529,7 @@ var ct = class {
1518
1529
  e && this.activeSinceMs === null ? this.activeSinceMs = this.now() : !e && this.activeSinceMs !== null && (this.activeTotalMs += this.now() - this.activeSinceMs, this.activeSinceMs = null);
1519
1530
  }
1520
1531
  syncBuffering() {
1521
- let e = this.isBuffering && !this.isHidden;
1532
+ let e = this.isBuffering && !this.isHidden && !this.stallIsSeekInduced;
1522
1533
  e && this.bufferingSinceMs === null ? this.bufferingSinceMs = this.now() : !e && this.bufferingSinceMs !== null && (this.rebufferTotalMs += this.now() - this.bufferingSinceMs, this.bufferingSinceMs = null);
1523
1534
  }
1524
1535
  closePresentation() {
@@ -1574,7 +1585,7 @@ var ct = class {
1574
1585
  let r = n ?? "";
1575
1586
  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";
1576
1587
  if (e === "DRM_CLIENT") return "ERROR_STAGE_DRM";
1577
- if (ot.has(e)) return "ERROR_STAGE_RESOLVE";
1588
+ if (st.has(e)) return "ERROR_STAGE_RESOLVE";
1578
1589
  let i = t ?? "";
1579
1590
  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";
1580
1591
  }
@@ -1593,36 +1604,38 @@ var ct = class {
1593
1604
  this.playingSinceMs !== null && (n += t - this.playingSinceMs);
1594
1605
  let r = this.hiddenTotalMs;
1595
1606
  this.hiddenSinceMs !== null && (r += t - this.hiddenSinceMs);
1596
- let i = {}, a = e.bytesLoaded ?? 0;
1597
- a > this.lastBytes && (i.bytesDownloadedDelta = a - this.lastBytes), this.lastBytes = a;
1598
- let o = e.playlistBytesLoaded ?? 0;
1599
- o > this.lastPlaylistBytes && (i.playlistBytesDelta = o - this.lastPlaylistBytes), this.lastPlaylistBytes = o;
1600
- let s = e.droppedFrames ?? 0;
1601
- s > this.lastDropped && (i.droppedFramesDelta = s - this.lastDropped), this.lastDropped = s;
1602
- let c = e.renditionSwitches ?? 0;
1603
- c > this.lastSwitches && (i.renditionSwitchesDelta = c - this.lastSwitches), this.lastSwitches = c, n > this.lastHbPlayingMs && (i.playingMsDelta = Math.round(n - this.lastHbPlayingMs)), this.lastHbPlayingMs = n, r > this.lastHbHiddenMs && (i.hiddenMsDelta = Math.round(r - this.lastHbHiddenMs)), this.lastHbHiddenMs = r, typeof e.latencySeconds == "number" && (i.latencyMs = Math.round(e.latencySeconds * 1e3)), typeof e.targetLatencySeconds == "number" && (i.targetLatencyMs = Math.round(e.targetLatencySeconds * 1e3)), typeof e.bandwidthKbps == "number" && (i.bandwidthEstKbps = e.bandwidthKbps), typeof e.levelHeight == "number" && (i.renditionHeight = e.levelHeight), typeof e.renditionBitrateKbps == "number" && (i.renditionBitrateKbps = e.renditionBitrateKbps), e.atLiveEdge === !0 && (i.atLiveEdge = !0), typeof e.bufferedMs == "number" && e.bufferedMs > 0 && (i.bufferedMs = Math.round(e.bufferedMs)), i.playerState = q[e.state] ?? "PLAYER_STATE_UNSPECIFIED";
1604
- let l = this.mutedTotalMs;
1605
- this.mutedSinceMs !== null && (l += t - this.mutedSinceMs), l > this.lastHbMutedMs && (i.mutedMsDelta = Math.round(l - this.lastHbMutedMs)), this.lastHbMutedMs = l;
1606
- let u = e.upshifts ?? 0;
1607
- u > this.lastUpshifts && (i.upshiftsDelta = u - this.lastUpshifts), this.lastUpshifts = u;
1608
- let d = e.downshifts ?? 0;
1609
- d > this.lastDownshifts && (i.downshiftsDelta = d - this.lastDownshifts), this.lastDownshifts = d;
1610
- let f = e.decodedFrames ?? 0;
1611
- f > this.lastDecoded && (i.decodedFramesDelta = f - this.lastDecoded), this.lastDecoded = f;
1612
- let p = e.requestErrors ?? 0;
1613
- p > this.lastRequestErrors && (i.requestErrorsDelta = p - this.lastRequestErrors), this.lastRequestErrors = p;
1614
- let m = e.requestCount ?? 0, h = m - this.lastRequestCount;
1615
- if (this.lastRequestCount = m, h > 0) {
1616
- i.requestCountDelta = h;
1617
- let t = e.requestTimesMs ?? [], n = t.slice(Math.max(0, t.length - h)).sort((e, t) => e - t);
1618
- n.length > 0 && (i.requestTimeP50Ms = K(n, 50), i.requestTimeP95Ms = K(n, 95));
1607
+ let i = this.rebufferTotalMs;
1608
+ this.bufferingSinceMs !== null && (i += t - this.bufferingSinceMs);
1609
+ let a = {}, o = e.bytesLoaded ?? 0;
1610
+ o > this.lastBytes && (a.bytesDownloadedDelta = o - this.lastBytes), this.lastBytes = o;
1611
+ let s = e.playlistBytesLoaded ?? 0;
1612
+ s > this.lastPlaylistBytes && (a.playlistBytesDelta = s - this.lastPlaylistBytes), this.lastPlaylistBytes = s;
1613
+ let c = e.droppedFrames ?? 0;
1614
+ c > this.lastDropped && (a.droppedFramesDelta = c - this.lastDropped), this.lastDropped = c;
1615
+ let l = e.renditionSwitches ?? 0;
1616
+ l > this.lastSwitches && (a.renditionSwitchesDelta = l - this.lastSwitches), this.lastSwitches = l, n > this.lastHbPlayingMs && (a.playingMsDelta = Math.round(n - this.lastHbPlayingMs)), this.lastHbPlayingMs = n, r > this.lastHbHiddenMs && (a.hiddenMsDelta = Math.round(r - this.lastHbHiddenMs)), this.lastHbHiddenMs = r, i > this.lastHbRebufferMs && (a.rebufferMsDelta = Math.round(i - this.lastHbRebufferMs)), this.lastHbRebufferMs = i, this.rebufferCount > this.lastHbRebufferCount && (a.rebufferCountDelta = this.rebufferCount - this.lastHbRebufferCount), this.lastHbRebufferCount = this.rebufferCount, typeof e.latencySeconds == "number" && (a.latencyMs = Math.round(e.latencySeconds * 1e3)), typeof e.targetLatencySeconds == "number" && (a.targetLatencyMs = Math.round(e.targetLatencySeconds * 1e3)), typeof e.bandwidthKbps == "number" && (a.bandwidthEstKbps = e.bandwidthKbps), typeof e.levelHeight == "number" && (a.renditionHeight = e.levelHeight), typeof e.renditionBitrateKbps == "number" && (a.renditionBitrateKbps = e.renditionBitrateKbps), e.atLiveEdge === !0 && (a.atLiveEdge = !0), typeof e.bufferedMs == "number" && e.bufferedMs > 0 && (a.bufferedMs = Math.round(e.bufferedMs)), a.playerState = q[e.state] ?? "PLAYER_STATE_UNSPECIFIED";
1617
+ let u = this.mutedTotalMs;
1618
+ this.mutedSinceMs !== null && (u += t - this.mutedSinceMs), u > this.lastHbMutedMs && (a.mutedMsDelta = Math.round(u - this.lastHbMutedMs)), this.lastHbMutedMs = u;
1619
+ let d = e.upshifts ?? 0;
1620
+ d > this.lastUpshifts && (a.upshiftsDelta = d - this.lastUpshifts), this.lastUpshifts = d;
1621
+ let f = e.downshifts ?? 0;
1622
+ f > this.lastDownshifts && (a.downshiftsDelta = f - this.lastDownshifts), this.lastDownshifts = f;
1623
+ let p = e.decodedFrames ?? 0;
1624
+ p > this.lastDecoded && (a.decodedFramesDelta = p - this.lastDecoded), this.lastDecoded = p;
1625
+ let m = e.requestErrors ?? 0;
1626
+ m > this.lastRequestErrors && (a.requestErrorsDelta = m - this.lastRequestErrors), this.lastRequestErrors = m;
1627
+ let h = e.requestCount ?? 0, g = h - this.lastRequestCount;
1628
+ if (this.lastRequestCount = h, g > 0) {
1629
+ a.requestCountDelta = g;
1630
+ let t = e.requestTimesMs ?? [], n = t.slice(Math.max(0, t.length - g)).sort((e, t) => e - t);
1631
+ n.length > 0 && (a.requestTimeP50Ms = K(n, 50), a.requestTimeP95Ms = K(n, 95));
1619
1632
  }
1620
- typeof e.latencySeconds == "number" && typeof e.targetLatencySeconds == "number" && (i.driftMs = Math.round((e.latencySeconds - e.targetLatencySeconds) * 1e3)), typeof e.playbackRate == "number" && e.playbackRate > 0 && (i.playbackRate = e.playbackRate), e.qualityPinned === !0 && (i.qualityPinned = !0), e.cdnHost && (i.cdnHost = e.cdnHost), this.maybeEmitMediaInfo(e), this.enqueue({ heartbeat: i }), this.flush(), a > 0 && this.noteFirstBytes();
1633
+ typeof e.latencySeconds == "number" && typeof e.targetLatencySeconds == "number" && (a.driftMs = Math.round((e.latencySeconds - e.targetLatencySeconds) * 1e3)), typeof e.playbackRate == "number" && e.playbackRate > 0 && (a.playbackRate = e.playbackRate), e.qualityPinned === !0 && (a.qualityPinned = !0), e.cdnHost && (a.cdnHost = e.cdnHost), this.maybeEmitMediaInfo(e), this.enqueue({ heartbeat: a }), this.flush(), o > 0 && this.noteFirstBytes();
1621
1634
  }
1622
1635
  maybeEmitMediaInfo(e) {
1623
1636
  if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
1624
1637
  let t = {};
1625
- e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = at[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);
1638
+ e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = ot[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);
1626
1639
  let n = JSON.stringify(t);
1627
1640
  n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
1628
1641
  }
@@ -1687,7 +1700,7 @@ var ct = class {
1687
1700
  };
1688
1701
  //#endregion
1689
1702
  //#region src/core/environment.ts
1690
- function lt() {
1703
+ function ut() {
1691
1704
  if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
1692
1705
  let e;
1693
1706
  try {
@@ -1712,14 +1725,14 @@ function lt() {
1712
1725
  integration: "INTEGRATION_KIND_IFRAME"
1713
1726
  };
1714
1727
  }
1715
- function ut() {
1728
+ function dt() {
1716
1729
  if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
1717
1730
  let e = navigator.userAgentData, t = e?.platform ?? "";
1718
1731
  if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
1719
1732
  let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
1720
1733
  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";
1721
1734
  }
1722
- function dt() {
1735
+ function ft() {
1723
1736
  let e = typeof navigator < "u" ? navigator.connection : void 0;
1724
1737
  if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
1725
1738
  let t = { connectionType: e.type ? {
@@ -1729,17 +1742,17 @@ function dt() {
1729
1742
  }[e.type] ?? "CONNECTION_TYPE_OTHER" : "CONNECTION_TYPE_UNKNOWN" };
1730
1743
  return typeof e.downlink == "number" && e.downlink > 0 && (t.downlinkKbps = Math.round(e.downlink * 1e3)), e.saveData && (t.saveData = !0), t;
1731
1744
  }
1732
- function ft(e) {
1733
- let t = lt(), n = {
1734
- deviceClass: ut(),
1745
+ function pt(e) {
1746
+ let t = ut(), n = {
1747
+ deviceClass: dt(),
1735
1748
  integration: t.integration,
1736
- ...dt()
1749
+ ...ft()
1737
1750
  };
1738
1751
  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);
1739
1752
  let r = e.getBoundingClientRect?.();
1740
1753
  return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
1741
1754
  }
1742
- async function pt() {
1755
+ async function mt() {
1743
1756
  let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
1744
1757
  if (!e?.getHighEntropyValues) return {};
1745
1758
  try {
@@ -1751,7 +1764,7 @@ async function pt() {
1751
1764
  }
1752
1765
  //#endregion
1753
1766
  //#region src/core/version.ts
1754
- var mt = "2.3.0", J = {
1767
+ var ht = "2.4.0", J = {
1755
1768
  accent: "#E1B600",
1756
1769
  accentText: "#1B1C23",
1757
1770
  background: "#0e0f13",
@@ -1784,15 +1797,15 @@ function X(e, t) {
1784
1797
  }
1785
1798
  //#endregion
1786
1799
  //#region src/ui/styles.ts
1787
- var Z = "havik-player-styles", ht = "\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.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.havik-spinner{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";
1788
- function gt(e = document) {
1800
+ var Z = "havik-player-styles", gt = "\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.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.havik-spinner{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";
1801
+ function _t(e = document) {
1789
1802
  if (e.getElementById(Z)) return;
1790
1803
  let t = e.createElement("style");
1791
- t.id = Z, t.textContent = ht, e.head.appendChild(t);
1804
+ t.id = Z, t.textContent = gt, e.head.appendChild(t);
1792
1805
  }
1793
1806
  //#endregion
1794
1807
  //#region src/ui/icons.ts
1795
- var _t = {
1808
+ var vt = {
1796
1809
  play: "<path d=\"M8 5v14l11-7z\"/>",
1797
1810
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
1798
1811
  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\"/>",
@@ -1806,20 +1819,20 @@ var _t = {
1806
1819
  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\"/>"
1807
1820
  };
1808
1821
  function Q(e) {
1809
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${_t[e]}</svg>`;
1822
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${vt[e]}</svg>`;
1810
1823
  }
1811
1824
  //#endregion
1812
1825
  //#region src/ui/controls.ts
1813
- var vt = 3e3, yt = /* @__PURE__ */ new Set([
1826
+ var yt = 3e3, bt = /* @__PURE__ */ new Set([
1814
1827
  "GONE",
1815
1828
  "NOT_FOUND",
1816
1829
  "INVALID_URN",
1817
1830
  "UNAUTHORIZED"
1818
1831
  ]);
1819
- function bt(e) {
1820
- return new xt(e);
1832
+ function xt(e) {
1833
+ return new St(e);
1821
1834
  }
1822
- var xt = class {
1835
+ var St = class {
1823
1836
  root;
1824
1837
  video;
1825
1838
  player;
@@ -1861,7 +1874,7 @@ var xt = class {
1861
1874
  scrubbing = !1;
1862
1875
  offFns = [];
1863
1876
  constructor(e) {
1864
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? vt, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, gt(this.doc), 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");
1877
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? yt, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, _t(this.doc), 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");
1865
1878
  let t = (e, t, n) => {
1866
1879
  let r = this.doc.createElement(e);
1867
1880
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -1952,7 +1965,7 @@ var xt = class {
1952
1965
  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);
1953
1966
  } else {
1954
1967
  let r = this.doc.createElement("div");
1955
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !yt.has(this.lastError.code)) {
1968
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !bt.has(this.lastError.code)) {
1956
1969
  let e = this.doc.createElement("button");
1957
1970
  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);
1958
1971
  }
@@ -1993,7 +2006,7 @@ var xt = class {
1993
2006
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
1994
2007
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
1995
2008
  }
1996
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${St(this.video.currentTime)} / ${St(this.video.duration)}`);
2009
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Ct(this.video.currentTime)} / ${Ct(this.video.duration)}`);
1997
2010
  }
1998
2011
  syncLive() {
1999
2012
  let e = this.player.getStats(), t = e.isLive !== !1;
@@ -2043,17 +2056,17 @@ var xt = class {
2043
2056
  renderQualityMenu() {
2044
2057
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
2045
2058
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
2046
- for (let n of e) this.menu.append(this.menuItem(Ct(n), t === n.index, () => this.player.setQuality(n.index)));
2059
+ for (let n of e) this.menu.append(this.menuItem(wt(n), t === n.index, () => this.player.setQuality(n.index)));
2047
2060
  }
2048
2061
  renderAudioMenu() {
2049
2062
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
2050
2063
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
2051
- for (let t of e) this.menu.append(this.menuItem(wt(t), t.default, () => this.player.setAudioTrack(t.id)));
2064
+ for (let t of e) this.menu.append(this.menuItem(Tt(t), t.default, () => this.player.setAudioTrack(t.id)));
2052
2065
  }
2053
2066
  renderCcMenu() {
2054
2067
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
2055
2068
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
2056
- for (let t of e) this.menu.append(this.menuItem(Tt(t), !1, () => this.player.setTextTrack(t.id)));
2069
+ for (let t of e) this.menu.append(this.menuItem(Et(t), !1, () => this.player.setTextTrack(t.id)));
2057
2070
  }
2058
2071
  onTracksChanged() {
2059
2072
  this.openMenu && this.renderMenu(this.openMenu);
@@ -2073,11 +2086,11 @@ var xt = class {
2073
2086
  }
2074
2087
  async toggleFullscreen() {
2075
2088
  try {
2076
- S(this.video, this.root) ? await w(this.video, this.root) : await C(this.video, this.root);
2089
+ C(this.video, this.root) ? await T(this.video, this.root) : await w(this.video, this.root);
2077
2090
  } catch {}
2078
2091
  }
2079
2092
  syncFullscreen() {
2080
- let e = S(this.video, this.root);
2093
+ let e = C(this.video, this.root);
2081
2094
  this.setIcon(this.fsBtn, e ? "fullscreenExit" : "fullscreen"), this.fsBtn.setAttribute("aria-label", e ? "Exit fullscreen" : "Fullscreen");
2082
2095
  }
2083
2096
  poke() {
@@ -2153,32 +2166,32 @@ var xt = class {
2153
2166
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
2154
2167
  }
2155
2168
  };
2156
- function St(e) {
2169
+ function Ct(e) {
2157
2170
  (!Number.isFinite(e) || e < 0) && (e = 0);
2158
2171
  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");
2159
2172
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
2160
2173
  }
2161
- function Ct(e) {
2174
+ function wt(e) {
2162
2175
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
2163
2176
  }
2164
- function wt(e) {
2177
+ function Tt(e) {
2165
2178
  return e.name || e.lang || `Track ${e.id}`;
2166
2179
  }
2167
- function Tt(e) {
2180
+ function Et(e) {
2168
2181
  return e.name || e.lang || `Track ${e.id}`;
2169
2182
  }
2170
2183
  //#endregion
2171
2184
  //#region src/managed/index.ts
2172
- var Et = 6e3, Dt = 45e3, Ot = 2e3, kt = 3, At = 2e3, jt = 48e4;
2173
- async function Mt(e) {
2185
+ var Dt = 6e3, Ot = 45e3, kt = 2e3, At = 3, jt = 2e3, Mt = 48e4;
2186
+ async function Nt(e) {
2174
2187
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
2175
2188
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: baseUrl is required");
2176
2189
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
2177
2190
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
2178
- let t = new Nt(e);
2191
+ let t = new Pt(e);
2179
2192
  return await t.start(), t;
2180
2193
  }
2181
- var Nt = class {
2194
+ var Pt = class {
2182
2195
  opts;
2183
2196
  abort = new AbortController();
2184
2197
  listeners = {};
@@ -2244,7 +2257,7 @@ var Nt = class {
2244
2257
  };
2245
2258
  }
2246
2259
  beginArming(e, t, n = !1) {
2247
- let r = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? E(this.opts.baseUrl);
2260
+ let r = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? D(this.opts.baseUrl);
2248
2261
  r ? this.armWithSse(e, r, t, n) : this.beginArmingByPolling(e);
2249
2262
  }
2250
2263
  beginArmingByPolling(e) {
@@ -2265,7 +2278,7 @@ var Nt = class {
2265
2278
  setTimeout(() => {
2266
2279
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
2267
2280
  phase: i ? "unavailable" : "tooEarly",
2268
- retryInMs: Dt,
2281
+ retryInMs: Ot,
2269
2282
  attempt: 1,
2270
2283
  elapsedMs: 0,
2271
2284
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -2304,13 +2317,13 @@ var Nt = class {
2304
2317
  !a && !p() && h();
2305
2318
  }, e);
2306
2319
  };
2307
- _(Et), this.armingSub = T({
2320
+ _(Dt), this.armingSub = E({
2308
2321
  eventsBaseUrl: t,
2309
2322
  matchUrn: this.opts.matchUrn,
2310
2323
  credential: this.opts.credential,
2311
- onAlive: () => _(Dt),
2324
+ onAlive: () => _(Ot),
2312
2325
  onState: (e) => {
2313
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Ot);
2326
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * kt);
2314
2327
  },
2315
2328
  onError: () => h()
2316
2329
  });
@@ -2352,15 +2365,15 @@ var Nt = class {
2352
2365
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
2353
2366
  let r = e.analytics?.sid;
2354
2367
  if (!r) return;
2355
- let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? st(this.opts.baseUrl);
2368
+ let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? ct(this.opts.baseUrl);
2356
2369
  if (!i) return;
2357
- let a = this.opts.video, o = new ct({
2370
+ let a = this.opts.video, o = new lt({
2358
2371
  endpoint: i,
2359
2372
  sid: r,
2360
2373
  apiKey: n,
2361
2374
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
2362
- sdkVersion: mt,
2363
- environment: ft(a),
2375
+ sdkVersion: ht,
2376
+ environment: pt(a),
2364
2377
  sample: () => {
2365
2378
  let e = this.engine?.getStats() ?? I, t = a.buffered.length ? Math.max(0, a.buffered.end(a.buffered.length - 1) - a.currentTime) : 0;
2366
2379
  return {
@@ -2376,7 +2389,7 @@ var Nt = class {
2376
2389
  let s = () => o.noteSeek(), c = () => o.noteVolume(a.muted, a.volume);
2377
2390
  a.addEventListener("seeking", s), a.addEventListener("volumechange", c), this.detachMediaListeners = () => {
2378
2391
  a.removeEventListener("seeking", s), a.removeEventListener("volumechange", c);
2379
- }, o.start(), o.seedVolume(a.muted, a.volume), o.notePresentation(this.currentPresentation()), pt().then((e) => o.noteEnvironment(e)), this.opts.autoplay === !1 && o.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
2392
+ }, o.start(), o.seedVolume(a.muted, a.volume), o.notePresentation(this.currentPresentation()), mt().then((e) => o.noteEnvironment(e)), this.opts.autoplay === !1 && o.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
2380
2393
  }
2381
2394
  currentPresentation() {
2382
2395
  let e = this.opts.video;
@@ -2391,7 +2404,7 @@ var Nt = class {
2391
2404
  video: this.opts.video,
2392
2405
  descriptor: e,
2393
2406
  credential: t,
2394
- deviceId: k(),
2407
+ deviceId: A(),
2395
2408
  userId: this.opts.userId,
2396
2409
  lowLatency: this.opts.lowLatency === void 0 || this.opts.lowLatency === "auto" || this.opts.lowLatency,
2397
2410
  snapToLiveOnRefocus: this.opts.snapToLiveOnRefocus,
@@ -2432,11 +2445,11 @@ var Nt = class {
2432
2445
  }
2433
2446
  startLiveState(e) {
2434
2447
  if (this.opts.liveStateEvents === !1) return;
2435
- let t = this.opts.eventsBaseUrl ?? E(this.opts.baseUrl);
2448
+ let t = this.opts.eventsBaseUrl ?? D(this.opts.baseUrl);
2436
2449
  if (!t) return;
2437
2450
  this.closeLiveState();
2438
2451
  let r = 0, i = !1;
2439
- this.liveStateSub = T({
2452
+ this.liveStateSub = E({
2440
2453
  eventsBaseUrl: t,
2441
2454
  matchUrn: this.opts.matchUrn,
2442
2455
  credential: this.opts.credential,
@@ -2447,7 +2460,7 @@ var Nt = class {
2447
2460
  r = 0, i = !1;
2448
2461
  },
2449
2462
  onError: () => {
2450
- this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= kt && (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.`))));
2463
+ this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= At && (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.`))));
2451
2464
  }
2452
2465
  });
2453
2466
  }
@@ -2467,9 +2480,9 @@ var Nt = class {
2467
2480
  return this.opts.rejoinOnLive !== !1 && this.opts.liveStateEvents !== !1;
2468
2481
  }
2469
2482
  scheduleRejoin(e) {
2470
- !this.rejoinEnabled() || this.rejoinTimer || (this.rejoinTimer = setTimeout(() => {
2483
+ this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
2471
2484
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
2472
- }, Math.random() * Ot));
2485
+ }, Math.random() * kt));
2473
2486
  }
2474
2487
  cancelRejoin() {
2475
2488
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -2493,7 +2506,7 @@ var Nt = class {
2493
2506
  }
2494
2507
  t.controls = !1;
2495
2508
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
2496
- r && r.insertBefore(n, t), n.appendChild(t), X(n, this.opts.theme), this.container = n, this.controlBar = bt({
2509
+ r && r.insertBefore(n, t), n.appendChild(t), X(n, this.opts.theme), this.container = n, this.controlBar = xt({
2497
2510
  root: n,
2498
2511
  video: t,
2499
2512
  player: this,
@@ -2523,7 +2536,7 @@ var Nt = class {
2523
2536
  }
2524
2537
  startStats() {
2525
2538
  this.stopStats();
2526
- let e = Math.max(500, this.opts.statsIntervalMs ?? At);
2539
+ let e = Math.max(500, this.opts.statsIntervalMs ?? jt);
2527
2540
  this.statsTimer = setInterval(() => {
2528
2541
  if (!this.engine) return;
2529
2542
  let e = this.engine.getStats();
@@ -2535,15 +2548,15 @@ var Nt = class {
2535
2548
  }
2536
2549
  scheduleLicenseRefresh() {
2537
2550
  this.stopRefresh();
2538
- let e = this.opts.licenseRefreshMs ?? jt;
2539
- !e || !this._descriptor?.drmEnabled || (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
2551
+ let e = this.opts.licenseRefreshMs ?? Mt;
2552
+ e && this._descriptor?.drmEnabled && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
2540
2553
  }
2541
2554
  stopRefresh() {
2542
2555
  this.refreshTimer && clearInterval(this.refreshTimer), this.refreshTimer = void 0;
2543
2556
  }
2544
2557
  async refreshLicense() {
2545
2558
  let e = this.engine;
2546
- if (!(this.destroyed || !e)) try {
2559
+ if (!this.destroyed && e) try {
2547
2560
  let t = await y(this.resolveOptions());
2548
2561
  if (this.destroyed || this.engine !== e) return;
2549
2562
  let n = t.drm?.widevine?.licenseUrl, r = t.drm?.fairplay?.licenseUrl;
@@ -2616,10 +2629,10 @@ var Nt = class {
2616
2629
  e.pictureInPictureElement === this.opts.video && typeof e.exitPictureInPicture == "function" && await e.exitPictureInPicture();
2617
2630
  }
2618
2631
  async enterFullscreen() {
2619
- return !this.destroyed && C(this.opts.video, this.container ?? void 0);
2632
+ return !this.destroyed && w(this.opts.video, this.container ?? void 0);
2620
2633
  }
2621
2634
  async exitFullscreen() {
2622
- await w(this.opts.video, this.container ?? void 0);
2635
+ await T(this.opts.video, this.container ?? void 0);
2623
2636
  }
2624
2637
  retry() {
2625
2638
  return this.reload();
@@ -2687,7 +2700,7 @@ function $(e) {
2687
2700
  }
2688
2701
  //#endregion
2689
2702
  //#region src/embed/host.ts
2690
- function Pt(e) {
2703
+ function Ft(e) {
2691
2704
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
2692
2705
  if (!e.src) throw Error("mountEmbed: opts.src is required");
2693
2706
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -2746,8 +2759,8 @@ function Pt(e) {
2746
2759
  }
2747
2760
  //#endregion
2748
2761
  //#region src/embed/page.ts
2749
- var Ft = /^[A-Za-z0-9:_.-]{1,128}$/;
2750
- function It() {
2762
+ var It = /^[A-Za-z0-9:_.-]{1,128}$/;
2763
+ function Lt() {
2751
2764
  let e = window.HAVIK_EMBED_CONFIG;
2752
2765
  if (e) return {
2753
2766
  baseUrl: e.baseUrl ?? "",
@@ -2769,9 +2782,9 @@ function It() {
2769
2782
  waitForLive: !0
2770
2783
  };
2771
2784
  }
2772
- async function Lt() {
2773
- let e = It();
2774
- zt();
2785
+ async function Rt() {
2786
+ let e = Lt();
2787
+ Bt();
2775
2788
  let t = document.createElement("video");
2776
2789
  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);
2777
2790
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -2826,12 +2839,12 @@ async function Lt() {
2826
2839
  code: e.code,
2827
2840
  message: e.message
2828
2841
  })), e.on("autoplayblocked", () => {
2829
- i({ type: "oddin:autoplayblocked" }), Rt(t, e);
2842
+ i({ type: "oddin:autoplayblocked" }), zt(t, e);
2830
2843
  });
2831
2844
  }, s = async (r) => {
2832
2845
  a?.destroy(), a = null;
2833
2846
  try {
2834
- let n = await Mt({
2847
+ let n = await Nt({
2835
2848
  video: t,
2836
2849
  baseUrl: e.baseUrl,
2837
2850
  matchUrn: r,
@@ -2892,30 +2905,30 @@ async function Lt() {
2892
2905
  a?.retry();
2893
2906
  break;
2894
2907
  case "oddin:load":
2895
- typeof e.matchUrn == "string" && Ft.test(e.matchUrn) && s(e.matchUrn);
2908
+ typeof e.matchUrn == "string" && It.test(e.matchUrn) && s(e.matchUrn);
2896
2909
  break;
2897
2910
  case "oddin:destroy": c();
2898
2911
  }
2899
2912
  }, u = (e) => {
2900
- !r || e.origin !== r || $(e.data) && l(e.data);
2913
+ r && e.origin === r && $(e.data) && l(e.data);
2901
2914
  };
2902
2915
  r && window.addEventListener("message", u), window.addEventListener("pagehide", c, { once: !0 }), await s(e.matchUrn), i({
2903
2916
  type: "oddin:ready",
2904
2917
  protocol: 1
2905
2918
  });
2906
2919
  }
2907
- function Rt(e, t) {
2920
+ function zt(e, t) {
2908
2921
  if (document.getElementById("havik-tap")) return;
2909
2922
  let n = document.createElement("button");
2910
2923
  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", () => {
2911
2924
  n.remove(), t.play();
2912
2925
  }), document.body.appendChild(n);
2913
2926
  }
2914
- function zt() {
2927
+ function Bt() {
2915
2928
  let e = document.createElement("style");
2916
2929
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
2917
2930
  }
2918
2931
  //#endregion
2919
- export { J as ODDIN_THEME, n as PlaybackError, X as applyTheme, Lt as bootEmbed, _e as clearDeviceId, Mt as createPlayer, E as deriveEventsBaseUrl, De as detectDrmSupport, C as enterVideoFullscreen, w as exitVideoFullscreen, de as fetchCatalog, pe as fetchTournament, he as flattenMatches, k as getDeviceId, ke as isLowLatencyManifest, $ as isOddinMessage, ye as licenseRequestHeaders, Pt as mountEmbed, v as pollToLive, b as resolveStream, T as subscribeLiveState, S as videoFullscreenActive, ge as watchStatus };
2932
+ export { J as ODDIN_THEME, n as PlaybackError, X as applyTheme, Rt as bootEmbed, ge as clearDeviceId, Nt as createPlayer, D as deriveEventsBaseUrl, De as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ue as fetchCatalog, fe as fetchTournament, me as flattenMatches, A as getDeviceId, ke as isLowLatencyManifest, $ as isOddinMessage, ve as licenseRequestHeaders, Ft as mountEmbed, v as pollToLive, b as resolveStream, E as subscribeLiveState, C as videoFullscreenActive, he as watchStatus };
2920
2933
 
2921
2934
  //# sourceMappingURL=index.js.map