@oddin-gg/havik-player 1.15.1 → 1.16.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
@@ -110,16 +110,16 @@ function ne(e) {
110
110
  let t = Date.parse(e);
111
111
  return Number.isFinite(t) ? t : void 0;
112
112
  }
113
- function _(e, t, n) {
113
+ function re(e, t, n) {
114
114
  return Math.min(n, Math.max(t, e));
115
115
  }
116
- function re(e) {
116
+ function ie(e) {
117
117
  return Math.round(e * (1 + .15 * Math.random()));
118
118
  }
119
- function ie(e, t, n, r) {
119
+ function ae(e, t, n, r) {
120
120
  let i = r ?? n.kickoffMs, a = i == null ? void 0 : Math.max(0, i - t), o = Math.max(n.floorMs, e ?? 0);
121
121
  if (a == null || a <= n.imminentMs) return {
122
- delay: _(e ?? n.floorMs, o, n.ceilMs),
122
+ delay: re(e ?? n.floorMs, o, n.ceilMs),
123
123
  ttkMs: a
124
124
  };
125
125
  let s = Math.min(Math.floor(a / 10), n.sanityFloorMs, a);
@@ -128,7 +128,7 @@ function ie(e, t, n, r) {
128
128
  ttkMs: a
129
129
  };
130
130
  }
131
- function ae(e, t) {
131
+ function oe(e, t) {
132
132
  return new Promise((r, i) => {
133
133
  if (t?.aborted) {
134
134
  i(new n("ABORTED", 0, "wait-for-live aborted"));
@@ -142,7 +142,7 @@ function ae(e, t) {
142
142
  t?.addEventListener("abort", a, { once: !0 });
143
143
  });
144
144
  }
145
- async function v(e, t, r) {
145
+ async function _(e, t, r) {
146
146
  let i = te(t), a = Date.now(), s = i.timeoutMs == null ? Infinity : a + i.timeoutMs, c = 0, l;
147
147
  for (;;) {
148
148
  if (r?.aborted) throw new n("ABORTED", 0, "wait-for-live aborted");
@@ -151,8 +151,8 @@ async function v(e, t, r) {
151
151
  } catch (e) {
152
152
  if (!(e instanceof n) || !o(e.code)) throw e;
153
153
  c += 1, e.liveStartsAtMs != null && Number.isFinite(e.liveStartsAtMs) && (l = e.liveStartsAtMs);
154
- let t = Date.now(), u = ie(e.retryAfterMs, t, i, l), d = u.ttkMs, f = u.delay;
155
- if (i.jitter && (f = re(f)), t + f > s) throw new n("TIMEOUT", e.httpStatus, `timed out after ${i.timeoutMs}ms waiting for the stream to go live`, {
154
+ let t = Date.now(), u = ae(e.retryAfterMs, t, i, l), d = u.ttkMs, f = u.delay;
155
+ if (i.jitter && (f = ie(f)), t + f > s) throw new n("TIMEOUT", e.httpStatus, `timed out after ${i.timeoutMs}ms waiting for the stream to go live`, {
156
156
  retryAfterMs: e.retryAfterMs,
157
157
  cause: e
158
158
  });
@@ -162,27 +162,27 @@ async function v(e, t, r) {
162
162
  attempt: c,
163
163
  elapsedMs: t - a,
164
164
  ttkMs: d
165
- }), await ae(f, r);
165
+ }), await oe(f, r);
166
166
  }
167
167
  }
168
168
  }
169
169
  //#endregion
170
170
  //#region src/core/resolve.ts
171
- async function y(e) {
171
+ async function v(e) {
172
172
  if (!e.matchUrn || typeof e.matchUrn != "string") throw new n("INVALID_URN", 0, "resolveStream: a non-empty matchUrn is required");
173
173
  let t = await u(c(e.baseUrl, `/v1/playback/${encodeURIComponent(e.matchUrn)}`), {
174
174
  credential: e.credential,
175
175
  signal: e.signal,
176
176
  apiHeaders: e.apiHeaders
177
177
  });
178
- if (!t.ok) throw await oe(t);
179
- return se(await t.json(), e.matchUrn);
178
+ if (!t.ok) throw await se(t);
179
+ return le(await t.json(), e.matchUrn);
180
180
  }
181
- function b(e) {
182
- let t = () => y(e);
183
- return e.waitForLive ? v(t, e.waitForLive, e.signal) : t();
181
+ function y(e) {
182
+ let t = () => v(e);
183
+ return e.waitForLive ? _(t, e.waitForLive, e.signal) : t();
184
184
  }
185
- async function oe(e) {
185
+ async function se(e) {
186
186
  let t = l(e.headers.get("retry-after")), i = e.headers.get("x-request-id") ?? e.headers.get("x-amzn-requestid") ?? void 0, a = e.statusText || `HTTP ${e.status}`, o, s;
187
187
  try {
188
188
  let t = await e.json();
@@ -198,7 +198,7 @@ async function oe(e) {
198
198
  liveStartsAtMs: s
199
199
  });
200
200
  }
201
- var x = /* @__PURE__ */ new Set([
201
+ var ce = /* @__PURE__ */ new Set([
202
202
  "matchUrn",
203
203
  "protocol",
204
204
  "drmEnabled",
@@ -209,7 +209,7 @@ var x = /* @__PURE__ */ new Set([
209
209
  "expiresAt",
210
210
  "analytics"
211
211
  ]);
212
- function S(e, t) {
212
+ function b(e, t) {
213
213
  let r;
214
214
  try {
215
215
  r = new URL(t);
@@ -219,29 +219,57 @@ function S(e, t) {
219
219
  if (r.protocol !== "https:" && r.protocol !== "http:") throw new n("INTERNAL", 0, `playback ${e} has a disallowed scheme: ${r.protocol}`);
220
220
  return t;
221
221
  }
222
- function se(e, t) {
222
+ function le(e, t) {
223
223
  if (!e.manifestUrl) throw new n("INTERNAL", 0, "playback response is missing manifestUrl");
224
224
  let r = e.drmEnabled ?? !!e.drm, i = {
225
225
  matchUrn: e.matchUrn ?? t,
226
226
  protocol: "hls",
227
227
  drmEnabled: r,
228
- manifestUrl: S("manifestUrl", e.manifestUrl),
228
+ manifestUrl: b("manifestUrl", e.manifestUrl),
229
229
  serverTime: e.serverTime ?? ""
230
230
  };
231
231
  if (e.liveStartsAt && (i.liveStartsAt = e.liveStartsAt), typeof e.analytics?.sid == "string" && e.analytics.sid && (i.analytics = { sid: e.analytics.sid }), r && e.drm) {
232
232
  let t = {};
233
- e.drm.widevine?.licenseUrl && (t.widevine = { licenseUrl: S("widevine licenseUrl", e.drm.widevine.licenseUrl) }), e.drm.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && (t.fairplay = {
234
- licenseUrl: S("fairplay licenseUrl", e.drm.fairplay.licenseUrl),
235
- certificateUrl: S("fairplay certificateUrl", e.drm.fairplay.certificateUrl)
233
+ e.drm.widevine?.licenseUrl && (t.widevine = { licenseUrl: b("widevine licenseUrl", e.drm.widevine.licenseUrl) }), e.drm.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && (t.fairplay = {
234
+ licenseUrl: b("fairplay licenseUrl", e.drm.fairplay.licenseUrl),
235
+ certificateUrl: b("fairplay certificateUrl", e.drm.fairplay.certificateUrl)
236
236
  }), (t.widevine || t.fairplay) && (i.drm = t);
237
237
  }
238
238
  let a = {};
239
- for (let [t, n] of Object.entries(e)) x.has(t) || (a[t] = n);
239
+ for (let [t, n] of Object.entries(e)) ce.has(t) || (a[t] = n);
240
240
  return Object.keys(a).length > 0 && (i.extensions = a), i;
241
241
  }
242
242
  //#endregion
243
+ //#region src/core/fullscreen.ts
244
+ function x(e) {
245
+ return e.ownerDocument ?? document;
246
+ }
247
+ function S(e) {
248
+ return e.fullscreenElement ?? e.webkitFullscreenElement ?? null;
249
+ }
250
+ function C(e, t) {
251
+ let n = S(x(e));
252
+ return n != null && (n === e || t != null && n === t) || e.webkitDisplayingFullscreen === !0;
253
+ }
254
+ async function w(e, t) {
255
+ let n = t ?? e;
256
+ if (typeof n.requestFullscreen == "function") return await n.requestFullscreen(), !0;
257
+ if (typeof n.webkitRequestFullscreen == "function") return n.webkitRequestFullscreen(), !0;
258
+ let r = e;
259
+ return r.webkitSupportsFullscreen && typeof r.webkitEnterFullscreen == "function" ? (r.webkitEnterFullscreen(), !0) : !1;
260
+ }
261
+ async function T(e, t) {
262
+ let n = e;
263
+ if (n.webkitDisplayingFullscreen) {
264
+ n.webkitExitFullscreen?.();
265
+ return;
266
+ }
267
+ let r = x(e), i = S(r);
268
+ i != null && (i === e || t != null && i === t) && (typeof r.exitFullscreen == "function" ? await r.exitFullscreen() : r.webkitExitFullscreen?.());
269
+ }
270
+ //#endregion
243
271
  //#region src/core/catalog.ts
244
- async function ce(e) {
272
+ async function ue(e) {
245
273
  let t = new URLSearchParams();
246
274
  e.status && t.set("status", Array.isArray(e.status) ? e.status.join(",") : e.status), e.sport && t.set("sport", e.sport);
247
275
  let i = t.toString(), a = c(e.baseUrl, "/v1/catalog" + (i ? `?${i}` : "")), o = new Headers();
@@ -259,12 +287,12 @@ async function ce(e) {
259
287
  };
260
288
  if (!s.ok) throw new n(r(s.status), s.status, `catalog request failed (HTTP ${s.status})`);
261
289
  return {
262
- tournaments: le(await s.json()),
290
+ tournaments: de(await s.json()),
263
291
  etag: s.headers.get("etag") ?? void 0,
264
292
  notModified: !1
265
293
  };
266
294
  }
267
- function le(e) {
295
+ function de(e) {
268
296
  return (e.tournaments ?? []).map((e) => {
269
297
  let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
270
298
  return {
@@ -284,7 +312,7 @@ function le(e) {
284
312
  };
285
313
  });
286
314
  }
287
- async function ue(e) {
315
+ async function fe(e) {
288
316
  let t = c(e.baseUrl, `/v1/tournaments/${encodeURIComponent(e.urn)}`), i = new Headers();
289
317
  e.etag && i.set("If-None-Match", e.etag);
290
318
  let a = await u(t, {
@@ -304,13 +332,13 @@ async function ue(e) {
304
332
  };
305
333
  if (!a.ok) throw new n(r(a.status), a.status, `tournament request failed (HTTP ${a.status})`);
306
334
  return {
307
- tournament: de(await a.json()),
335
+ tournament: pe(await a.json()),
308
336
  etag: a.headers.get("etag") ?? void 0,
309
337
  notModified: !1,
310
338
  notFound: !1
311
339
  };
312
340
  }
313
- function de(e) {
341
+ function pe(e) {
314
342
  let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
315
343
  return {
316
344
  urn: t,
@@ -328,12 +356,12 @@ function de(e) {
328
356
  }))
329
357
  };
330
358
  }
331
- function fe(e) {
359
+ function me(e) {
332
360
  return e.tournaments.flatMap((e) => e.matches);
333
361
  }
334
362
  //#endregion
335
363
  //#region src/core/poll.ts
336
- function pe(e) {
364
+ function he(e) {
337
365
  let t = Math.max(2e3, e.intervalMs ?? 1e4), i = new AbortController(), a, o, s, l = !1, d = () => {
338
366
  l || (a = setTimeout(f, t));
339
367
  }, f = async () => {
@@ -369,7 +397,7 @@ function pe(e) {
369
397
  }
370
398
  //#endregion
371
399
  //#region src/core/events.ts
372
- function C(e) {
400
+ function E(e) {
373
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) => {
374
402
  if (r.signal.aborted) return t();
375
403
  let n = setTimeout(t, e);
@@ -436,7 +464,7 @@ function C(e) {
436
464
  i || (i = !0, r.abort());
437
465
  } };
438
466
  }
439
- function w(e) {
467
+ function D(e) {
440
468
  try {
441
469
  let t = new URL(e);
442
470
  return t.hostname = `events.${t.hostname}`, t.pathname = "", t.search = "", t.origin;
@@ -446,27 +474,27 @@ function w(e) {
446
474
  }
447
475
  //#endregion
448
476
  //#region src/core/drm/deviceId.ts
449
- var T = "havik.player.deviceId", E;
450
- function D() {
451
- if (E) return E;
477
+ var O = "havik.player.deviceId", k;
478
+ function A() {
479
+ if (k) return k;
452
480
  try {
453
- let e = globalThis.localStorage?.getItem(T);
454
- if (e) return E = e, e;
481
+ let e = globalThis.localStorage?.getItem(O);
482
+ if (e) return k = e, e;
455
483
  } catch {}
456
- let e = he();
457
- E = e;
484
+ let e = _e();
485
+ k = e;
458
486
  try {
459
- globalThis.localStorage?.setItem(T, e);
487
+ globalThis.localStorage?.setItem(O, e);
460
488
  } catch {}
461
489
  return e;
462
490
  }
463
- function me() {
464
- E = void 0;
491
+ function ge() {
492
+ k = void 0;
465
493
  try {
466
- globalThis.localStorage?.removeItem(T);
494
+ globalThis.localStorage?.removeItem(O);
467
495
  } catch {}
468
496
  }
469
- function he() {
497
+ function _e() {
470
498
  let e = globalThis.crypto;
471
499
  if (e && typeof e.randomUUID == "function") return e.randomUUID();
472
500
  let t = /* @__PURE__ */ new Uint8Array(16);
@@ -478,22 +506,22 @@ function he() {
478
506
  }
479
507
  //#endregion
480
508
  //#region src/core/drm/headers.ts
481
- function ge(e, t, n = {}) {
509
+ function ve(e, t, n = {}) {
482
510
  let r = {
483
511
  "x-api-key": t.apiKey,
484
512
  "X-Match-Urn": e.matchUrn,
485
- "X-Device-Id": n.deviceId ?? D(),
513
+ "X-Device-Id": n.deviceId ?? A(),
486
514
  "Content-Type": "application/octet-stream"
487
515
  };
488
516
  return n.userId && (r["X-User-Id"] = n.userId), r;
489
517
  }
490
518
  //#endregion
491
519
  //#region src/core/drm/support.ts
492
- var _e = 5e3, O = "video/mp4; codecs=\"avc1.42E01E\"", k = "audio/mp4; codecs=\"mp4a.40.2\"", ve = {
520
+ var ye = 5e3, j = "video/mp4; codecs=\"avc1.42E01E\"", M = "audio/mp4; codecs=\"mp4a.40.2\"", be = {
493
521
  widevine: ["com.widevine.alpha"],
494
522
  fairplay: ["com.apple.fps"]
495
523
  };
496
- function A(e) {
524
+ function N(e) {
497
525
  return e === "fairplay" ? [{
498
526
  initDataTypes: [
499
527
  "sinf",
@@ -501,30 +529,30 @@ function A(e) {
501
529
  "cenc"
502
530
  ],
503
531
  videoCapabilities: [{
504
- contentType: O,
532
+ contentType: j,
505
533
  robustness: "",
506
534
  encryptionScheme: "cbcs"
507
535
  }],
508
536
  audioCapabilities: [{
509
- contentType: k,
537
+ contentType: M,
510
538
  robustness: "",
511
539
  encryptionScheme: "cbcs"
512
540
  }]
513
541
  }] : [{
514
542
  initDataTypes: ["cenc"],
515
543
  videoCapabilities: [{
516
- contentType: O,
544
+ contentType: j,
517
545
  robustness: "SW_SECURE_DECODE",
518
546
  encryptionScheme: "cbcs"
519
547
  }],
520
548
  audioCapabilities: [{
521
- contentType: k,
549
+ contentType: M,
522
550
  robustness: "SW_SECURE_CRYPTO",
523
551
  encryptionScheme: "cbcs"
524
552
  }]
525
553
  }];
526
554
  }
527
- function j(e, t) {
555
+ function P(e, t) {
528
556
  return new Promise((n, r) => {
529
557
  let i = setTimeout(() => r(/* @__PURE__ */ Error("probe-timeout")), t);
530
558
  e.then((e) => {
@@ -534,8 +562,8 @@ function j(e, t) {
534
562
  });
535
563
  });
536
564
  }
537
- async function M(e = {}) {
538
- let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? _e, r = {};
565
+ async function xe(e = {}) {
566
+ let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? ye, r = {};
539
567
  if (typeof window < "u" && window.isSecureContext === !1) return {
540
568
  verdict: "insecure-context",
541
569
  detail: r
@@ -547,7 +575,7 @@ async function M(e = {}) {
547
575
  };
548
576
  let a = !1, o = !1, s = Date.now() + n, c = () => Math.max(1, s - Date.now());
549
577
  for (let e of t) {
550
- let t = ve[e];
578
+ let t = be[e];
551
579
  if (!t) {
552
580
  r[e] = "unknown system";
553
581
  continue;
@@ -558,7 +586,7 @@ async function M(e = {}) {
558
586
  break;
559
587
  }
560
588
  try {
561
- return await j((await j(i(n, A(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
589
+ return await P((await P(i(n, N(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
562
590
  verdict: "supported",
563
591
  system: e,
564
592
  detail: r
@@ -582,9 +610,9 @@ async function M(e = {}) {
582
610
  }
583
611
  //#endregion
584
612
  //#region src/core/manifest.ts
585
- var N = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
586
- function P(e) {
587
- return N.test(e);
613
+ var Se = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
614
+ function Ce(e) {
615
+ return Se.test(e);
588
616
  }
589
617
  //#endregion
590
618
  //#region src/core/stats.ts
@@ -592,7 +620,7 @@ var F = { droppedFrames: 0 };
592
620
  function I(e) {
593
621
  return e.getVideoPlaybackQuality?.()?.droppedVideoFrames ?? 0;
594
622
  }
595
- function ye(e, t) {
623
+ function we(e, t) {
596
624
  let n = [
597
625
  ["playing", () => t("playing")],
598
626
  ["waiting", () => t("buffering")],
@@ -609,7 +637,7 @@ function ye(e, t) {
609
637
  }
610
638
  //#endregion
611
639
  //#region src/core/engine/native.ts
612
- function be(e) {
640
+ function Te(e) {
613
641
  try {
614
642
  let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
615
643
  return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
@@ -655,7 +683,7 @@ var z = class {
655
683
  type: "error",
656
684
  error: new n("INTERNAL", 0, "native HLS playback error"),
657
685
  fatal: !0
658
- }), e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = xe(t.manifestUrl, t.analytics?.sid), e.load();
686
+ }), e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = Ee(t.manifestUrl, t.analytics?.sid), e.load();
659
687
  }
660
688
  armFairPlay() {
661
689
  let { video: e, descriptor: t, credential: r, deviceId: a, userId: o, emit: s } = this.deps, c = t.drm?.fairplay;
@@ -668,7 +696,7 @@ var z = class {
668
696
  });
669
697
  this.onEncrypted = (n) => {
670
698
  let i = n;
671
- !i.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV = be(i.initData), (async () => {
699
+ !i.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV = Te(i.initData), (async () => {
672
700
  try {
673
701
  let n = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
674
702
  initDataTypes: [i.initDataType],
@@ -783,29 +811,29 @@ var z = class {
783
811
  } catch {}
784
812
  }
785
813
  };
786
- function xe(e, t) {
814
+ function Ee(e, t) {
787
815
  return t ? `${e}${e.includes("?") ? "&" : "?"}CMCD=${encodeURIComponent(`sid="${t}"`)}` : e;
788
816
  }
789
817
  //#endregion
790
818
  //#region src/core/engine/hls.ts
791
- function Se() {
819
+ function De() {
792
820
  return e.isSupported();
793
821
  }
794
- var Ce = 3, we = 5, Te = 3, Ee = 3e4, De = 12e3, Oe = 3e3, B = 6e3, ke = 4e3, Ae = 2, je = 5, Me = 2e3, Ne = 3e4, Pe = 2, Fe = 9, Ie = .5;
795
- function Le(e) {
822
+ var Oe = 3, ke = 5, Ae = 3, je = 3e4, Me = 12e3, Ne = 3e3, B = 6e3, Pe = 4e3, Fe = 2, Ie = 5, Le = 2e3, Re = 3e4, ze = 2, Be = 9, V = .5;
823
+ function Ve(e) {
796
824
  if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
797
825
  delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
798
826
  return;
799
827
  }
800
- e.liveSyncDuration ??= Pe;
801
- let t = Math.max(12, e.liveSyncDuration + Fe);
802
- (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Re(e, e.liveSyncDuration);
828
+ e.liveSyncDuration ??= ze;
829
+ let t = Math.max(12, e.liveSyncDuration + Be);
830
+ (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), He(e, e.liveSyncDuration);
803
831
  }
804
- function Re(e, t) {
832
+ function He(e, t) {
805
833
  let n = (e, t, n) => Math.min(n, Math.max(t, e));
806
834
  e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
807
835
  }
808
- var V = class {
836
+ var H = class {
809
837
  name = "hls";
810
838
  hls = null;
811
839
  deps;
@@ -882,7 +910,7 @@ var V = class {
882
910
  let t = e.liveSyncPosition;
883
911
  if (typeof t != "number" || !Number.isFinite(t)) return;
884
912
  let { video: n } = this.deps;
885
- n.currentTime <= t + Ie || (n.currentTime = t);
913
+ n.currentTime <= t + V || (n.currentTime = t);
886
914
  }
887
915
  startHls() {
888
916
  let { video: t, descriptor: n, credential: r, deviceId: i, userId: a, lowLatency: o, debug: s, hlsConfig: c, emit: l } = this.deps, u = n.drmEnabled && !!(n.drm?.widevine?.licenseUrl || n.drm?.fairplay?.licenseUrl), d = {
@@ -918,14 +946,14 @@ var V = class {
918
946
  } } : {},
919
947
  ...c
920
948
  };
921
- Le(d), this.sizeCapManaged = d.capLevelToPlayerSize !== !0;
949
+ Ve(d), this.sizeCapManaged = d.capLevelToPlayerSize !== !0;
922
950
  let f = new e(d);
923
951
  this.hls = f, f.on(e.Events.MANIFEST_PARSED, () => {
924
952
  this.mediaRecoveries = 0, this.networkRecoveries = 0, this.applySizeCap(), l({ type: "ready" });
925
953
  }), f.on(e.Events.FRAG_BUFFERED, (e, t) => {
926
954
  t?.frag?.type === "main" && (this.mediaRecoveries > 0 || this.networkRecoveries > 0 || this.licenseRecoveries > 0 || this.starvationKicks > 0) && !this.recoveryResetTimer && (this.recoveryResetTimer = setTimeout(() => {
927
955
  this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.recoveryResetTimer = null;
928
- }, Ee));
956
+ }, je));
929
957
  }), f.on(e.Events.FRAG_LOADING, (e, t) => {
930
958
  t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
931
959
  }), f.on(e.Events.FRAG_LOADED, (e, t) => {
@@ -941,7 +969,7 @@ var V = class {
941
969
  }));
942
970
  }), f.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.endWatchdog = setInterval(() => {
943
971
  this.checkEndStale(), this.checkStarvation();
944
- }, Oe), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
972
+ }, Ne), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
945
973
  this.renditionSwitches += 1, l({
946
974
  type: "qualitychange",
947
975
  index: t.level
@@ -1003,7 +1031,7 @@ var V = class {
1003
1031
  return;
1004
1032
  }
1005
1033
  this.capTimer && clearTimeout(this.capTimer);
1006
- let t = e > this.sizeCapLevel ? Me : Ne;
1034
+ let t = e > this.sizeCapLevel ? Le : Re;
1007
1035
  this.capTimer = setTimeout(() => {
1008
1036
  this.capTimer = null, this.computeSizeCapTarget() === e ? this.applySizeCap() : this.onMediaResize();
1009
1037
  }, t);
@@ -1084,7 +1112,7 @@ var V = class {
1084
1112
  return;
1085
1113
  }
1086
1114
  let e = Date.now() - this.lastAdvanceMs;
1087
- !this.chaseSuspended && e > B && this.suspendChasing(), e > De && this.emitEnded("stalled");
1115
+ !this.chaseSuspended && e > B && this.suspendChasing(), e > Me && this.emitEnded("stalled");
1088
1116
  }
1089
1117
  checkStarvation() {
1090
1118
  let e = this.hls, { video: t } = this.deps;
@@ -1093,12 +1121,12 @@ var V = class {
1093
1121
  this.starvedTicks = 0;
1094
1122
  return;
1095
1123
  }
1096
- let n = this.lastAdvanceMs !== 0 && Date.now() - this.lastAdvanceMs < B, r = this.lastFragLoadingMs !== 0 && Date.now() - this.lastFragLoadingMs > ke;
1124
+ let n = this.lastAdvanceMs !== 0 && Date.now() - this.lastAdvanceMs < B, r = this.lastFragLoadingMs !== 0 && Date.now() - this.lastFragLoadingMs > Pe;
1097
1125
  if (!(t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused && n && r)) {
1098
1126
  this.starvedTicks = 0;
1099
1127
  return;
1100
1128
  }
1101
- if (!(++this.starvedTicks < Ae) && (this.starvedTicks = 0, !(this.starvationKicks >= je))) {
1129
+ if (!(++this.starvedTicks < Fe) && (this.starvedTicks = 0, !(this.starvationKicks >= Ie))) {
1102
1130
  this.starvationKicks += 1, console.warn("havik-player: video starved while the live playlist advances (wedged hls.js fragment pipeline) — restarting load at the live edge");
1103
1131
  try {
1104
1132
  e.stopLoad(), e.startLoad(-1);
@@ -1147,7 +1175,7 @@ var V = class {
1147
1175
  scheduleLicenseRetry(t) {
1148
1176
  if (t.details !== e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED && t.details !== e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED) return !1;
1149
1177
  let n = t.response?.code ?? 0;
1150
- if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Te) return !1;
1178
+ if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Ae) return !1;
1151
1179
  this.licenseRecoveries += 1;
1152
1180
  let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
1153
1181
  return this.licenseRetryTimer = setTimeout(() => {
@@ -1175,7 +1203,7 @@ var V = class {
1175
1203
  return;
1176
1204
  }
1177
1205
  if (t.type === e.ErrorTypes.NETWORK_ERROR) {
1178
- if (this.networkRecoveries < we) {
1206
+ if (this.networkRecoveries < ke) {
1179
1207
  this.networkRecoveries += 1;
1180
1208
  let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
1181
1209
  this.networkRetryTimer = setTimeout(() => {
@@ -1191,7 +1219,7 @@ var V = class {
1191
1219
  return;
1192
1220
  }
1193
1221
  if (t.type === e.ErrorTypes.MEDIA_ERROR) {
1194
- if (this.mediaRecoveries < Ce) {
1222
+ if (this.mediaRecoveries < Oe) {
1195
1223
  this.mediaRecoveries += 1, n.recoverMediaError();
1196
1224
  return;
1197
1225
  }
@@ -1213,7 +1241,7 @@ var V = class {
1213
1241
  let r = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
1214
1242
  return new n(t === "DRM" ? i(r) : r === 401 ? "UNAUTHORIZED" : r === 403 ? "FORBIDDEN" : "INTERNAL", r, `${t} error: ${a}${o ? ` (${o})` : ""}`);
1215
1243
  }
1216
- }, ze = 15e3, Be = 100, H = 1, U = {
1244
+ }, Ue = 15e3, We = 100, U = 1, W = {
1217
1245
  idle: "PLAYER_STATE_IDLE",
1218
1246
  waiting: "PLAYER_STATE_WAITING",
1219
1247
  loading: "PLAYER_STATE_LOADING",
@@ -1223,7 +1251,7 @@ var V = class {
1223
1251
  ended: "PLAYER_STATE_ENDED",
1224
1252
  error: "PLAYER_STATE_ERROR"
1225
1253
  };
1226
- function Ve(e) {
1254
+ function Ge(e) {
1227
1255
  try {
1228
1256
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
1229
1257
  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);
@@ -1231,7 +1259,7 @@ function Ve(e) {
1231
1259
  return;
1232
1260
  }
1233
1261
  }
1234
- var He = class {
1262
+ var Ke = class {
1235
1263
  opts;
1236
1264
  url;
1237
1265
  now;
@@ -1266,7 +1294,7 @@ var He = class {
1266
1294
  constructor(e) {
1267
1295
  this.opts = {
1268
1296
  ...e,
1269
- heartbeatMs: e.heartbeatMs ?? ze
1297
+ heartbeatMs: e.heartbeatMs ?? Ue
1270
1298
  }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`, this.now = e.now ?? (() => Date.now()), this.bornMs = this.now();
1271
1299
  }
1272
1300
  start() {
@@ -1278,18 +1306,18 @@ var He = class {
1278
1306
  noteStateChange(e, t, n, r) {
1279
1307
  if (this.endedFlag || e === t) return;
1280
1308
  let i = this.now();
1281
- t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(H, i - this.bornMs))) : this.playingSinceMs !== null && (this.playingTotalMs += i - this.playingSinceMs, this.playingSinceMs = null), t === "buffering" && e === "playing" ? (this.rebufferCount += 1, this.bufferingSinceMs = i) : e === "buffering" && this.bufferingSinceMs !== null && (this.rebufferTotalMs += i - this.bufferingSinceMs, this.bufferingSinceMs = null), t === "paused" && (this.pauseCount += 1);
1309
+ t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(U, i - this.bornMs))) : this.playingSinceMs !== null && (this.playingTotalMs += i - this.playingSinceMs, this.playingSinceMs = null), t === "buffering" && e === "playing" ? (this.rebufferCount += 1, this.bufferingSinceMs = i) : e === "buffering" && this.bufferingSinceMs !== null && (this.rebufferTotalMs += i - this.bufferingSinceMs, this.bufferingSinceMs = null), t === "paused" && (this.pauseCount += 1);
1282
1310
  let a = {
1283
- fromState: U[e] ?? "PLAYER_STATE_UNSPECIFIED",
1284
- toState: U[t] ?? "PLAYER_STATE_UNSPECIFIED"
1311
+ fromState: W[e] ?? "PLAYER_STATE_UNSPECIFIED",
1312
+ toState: W[t] ?? "PLAYER_STATE_UNSPECIFIED"
1285
1313
  };
1286
1314
  n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
1287
1315
  }
1288
1316
  noteReady() {
1289
- this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(H, this.now() - this.bornMs));
1317
+ this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(U, this.now() - this.bornMs));
1290
1318
  }
1291
1319
  noteFirstBytes() {
1292
- this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(H, this.now() - this.bornMs));
1320
+ this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(U, this.now() - this.bornMs));
1293
1321
  }
1294
1322
  noteSeek() {
1295
1323
  this.endedFlag || (this.seekCount += 1);
@@ -1324,7 +1352,7 @@ var He = class {
1324
1352
  let c = e.renditionSwitches ?? 0;
1325
1353
  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);
1326
1354
  let l = this.bufferedMsFrom(e);
1327
- l > 0 && (i.bufferedMs = l), i.playerState = U[e.state] ?? "PLAYER_STATE_UNSPECIFIED", this.enqueue({ heartbeat: i }), this.flush(), a > 0 && this.noteFirstBytes();
1355
+ l > 0 && (i.bufferedMs = l), i.playerState = W[e.state] ?? "PLAYER_STATE_UNSPECIFIED", this.enqueue({ heartbeat: i }), this.flush(), a > 0 && this.noteFirstBytes();
1328
1356
  }
1329
1357
  bufferedMsFrom(e) {
1330
1358
  let t = e;
@@ -1358,7 +1386,7 @@ var He = class {
1358
1386
  };
1359
1387
  }
1360
1388
  enqueue(e) {
1361
- for (this.pending.push(this.wrap(e)); this.pending.length > Be;) {
1389
+ for (this.pending.push(this.wrap(e)); this.pending.length > We;) {
1362
1390
  let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
1363
1391
  this.pending.splice(e >= 0 ? e : 0, 1);
1364
1392
  }
@@ -1380,7 +1408,7 @@ var He = class {
1380
1408
  keepalive: !0
1381
1409
  }).catch(() => {});
1382
1410
  }
1383
- }, Ue = "1.15.1", W = {
1411
+ }, qe = "1.16.1", G = {
1384
1412
  accent: "#E1B600",
1385
1413
  accentText: "#1B1C23",
1386
1414
  background: "#0e0f13",
@@ -1391,7 +1419,7 @@ var He = class {
1391
1419
  border: "rgba(255, 255, 255, 0.1)",
1392
1420
  radius: "8px",
1393
1421
  fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
1394
- }, G = {
1422
+ }, K = {
1395
1423
  accent: "--havik-accent",
1396
1424
  accentText: "--havik-accent-text",
1397
1425
  background: "--havik-bg",
@@ -1403,25 +1431,25 @@ var He = class {
1403
1431
  radius: "--havik-radius",
1404
1432
  fontFamily: "--havik-font"
1405
1433
  };
1406
- function K(e, t) {
1434
+ function q(e, t) {
1407
1435
  let n = {
1408
- ...W,
1436
+ ...G,
1409
1437
  ...t
1410
1438
  };
1411
- for (let t of Object.keys(G)) e.style.setProperty(G[t], n[t]);
1439
+ for (let t of Object.keys(K)) e.style.setProperty(K[t], n[t]);
1412
1440
  n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
1413
1441
  }
1414
1442
  //#endregion
1415
1443
  //#region src/ui/styles.ts
1416
- var q = "havik-player-styles", We = "\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";
1417
- function Ge(e = document) {
1418
- if (e.getElementById(q)) return;
1444
+ var J = "havik-player-styles", Je = "\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";
1445
+ function Ye(e = document) {
1446
+ if (e.getElementById(J)) return;
1419
1447
  let t = e.createElement("style");
1420
- t.id = q, t.textContent = We, e.head.appendChild(t);
1448
+ t.id = J, t.textContent = Je, e.head.appendChild(t);
1421
1449
  }
1422
1450
  //#endregion
1423
1451
  //#region src/ui/icons.ts
1424
- var Ke = {
1452
+ var Xe = {
1425
1453
  play: "<path d=\"M8 5v14l11-7z\"/>",
1426
1454
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
1427
1455
  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\"/>",
@@ -1434,21 +1462,21 @@ var Ke = {
1434
1462
  liveDot: "<circle cx=\"12\" cy=\"12\" r=\"6\"/>",
1435
1463
  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\"/>"
1436
1464
  };
1437
- function J(e) {
1438
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${Ke[e]}</svg>`;
1465
+ function Y(e) {
1466
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${Xe[e]}</svg>`;
1439
1467
  }
1440
1468
  //#endregion
1441
1469
  //#region src/ui/controls.ts
1442
- var qe = 3e3, Je = /* @__PURE__ */ new Set([
1470
+ var Ze = 3e3, Qe = /* @__PURE__ */ new Set([
1443
1471
  "GONE",
1444
1472
  "NOT_FOUND",
1445
1473
  "INVALID_URN",
1446
1474
  "UNAUTHORIZED"
1447
1475
  ]);
1448
- function Ye(e) {
1449
- return new Xe(e);
1476
+ function $e(e) {
1477
+ return new et(e);
1450
1478
  }
1451
- var Xe = class {
1479
+ var et = class {
1452
1480
  root;
1453
1481
  video;
1454
1482
  player;
@@ -1488,7 +1516,7 @@ var Xe = class {
1488
1516
  scrubbing = !1;
1489
1517
  offFns = [];
1490
1518
  constructor(e) {
1491
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? qe, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.errorMessage = e.errorMessage, Ge(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");
1519
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Ze, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.errorMessage = e.errorMessage, Ye(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");
1492
1520
  let t = (e, t, n) => {
1493
1521
  let r = this.doc.createElement(e);
1494
1522
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -1499,12 +1527,12 @@ var Xe = class {
1499
1527
  "aria-label": r,
1500
1528
  title: r
1501
1529
  });
1502
- return i.innerHTML = J(n), i;
1530
+ return i.innerHTML = Y(n), i;
1503
1531
  };
1504
1532
  this.overlay = t("div", "havik-overlay"), this.center = t("button", "havik-center", {
1505
1533
  type: "button",
1506
1534
  "aria-label": "Play"
1507
- }), this.center.innerHTML = J("play"), this.spinner = t("div", "havik-spinner"), this.spinner.style.display = "none", this.overlay.append(this.center, this.spinner), this.gradient = t("div", "havik-gradient"), this.bar = t("div", "havik-bar"), this.seek = t("div", "havik-seek", {
1535
+ }), this.center.innerHTML = Y("play"), this.spinner = t("div", "havik-spinner"), this.spinner.style.display = "none", this.overlay.append(this.center, this.spinner), this.gradient = t("div", "havik-gradient"), this.bar = t("div", "havik-bar"), this.seek = t("div", "havik-seek", {
1508
1536
  role: "slider",
1509
1537
  "aria-label": "Seek",
1510
1538
  "aria-valuemin": "0",
@@ -1522,7 +1550,7 @@ var Xe = class {
1522
1550
  "aria-label": "Volume"
1523
1551
  });
1524
1552
  let a = t("div", "havik-vol");
1525
- a.append(this.muteBtn, this.volSlider), this.live = t("div", "havik-live"), this.live.innerHTML = J("liveDot");
1553
+ a.append(this.muteBtn, this.volSlider), this.live = t("div", "havik-live"), this.live.innerHTML = Y("liveDot");
1526
1554
  let o = this.doc.createElement("span");
1527
1555
  o.textContent = "LIVE", this.liveGo = t("button", "havik-live__go", { type: "button" }), this.liveGo.textContent = "Go live", this.live.append(o, this.liveGo), this.time = t("div", "havik-time"), this.time.textContent = "0:00", this.time.style.display = "none";
1528
1556
  let s = t("div", "havik-spacer");
@@ -1572,12 +1600,12 @@ var Xe = class {
1572
1600
  }
1573
1601
  if (e === "preplay") {
1574
1602
  let e = this.doc.createElement("button");
1575
- e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = J("play"), e.addEventListener("click", () => void this.player.play()), t.append(e);
1603
+ e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = Y("play"), e.addEventListener("click", () => void this.player.play()), t.append(e);
1576
1604
  } else {
1577
1605
  let n = this.doc.createElement("div");
1578
- if (n.className = "havik-status__msg", n.textContent = e === "ended" ? this.endedMessage : e === "waiting" ? "Starting soon…" : this.errorMessage ?? this.lastError?.message ?? "Playback unavailable", t.append(n), e === "error" && this.lastError && !Je.has(this.lastError.code)) {
1606
+ if (n.className = "havik-status__msg", n.textContent = e === "ended" ? this.endedMessage : e === "waiting" ? "Starting soon…" : this.errorMessage ?? this.lastError?.message ?? "Playback unavailable", t.append(n), e === "error" && this.lastError && !Qe.has(this.lastError.code)) {
1579
1607
  let e = this.doc.createElement("button");
1580
- e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${J("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), t.append(e);
1608
+ e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${Y("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), t.append(e);
1581
1609
  }
1582
1610
  }
1583
1611
  this.root.append(t), this.statusBox = t, this.show();
@@ -1616,7 +1644,7 @@ var Xe = class {
1616
1644
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
1617
1645
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
1618
1646
  }
1619
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Y(this.video.currentTime)} / ${Y(this.video.duration)}`);
1647
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${X(this.video.currentTime)} / ${X(this.video.duration)}`);
1620
1648
  }
1621
1649
  syncLive() {
1622
1650
  let e = this.player.getStats(), t = e.isLive !== !1;
@@ -1666,17 +1694,17 @@ var Xe = class {
1666
1694
  renderQualityMenu() {
1667
1695
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
1668
1696
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
1669
- for (let n of e) this.menu.append(this.menuItem(Ze(n), t === n.index, () => this.player.setQuality(n.index)));
1697
+ for (let n of e) this.menu.append(this.menuItem(tt(n), t === n.index, () => this.player.setQuality(n.index)));
1670
1698
  }
1671
1699
  renderAudioMenu() {
1672
1700
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
1673
1701
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
1674
- for (let t of e) this.menu.append(this.menuItem(X(t), t.default, () => this.player.setAudioTrack(t.id)));
1702
+ for (let t of e) this.menu.append(this.menuItem(nt(t), t.default, () => this.player.setAudioTrack(t.id)));
1675
1703
  }
1676
1704
  renderCcMenu() {
1677
1705
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
1678
1706
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
1679
- for (let t of e) this.menu.append(this.menuItem(Qe(t), !1, () => this.player.setTextTrack(t.id)));
1707
+ for (let t of e) this.menu.append(this.menuItem(rt(t), !1, () => this.player.setTextTrack(t.id)));
1680
1708
  }
1681
1709
  onTracksChanged() {
1682
1710
  this.openMenu && this.renderMenu(this.openMenu);
@@ -1695,18 +1723,13 @@ var Xe = class {
1695
1723
  this.pipBtn.setAttribute("aria-pressed", e ? "true" : "false");
1696
1724
  }
1697
1725
  async toggleFullscreen() {
1698
- let e = this.root, t = this.doc, n = this.video;
1699
1726
  try {
1700
- if (typeof e.requestFullscreen == "function" || typeof e.webkitRequestFullscreen == "function") {
1701
- t.fullscreenElement ?? t.webkitFullscreenElement ? typeof t.exitFullscreen == "function" ? await t.exitFullscreen() : t.webkitExitFullscreen?.() : typeof e.requestFullscreen == "function" ? await e.requestFullscreen() : e.webkitRequestFullscreen?.();
1702
- return;
1703
- }
1704
- n.webkitSupportsFullscreen && typeof n.webkitEnterFullscreen == "function" && (n.webkitDisplayingFullscreen ? n.webkitExitFullscreen?.() : n.webkitEnterFullscreen());
1727
+ C(this.video, this.root) ? await T(this.video, this.root) : await w(this.video, this.root);
1705
1728
  } catch {}
1706
1729
  }
1707
1730
  syncFullscreen() {
1708
- let e = this.doc, t = this.video, n = (e.fullscreenElement ?? e.webkitFullscreenElement) === this.root || t.webkitDisplayingFullscreen === !0;
1709
- this.setIcon(this.fsBtn, n ? "fullscreenExit" : "fullscreen"), this.fsBtn.setAttribute("aria-label", n ? "Exit fullscreen" : "Fullscreen");
1731
+ let e = C(this.video, this.root);
1732
+ this.setIcon(this.fsBtn, e ? "fullscreenExit" : "fullscreen"), this.fsBtn.setAttribute("aria-label", e ? "Exit fullscreen" : "Fullscreen");
1710
1733
  }
1711
1734
  poke() {
1712
1735
  this.show(), this.scheduleHide();
@@ -1764,7 +1787,7 @@ var Xe = class {
1764
1787
  }
1765
1788
  }
1766
1789
  setIcon(e, t) {
1767
- e.innerHTML = J(t);
1790
+ e.innerHTML = Y(t);
1768
1791
  }
1769
1792
  destroy() {
1770
1793
  this.hideTimer && clearTimeout(this.hideTimer);
@@ -1781,32 +1804,32 @@ var Xe = class {
1781
1804
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
1782
1805
  }
1783
1806
  };
1784
- function Y(e) {
1807
+ function X(e) {
1785
1808
  (!Number.isFinite(e) || e < 0) && (e = 0);
1786
1809
  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");
1787
1810
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
1788
1811
  }
1789
- function Ze(e) {
1812
+ function tt(e) {
1790
1813
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
1791
1814
  }
1792
- function X(e) {
1815
+ function nt(e) {
1793
1816
  return e.name || e.lang || `Track ${e.id}`;
1794
1817
  }
1795
- function Qe(e) {
1818
+ function rt(e) {
1796
1819
  return e.name || e.lang || `Track ${e.id}`;
1797
1820
  }
1798
1821
  //#endregion
1799
1822
  //#region src/managed/index.ts
1800
- var $e = 6e3, Z = 45e3, et = 2e3, tt = 3, nt = 2e3, rt = 48e4;
1823
+ var it = 6e3, Z = 45e3, at = 2e3, ot = 3, st = 2e3, ct = 48e4;
1801
1824
  async function Q(e) {
1802
1825
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
1803
1826
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: baseUrl is required");
1804
1827
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
1805
1828
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
1806
- let t = new it(e);
1829
+ let t = new lt(e);
1807
1830
  return await t.start(), t;
1808
1831
  }
1809
- var it = class {
1832
+ var lt = class {
1810
1833
  opts;
1811
1834
  abort = new AbortController();
1812
1835
  listeners = {};
@@ -1842,7 +1865,7 @@ var it = class {
1842
1865
  this.opts.muted && (this.opts.video.muted = !0), this.opts.poster && (this.opts.video.poster = this.opts.poster), this.mountControls(), this.attachPipListeners(), this.setState("loading");
1843
1866
  let e = ++this.loadGen, t = this.resolveOptions();
1844
1867
  try {
1845
- let n = await y(t);
1868
+ let n = await v(t);
1846
1869
  if (await this.onResolved(n, e), e === this.loadGen && this._state === "error" && this._lastError) {
1847
1870
  let e = this._lastError;
1848
1871
  throw this.destroy(), e;
@@ -1865,11 +1888,11 @@ var it = class {
1865
1888
  };
1866
1889
  }
1867
1890
  beginArming(e, t) {
1868
- let n = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? w(this.opts.baseUrl);
1891
+ let n = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? D(this.opts.baseUrl);
1869
1892
  n ? this.armWithSse(e, n, t) : this.beginArmingByPolling(e);
1870
1893
  }
1871
1894
  beginArmingByPolling(e) {
1872
- b({
1895
+ y({
1873
1896
  ...this.resolveOptions(),
1874
1897
  signal: this.abort.signal,
1875
1898
  waitForLive: this.buildWaitConfig(e)
@@ -1903,7 +1926,7 @@ var it = class {
1903
1926
  }
1904
1927
  }
1905
1928
  }, m = () => {
1906
- i || a || f() || (a = !0, b({
1929
+ i || a || f() || (a = !0, y({
1907
1930
  ...this.resolveOptions(),
1908
1931
  signal: s.signal,
1909
1932
  waitForLive: this.buildWaitConfig(e)
@@ -1911,7 +1934,7 @@ var it = class {
1911
1934
  i || f() || s.signal.aborted || (i = !0, this.closeArming(), this.fail(e instanceof n ? e : new n("INTERNAL", 0, String(e))));
1912
1935
  }));
1913
1936
  }, h = () => {
1914
- i || f() || y(this.resolveOptions()).then((e) => p(e)).catch((e) => {
1937
+ i || f() || v(this.resolveOptions()).then((e) => p(e)).catch((e) => {
1915
1938
  i || f() || e instanceof n && o(e.code) || (i = !0, this.closeArming(), s.abort(), this.fail(e instanceof n ? e : new n("INTERNAL", 0, String(e))));
1916
1939
  });
1917
1940
  }, g = (e) => {
@@ -1919,13 +1942,13 @@ var it = class {
1919
1942
  !i && !f() && m();
1920
1943
  }, e);
1921
1944
  };
1922
- g($e), this.armingSub = C({
1945
+ g(it), this.armingSub = E({
1923
1946
  eventsBaseUrl: t,
1924
1947
  matchUrn: this.opts.matchUrn,
1925
1948
  credential: this.opts.credential,
1926
1949
  onAlive: () => g(Z),
1927
1950
  onState: (e) => {
1928
- i || f() || (e.state === "live" || e.state === "gone") && setTimeout(h, Math.random() * et);
1951
+ i || f() || (e.state === "live" || e.state === "gone") && setTimeout(h, Math.random() * at);
1929
1952
  },
1930
1953
  onError: () => m()
1931
1954
  });
@@ -1948,7 +1971,7 @@ var it = class {
1948
1971
  let n = await s(this.opts.credential);
1949
1972
  if (this.destroyed || t !== this.loadGen) return;
1950
1973
  this.setState("loading");
1951
- let r = this.createEngine(e, n), i = ye(this.opts.video, (e) => {
1974
+ let r = this.createEngine(e, n), i = we(this.opts.video, (e) => {
1952
1975
  this._state !== "ended" && this._state !== "error" && this.setState(e);
1953
1976
  });
1954
1977
  if (await r.load(), this.destroyed || t !== this.loadGen) {
@@ -1965,14 +1988,14 @@ var it = class {
1965
1988
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachSeekListener?.(), this.opts.analytics === !1) return;
1966
1989
  let r = e.analytics?.sid;
1967
1990
  if (!r) return;
1968
- let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? Ve(this.opts.baseUrl);
1991
+ let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? Ge(this.opts.baseUrl);
1969
1992
  if (!i) return;
1970
- let a = this.opts.video, o = new He({
1993
+ let a = this.opts.video, o = new Ke({
1971
1994
  endpoint: i,
1972
1995
  sid: r,
1973
1996
  apiKey: n,
1974
1997
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
1975
- sdkVersion: Ue,
1998
+ sdkVersion: qe,
1976
1999
  sample: () => {
1977
2000
  let e = this.engine?.getStats() ?? F, t = a.buffered.length ? Math.max(0, a.buffered.end(a.buffered.length - 1) - a.currentTime) : 0;
1978
2001
  return {
@@ -1994,7 +2017,7 @@ var it = class {
1994
2017
  video: this.opts.video,
1995
2018
  descriptor: e,
1996
2019
  credential: t,
1997
- deviceId: D(),
2020
+ deviceId: A(),
1998
2021
  userId: this.opts.userId,
1999
2022
  lowLatency: this.opts.lowLatency === void 0 || this.opts.lowLatency === "auto" || this.opts.lowLatency,
2000
2023
  snapToLiveOnRefocus: this.opts.snapToLiveOnRefocus,
@@ -2003,9 +2026,9 @@ var it = class {
2003
2026
  emit: (e) => this.onEngineEvent(e)
2004
2027
  }, a = this.opts.engine ?? "auto";
2005
2028
  if (a === "native") return new z(i);
2006
- if (a === "hls") return new V(i);
2029
+ if (a === "hls") return new H(i);
2007
2030
  if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && L(this.opts.video) && R()) return new z(i);
2008
- if (Se()) return new V(i);
2031
+ if (De()) return new H(i);
2009
2032
  if (L(this.opts.video)) return new z(i);
2010
2033
  throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
2011
2034
  }
@@ -2034,11 +2057,11 @@ var it = class {
2034
2057
  }
2035
2058
  startLiveState(e) {
2036
2059
  if (this.opts.liveStateEvents === !1) return;
2037
- let t = this.opts.eventsBaseUrl ?? w(this.opts.baseUrl);
2060
+ let t = this.opts.eventsBaseUrl ?? D(this.opts.baseUrl);
2038
2061
  if (!t) return;
2039
2062
  this.closeLiveState();
2040
2063
  let r = 0, i = !1;
2041
- this.liveStateSub = C({
2064
+ this.liveStateSub = E({
2042
2065
  eventsBaseUrl: t,
2043
2066
  matchUrn: this.opts.matchUrn,
2044
2067
  credential: this.opts.credential,
@@ -2049,7 +2072,7 @@ var it = class {
2049
2072
  r = 0, i = !1;
2050
2073
  },
2051
2074
  onError: () => {
2052
- this.destroyed || e !== this.loadGen || i || (r += 1, r >= tt && (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.`))));
2075
+ this.destroyed || e !== this.loadGen || i || (r += 1, r >= ot && (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.`))));
2053
2076
  }
2054
2077
  });
2055
2078
  }
@@ -2071,7 +2094,7 @@ var it = class {
2071
2094
  }
2072
2095
  t.controls = !1;
2073
2096
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
2074
- r && r.insertBefore(n, t), n.appendChild(t), K(n, this.opts.theme), this.container = n, this.controlBar = Ye({
2097
+ r && r.insertBefore(n, t), n.appendChild(t), q(n, this.opts.theme), this.container = n, this.controlBar = $e({
2075
2098
  root: n,
2076
2099
  video: t,
2077
2100
  player: this,
@@ -2094,7 +2117,7 @@ var it = class {
2094
2117
  }
2095
2118
  startStats() {
2096
2119
  this.stopStats();
2097
- let e = Math.max(500, this.opts.statsIntervalMs ?? nt);
2120
+ let e = Math.max(500, this.opts.statsIntervalMs ?? st);
2098
2121
  this.statsTimer = setInterval(() => {
2099
2122
  if (!this.engine) return;
2100
2123
  let e = this.engine.getStats();
@@ -2106,7 +2129,7 @@ var it = class {
2106
2129
  }
2107
2130
  scheduleLicenseRefresh() {
2108
2131
  this.stopRefresh();
2109
- let e = this.opts.licenseRefreshMs ?? rt;
2132
+ let e = this.opts.licenseRefreshMs ?? ct;
2110
2133
  !e || !this._descriptor?.drmEnabled || (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
2111
2134
  }
2112
2135
  stopRefresh() {
@@ -2115,7 +2138,7 @@ var it = class {
2115
2138
  async refreshLicense() {
2116
2139
  let e = this.engine;
2117
2140
  if (!(this.destroyed || !e)) try {
2118
- let t = await y(this.resolveOptions());
2141
+ let t = await v(this.resolveOptions());
2119
2142
  if (this.destroyed || this.engine !== e) return;
2120
2143
  let n = t.drm?.widevine?.licenseUrl, r = t.drm?.fairplay?.licenseUrl;
2121
2144
  (n || r) && (e.setLicenseUrls({
@@ -2181,6 +2204,12 @@ var it = class {
2181
2204
  let e = document;
2182
2205
  e.pictureInPictureElement === this.opts.video && typeof e.exitPictureInPicture == "function" && await e.exitPictureInPicture();
2183
2206
  }
2207
+ async enterFullscreen() {
2208
+ return !this.destroyed && w(this.opts.video, this.container ?? void 0);
2209
+ }
2210
+ async exitFullscreen() {
2211
+ await T(this.opts.video, this.container ?? void 0);
2212
+ }
2184
2213
  retry() {
2185
2214
  return this.reload();
2186
2215
  }
@@ -2189,7 +2218,7 @@ var it = class {
2189
2218
  let e = ++this.loadGen;
2190
2219
  this.stopStats(), this.stopRefresh(), this.closeArming(), this.closeLiveState(), this.detachMedia?.(), this.engine?.destroy(), this.engine = null;
2191
2220
  try {
2192
- let t = await b({
2221
+ let t = await y({
2193
2222
  ...this.resolveOptions(),
2194
2223
  waitForLive: this.opts.waitForLive ? this.buildWaitConfig(e) : void 0
2195
2224
  });
@@ -2247,7 +2276,7 @@ function $(e) {
2247
2276
  }
2248
2277
  //#endregion
2249
2278
  //#region src/embed/host.ts
2250
- function at(e) {
2279
+ function ut(e) {
2251
2280
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
2252
2281
  if (!e.src) throw Error("mountEmbed: opts.src is required");
2253
2282
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -2306,8 +2335,8 @@ function at(e) {
2306
2335
  }
2307
2336
  //#endregion
2308
2337
  //#region src/embed/page.ts
2309
- var ot = /^[A-Za-z0-9:_.-]{1,128}$/;
2310
- function st() {
2338
+ var dt = /^[A-Za-z0-9:_.-]{1,128}$/;
2339
+ function ft() {
2311
2340
  let e = window.HAVIK_EMBED_CONFIG;
2312
2341
  if (e) return {
2313
2342
  baseUrl: e.baseUrl ?? "",
@@ -2329,9 +2358,9 @@ function st() {
2329
2358
  waitForLive: !0
2330
2359
  };
2331
2360
  }
2332
- async function ct() {
2333
- let e = st();
2334
- ut();
2361
+ async function pt() {
2362
+ let e = ft();
2363
+ ht();
2335
2364
  let t = document.createElement("video");
2336
2365
  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);
2337
2366
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -2386,7 +2415,7 @@ async function ct() {
2386
2415
  code: e.code,
2387
2416
  message: e.message
2388
2417
  })), e.on("autoplayblocked", () => {
2389
- i({ type: "oddin:autoplayblocked" }), lt(t, e);
2418
+ i({ type: "oddin:autoplayblocked" }), mt(t, e);
2390
2419
  });
2391
2420
  }, s = async (r) => {
2392
2421
  a?.destroy(), a = null;
@@ -2452,7 +2481,7 @@ async function ct() {
2452
2481
  a?.retry();
2453
2482
  break;
2454
2483
  case "oddin:load":
2455
- typeof e.matchUrn == "string" && ot.test(e.matchUrn) && s(e.matchUrn);
2484
+ typeof e.matchUrn == "string" && dt.test(e.matchUrn) && s(e.matchUrn);
2456
2485
  break;
2457
2486
  case "oddin:destroy": c();
2458
2487
  }
@@ -2464,18 +2493,18 @@ async function ct() {
2464
2493
  protocol: 1
2465
2494
  });
2466
2495
  }
2467
- function lt(e, t) {
2496
+ function mt(e, t) {
2468
2497
  if (document.getElementById("havik-tap")) return;
2469
2498
  let n = document.createElement("button");
2470
2499
  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", () => {
2471
2500
  n.remove(), t.play();
2472
2501
  }), document.body.appendChild(n);
2473
2502
  }
2474
- function ut() {
2503
+ function ht() {
2475
2504
  let e = document.createElement("style");
2476
2505
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
2477
2506
  }
2478
2507
  //#endregion
2479
- export { W as ODDIN_THEME, n as PlaybackError, K as applyTheme, ct as bootEmbed, me as clearDeviceId, Q as createPlayer, w as deriveEventsBaseUrl, M as detectDrmSupport, ce as fetchCatalog, ue as fetchTournament, fe as flattenMatches, D as getDeviceId, P as isLowLatencyManifest, $ as isOddinMessage, ge as licenseRequestHeaders, at as mountEmbed, v as pollToLive, b as resolveStream, C as subscribeLiveState, pe as watchStatus };
2508
+ export { G as ODDIN_THEME, n as PlaybackError, q as applyTheme, pt as bootEmbed, ge as clearDeviceId, Q as createPlayer, D as deriveEventsBaseUrl, xe as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ue as fetchCatalog, fe as fetchTournament, me as flattenMatches, A as getDeviceId, Ce as isLowLatencyManifest, $ as isOddinMessage, ve as licenseRequestHeaders, ut as mountEmbed, _ as pollToLive, y as resolveStream, E as subscribeLiveState, C as videoFullscreenActive, he as watchStatus };
2480
2509
 
2481
2510
  //# sourceMappingURL=index.js.map