@oddin-gg/havik-player 1.12.0 → 1.14.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
@@ -24,25 +24,28 @@ function n(e) {
24
24
  default: return "INTERNAL";
25
25
  }
26
26
  }
27
- var r = /* @__PURE__ */ new Set([
27
+ function r(e) {
28
+ return e === 401 ? "UNAUTHORIZED" : e === 403 ? "FORBIDDEN" : "DRM_CLIENT";
29
+ }
30
+ var i = /* @__PURE__ */ new Set([
28
31
  "TOO_EARLY",
29
32
  "UNAVAILABLE",
30
33
  "RATE_LIMITED",
31
34
  "NETWORK"
32
35
  ]);
33
- function i(e) {
34
- return r.has(e);
36
+ function a(e) {
37
+ return i.has(e);
35
38
  }
36
39
  //#endregion
37
40
  //#region src/core/types.ts
38
- async function a(e) {
41
+ async function o(e) {
39
42
  let t = typeof e == "function" ? await e() : e;
40
43
  if (!t || !t.apiKey) throw Error("havik-player: credential is missing an apiKey");
41
44
  return t;
42
45
  }
43
46
  //#endregion
44
47
  //#region src/core/http.ts
45
- function o(e, n) {
48
+ function s(e, n) {
46
49
  let r = e.replace(/\/+$/, ""), i;
47
50
  try {
48
51
  i = new URL(r + n);
@@ -52,15 +55,15 @@ function o(e, n) {
52
55
  if (i.protocol !== "https:" && i.protocol !== "http:") throw new t("INTERNAL", 0, `baseUrl must be http(s), got: ${JSON.stringify(e)}`);
53
56
  return i.toString();
54
57
  }
55
- function s(e) {
58
+ function c(e) {
56
59
  if (!e) return;
57
60
  let t = Number(e);
58
61
  if (Number.isFinite(t)) return Math.max(0, Math.round(t * 1e3));
59
62
  let n = Date.parse(e);
60
63
  if (!Number.isNaN(n)) return Math.max(0, n - Date.now());
61
64
  }
62
- async function c(e, n) {
63
- let r = await a(n.credential), i = n.headers ?? new Headers();
65
+ async function l(e, n) {
66
+ let r = await o(n.credential), i = n.headers ?? new Headers();
64
67
  i.set("x-api-key", r.apiKey);
65
68
  try {
66
69
  return await fetch(e, {
@@ -73,49 +76,49 @@ async function c(e, n) {
73
76
  mode: "cors"
74
77
  });
75
78
  } catch (e) {
76
- throw n.signal?.aborted ? new t("ABORTED", 0, "request aborted", { cause: e }) : new t("NETWORK", 0, `network request failed: ${l(e)}`, { cause: e });
79
+ throw n.signal?.aborted ? new t("ABORTED", 0, "request aborted", { cause: e }) : new t("NETWORK", 0, `network request failed: ${u(e)}`, { cause: e });
77
80
  }
78
81
  }
79
- function l(e) {
82
+ function u(e) {
80
83
  return e instanceof Error ? e.message : String(e);
81
84
  }
82
85
  //#endregion
83
86
  //#region src/core/waitForLive.ts
84
- var u = 1e3, d = 3e4, f = 600 * 1e3, p = 300 * 1e3, m = 1e3, h = {
87
+ var d = 1e3, f = 3e4, p = 600 * 1e3, m = 300 * 1e3, h = 1e3, g = {
85
88
  TOO_EARLY: "tooEarly",
86
89
  UNAVAILABLE: "unavailable",
87
90
  RATE_LIMITED: "rateLimited",
88
91
  NETWORK: "network"
89
92
  };
90
- function g(e) {
91
- let t = e === !0 || e === !1 ? {} : e, n = Math.max(m, t.floorMs ?? u), r = Math.max(n, t.ceilMs ?? d), i = Math.max(0, t.imminentMs ?? f), a = Math.max(r, t.sanityFloorMs ?? p);
93
+ function ee(e) {
94
+ let t = e === !0 || e === !1 ? {} : e, n = Math.max(h, t.floorMs ?? d), r = Math.max(n, t.ceilMs ?? f), i = Math.max(0, t.imminentMs ?? p), a = Math.max(r, t.sanityFloorMs ?? m);
92
95
  return {
93
96
  timeoutMs: t.timeoutMs,
94
97
  floorMs: n,
95
98
  ceilMs: r,
96
99
  imminentMs: i,
97
100
  sanityFloorMs: a,
98
- kickoffMs: ee(t.kickoffAt),
101
+ kickoffMs: te(t.kickoffAt),
99
102
  jitter: t.jitter ?? !0,
100
103
  onState: t.onState
101
104
  };
102
105
  }
103
- function ee(e) {
106
+ function te(e) {
104
107
  if (e == null) return;
105
108
  if (typeof e == "number") return Number.isFinite(e) ? e : void 0;
106
109
  let t = Date.parse(e);
107
110
  return Number.isFinite(t) ? t : void 0;
108
111
  }
109
- function te(e, t, n) {
112
+ function ne(e, t, n) {
110
113
  return Math.min(n, Math.max(t, e));
111
114
  }
112
- function ne(e) {
115
+ function _(e) {
113
116
  return Math.round(e * (1 + .15 * Math.random()));
114
117
  }
115
118
  function re(e, t, n, r) {
116
119
  let i = r ?? n.kickoffMs, a = i == null ? void 0 : Math.max(0, i - t), o = Math.max(n.floorMs, e ?? 0);
117
120
  if (a == null || a <= n.imminentMs) return {
118
- delay: te(e ?? n.floorMs, o, n.ceilMs),
121
+ delay: ne(e ?? n.floorMs, o, n.ceilMs),
119
122
  ttkMs: a
120
123
  };
121
124
  let s = Math.min(Math.floor(a / 10), n.sanityFloorMs, a);
@@ -138,22 +141,22 @@ function ie(e, n) {
138
141
  n?.addEventListener("abort", a, { once: !0 });
139
142
  });
140
143
  }
141
- async function _(e, n, r) {
142
- let a = g(n), o = Date.now(), s = a.timeoutMs == null ? Infinity : o + a.timeoutMs, c = 0, l;
144
+ async function v(e, n, r) {
145
+ let i = ee(n), o = Date.now(), s = i.timeoutMs == null ? Infinity : o + i.timeoutMs, c = 0, l;
143
146
  for (;;) {
144
147
  if (r?.aborted) throw new t("ABORTED", 0, "wait-for-live aborted");
145
148
  try {
146
149
  return await e();
147
150
  } catch (e) {
148
- if (!(e instanceof t) || !i(e.code)) throw e;
151
+ if (!(e instanceof t) || !a(e.code)) throw e;
149
152
  c += 1, e.liveStartsAtMs != null && Number.isFinite(e.liveStartsAtMs) && (l = e.liveStartsAtMs);
150
- let n = Date.now(), u = re(e.retryAfterMs, n, a, l), d = u.ttkMs, f = u.delay;
151
- if (a.jitter && (f = ne(f)), n + f > s) throw new t("TIMEOUT", e.httpStatus, `timed out after ${a.timeoutMs}ms waiting for the stream to go live`, {
153
+ let n = Date.now(), u = re(e.retryAfterMs, n, i, l), d = u.ttkMs, f = u.delay;
154
+ if (i.jitter && (f = _(f)), n + f > s) throw new t("TIMEOUT", e.httpStatus, `timed out after ${i.timeoutMs}ms waiting for the stream to go live`, {
152
155
  retryAfterMs: e.retryAfterMs,
153
156
  cause: e
154
157
  });
155
- a.onState?.({
156
- phase: h[e.code] ?? "unavailable",
158
+ i.onState?.({
159
+ phase: g[e.code] ?? "unavailable",
157
160
  retryInMs: f,
158
161
  attempt: c,
159
162
  elapsedMs: n - o,
@@ -164,33 +167,33 @@ async function _(e, n, r) {
164
167
  }
165
168
  //#endregion
166
169
  //#region src/core/resolve.ts
167
- async function v(e) {
170
+ async function y(e) {
168
171
  if (!e.matchUrn || typeof e.matchUrn != "string") throw new t("INVALID_URN", 0, "resolveStream: a non-empty matchUrn is required");
169
- let n = await c(o(e.baseUrl, `/v1/playback/${encodeURIComponent(e.matchUrn)}`), {
172
+ let n = await l(s(e.baseUrl, `/v1/playback/${encodeURIComponent(e.matchUrn)}`), {
170
173
  credential: e.credential,
171
174
  signal: e.signal
172
175
  });
173
176
  if (!n.ok) throw await ae(n);
174
- return se(await n.json(), e.matchUrn);
177
+ return S(await n.json(), e.matchUrn);
175
178
  }
176
- function y(e) {
177
- let t = () => v(e);
178
- return e.waitForLive ? _(t, e.waitForLive, e.signal) : t();
179
+ function b(e) {
180
+ let t = () => y(e);
181
+ return e.waitForLive ? v(t, e.waitForLive, e.signal) : t();
179
182
  }
180
183
  async function ae(e) {
181
- let r = s(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, c;
184
+ let r = c(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;
182
185
  try {
183
186
  let t = await e.json();
184
187
  if (t?.error?.message && (a = t.error.message), t?.error?.code && (o = t.error.code), typeof t?.liveStartsAt == "string") {
185
188
  let e = Date.parse(t.liveStartsAt);
186
- Number.isFinite(e) && (c = e);
189
+ Number.isFinite(e) && (s = e);
187
190
  }
188
191
  } catch {}
189
192
  return new t(n(e.status), e.status, a, {
190
193
  retryAfterMs: r,
191
194
  requestId: i,
192
195
  serverCode: o,
193
- liveStartsAtMs: c
196
+ liveStartsAtMs: s
194
197
  });
195
198
  }
196
199
  var oe = /* @__PURE__ */ new Set([
@@ -204,7 +207,7 @@ var oe = /* @__PURE__ */ new Set([
204
207
  "expiresAt",
205
208
  "analytics"
206
209
  ]);
207
- function b(e, n) {
210
+ function x(e, n) {
208
211
  let r;
209
212
  try {
210
213
  r = new URL(n);
@@ -214,20 +217,20 @@ function b(e, n) {
214
217
  if (r.protocol !== "https:" && r.protocol !== "http:") throw new t("INTERNAL", 0, `playback ${e} has a disallowed scheme: ${r.protocol}`);
215
218
  return n;
216
219
  }
217
- function se(e, n) {
220
+ function S(e, n) {
218
221
  if (!e.manifestUrl) throw new t("INTERNAL", 0, "playback response is missing manifestUrl");
219
222
  let r = e.drmEnabled ?? !!e.drm, i = {
220
223
  matchUrn: e.matchUrn ?? n,
221
224
  protocol: "hls",
222
225
  drmEnabled: r,
223
- manifestUrl: b("manifestUrl", e.manifestUrl),
226
+ manifestUrl: x("manifestUrl", e.manifestUrl),
224
227
  serverTime: e.serverTime ?? ""
225
228
  };
226
229
  if (e.liveStartsAt && (i.liveStartsAt = e.liveStartsAt), typeof e.analytics?.sid == "string" && e.analytics.sid && (i.analytics = { sid: e.analytics.sid }), r && e.drm) {
227
230
  let t = {};
228
- e.drm.widevine?.licenseUrl && (t.widevine = { licenseUrl: b("widevine licenseUrl", e.drm.widevine.licenseUrl) }), e.drm.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && (t.fairplay = {
229
- licenseUrl: b("fairplay licenseUrl", e.drm.fairplay.licenseUrl),
230
- certificateUrl: b("fairplay certificateUrl", e.drm.fairplay.certificateUrl)
231
+ e.drm.widevine?.licenseUrl && (t.widevine = { licenseUrl: x("widevine licenseUrl", e.drm.widevine.licenseUrl) }), e.drm.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && (t.fairplay = {
232
+ licenseUrl: x("fairplay licenseUrl", e.drm.fairplay.licenseUrl),
233
+ certificateUrl: x("fairplay certificateUrl", e.drm.fairplay.certificateUrl)
231
234
  }), (t.widevine || t.fairplay) && (i.drm = t);
232
235
  }
233
236
  let a = {};
@@ -236,29 +239,29 @@ function se(e, n) {
236
239
  }
237
240
  //#endregion
238
241
  //#region src/core/catalog.ts
239
- async function x(e) {
242
+ async function C(e) {
240
243
  let r = new URLSearchParams();
241
244
  e.status && r.set("status", Array.isArray(e.status) ? e.status.join(",") : e.status), e.sport && r.set("sport", e.sport);
242
- let i = r.toString(), a = o(e.baseUrl, "/v1/catalog" + (i ? `?${i}` : "")), s = new Headers();
243
- e.etag && s.set("If-None-Match", e.etag);
244
- let l = await c(a, {
245
+ let i = r.toString(), a = s(e.baseUrl, "/v1/catalog" + (i ? `?${i}` : "")), o = new Headers();
246
+ e.etag && o.set("If-None-Match", e.etag);
247
+ let c = await l(a, {
245
248
  credential: e.credential,
246
249
  signal: e.signal,
247
- headers: s
250
+ headers: o
248
251
  });
249
- if (l.status === 304) return {
252
+ if (c.status === 304) return {
250
253
  tournaments: [],
251
254
  etag: e.etag,
252
255
  notModified: !0
253
256
  };
254
- if (!l.ok) throw new t(n(l.status), l.status, `catalog request failed (HTTP ${l.status})`);
257
+ if (!c.ok) throw new t(n(c.status), c.status, `catalog request failed (HTTP ${c.status})`);
255
258
  return {
256
- tournaments: S(await l.json()),
257
- etag: l.headers.get("etag") ?? void 0,
259
+ tournaments: se(await c.json()),
260
+ etag: c.headers.get("etag") ?? void 0,
258
261
  notModified: !1
259
262
  };
260
263
  }
261
- function S(e) {
264
+ function se(e) {
262
265
  return (e.tournaments ?? []).map((e) => {
263
266
  let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
264
267
  return {
@@ -279,9 +282,9 @@ function S(e) {
279
282
  });
280
283
  }
281
284
  async function ce(e) {
282
- let r = o(e.baseUrl, `/v1/tournaments/${encodeURIComponent(e.urn)}`), i = new Headers();
285
+ let r = s(e.baseUrl, `/v1/tournaments/${encodeURIComponent(e.urn)}`), i = new Headers();
283
286
  e.etag && i.set("If-None-Match", e.etag);
284
- let a = await c(r, {
287
+ let a = await l(r, {
285
288
  credential: e.credential,
286
289
  signal: e.signal,
287
290
  headers: i
@@ -327,22 +330,22 @@ function ue(e) {
327
330
  //#endregion
328
331
  //#region src/core/poll.ts
329
332
  function de(e) {
330
- let r = Math.max(2e3, e.intervalMs ?? 1e4), i = new AbortController(), a, s, l, u = !1, d = () => {
333
+ let r = Math.max(2e3, e.intervalMs ?? 1e4), i = new AbortController(), a, o, c, u = !1, d = () => {
331
334
  u || (a = setTimeout(f, r));
332
335
  }, f = async () => {
333
336
  try {
334
- let r = o(e.baseUrl, `/v1/matches/${encodeURIComponent(e.matchUrn)}`), a = new Headers();
335
- s && a.set("If-None-Match", s);
336
- let u = await c(r, {
337
+ let r = s(e.baseUrl, `/v1/matches/${encodeURIComponent(e.matchUrn)}`), a = new Headers();
338
+ o && a.set("If-None-Match", o);
339
+ let u = await l(r, {
337
340
  credential: e.credential,
338
341
  signal: i.signal,
339
342
  headers: a
340
343
  });
341
344
  if (u.status !== 304) {
342
345
  if (!u.ok) throw new t(n(u.status), u.status, `match status request failed (HTTP ${u.status})`);
343
- s = u.headers.get("etag") ?? s;
346
+ o = u.headers.get("etag") ?? o;
344
347
  let r = await u.json(), i = r.status ?? "upcoming";
345
- i !== l && (l = i, e.onChange({
348
+ i !== c && (c = i, e.onChange({
346
349
  matchUrn: r.matchUrn ?? e.matchUrn,
347
350
  matchName: r.matchName,
348
351
  status: i,
@@ -361,8 +364,8 @@ function de(e) {
361
364
  }
362
365
  //#endregion
363
366
  //#region src/core/events.ts
364
- function C(e) {
365
- let t = Math.max(250, e.minBackoffMs ?? 1e3), n = Math.max(t, e.maxBackoffMs ?? 15e3), r = new AbortController(), i = !1, s = (e) => new Promise((t) => {
367
+ function w(e) {
368
+ 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) => {
366
369
  if (r.signal.aborted) return t();
367
370
  let n = setTimeout(t, e);
368
371
  r.signal.addEventListener("abort", () => {
@@ -400,9 +403,9 @@ function C(e) {
400
403
  let c = t;
401
404
  for (; !i;) {
402
405
  try {
403
- let n = await a(e.credential);
406
+ let n = await o(e.credential);
404
407
  if (i) return;
405
- let s = o(e.eventsBaseUrl, `/v1/events/${encodeURIComponent(e.matchUrn)}`), l = await fetch(s, {
408
+ let a = s(e.eventsBaseUrl, `/v1/events/${encodeURIComponent(e.matchUrn)}`), l = await fetch(a, {
406
409
  method: "GET",
407
410
  headers: {
408
411
  "x-api-key": n.apiKey,
@@ -422,13 +425,13 @@ function C(e) {
422
425
  e.onError?.(t);
423
426
  }
424
427
  if (i) return;
425
- await s(c), c = Math.min(n, c * 2);
428
+ await a(c), c = Math.min(n, c * 2);
426
429
  }
427
430
  })(), { close() {
428
431
  i || (i = !0, r.abort());
429
432
  } };
430
433
  }
431
- function w(e) {
434
+ function T(e) {
432
435
  try {
433
436
  let t = new URL(e);
434
437
  return t.hostname = `events.${t.hostname}`, t.pathname = "", t.search = "", t.origin;
@@ -438,24 +441,24 @@ function w(e) {
438
441
  }
439
442
  //#endregion
440
443
  //#region src/core/drm/deviceId.ts
441
- var T = "havik.player.deviceId", E;
442
- function D() {
443
- if (E) return E;
444
+ var E = "havik.player.deviceId", D;
445
+ function O() {
446
+ if (D) return D;
444
447
  try {
445
- let e = globalThis.localStorage?.getItem(T);
446
- if (e) return E = e, e;
448
+ let e = globalThis.localStorage?.getItem(E);
449
+ if (e) return D = e, e;
447
450
  } catch {}
448
451
  let e = pe();
449
- E = e;
452
+ D = e;
450
453
  try {
451
- globalThis.localStorage?.setItem(T, e);
454
+ globalThis.localStorage?.setItem(E, e);
452
455
  } catch {}
453
456
  return e;
454
457
  }
455
458
  function fe() {
456
- E = void 0;
459
+ D = void 0;
457
460
  try {
458
- globalThis.localStorage?.removeItem(T);
461
+ globalThis.localStorage?.removeItem(E);
459
462
  } catch {}
460
463
  }
461
464
  function pe() {
@@ -474,14 +477,14 @@ function me(e, t, n = {}) {
474
477
  let r = {
475
478
  "x-api-key": t.apiKey,
476
479
  "X-Match-Urn": e.matchUrn,
477
- "X-Device-Id": n.deviceId ?? D(),
480
+ "X-Device-Id": n.deviceId ?? O(),
478
481
  "Content-Type": "application/octet-stream"
479
482
  };
480
483
  return n.userId && (r["X-User-Id"] = n.userId), r;
481
484
  }
482
485
  //#endregion
483
486
  //#region src/core/drm/support.ts
484
- var he = 5e3, O = "video/mp4; codecs=\"avc1.42E01E\"", k = "audio/mp4; codecs=\"mp4a.40.2\"", ge = {
487
+ var he = 5e3, k = "video/mp4; codecs=\"avc1.42E01E\"", A = "audio/mp4; codecs=\"mp4a.40.2\"", ge = {
485
488
  widevine: ["com.widevine.alpha"],
486
489
  fairplay: ["com.apple.fps"]
487
490
  };
@@ -493,30 +496,30 @@ function _e(e) {
493
496
  "cenc"
494
497
  ],
495
498
  videoCapabilities: [{
496
- contentType: O,
499
+ contentType: k,
497
500
  robustness: "",
498
501
  encryptionScheme: "cbcs"
499
502
  }],
500
503
  audioCapabilities: [{
501
- contentType: k,
504
+ contentType: A,
502
505
  robustness: "",
503
506
  encryptionScheme: "cbcs"
504
507
  }]
505
508
  }] : [{
506
509
  initDataTypes: ["cenc"],
507
510
  videoCapabilities: [{
508
- contentType: O,
511
+ contentType: k,
509
512
  robustness: "SW_SECURE_DECODE",
510
513
  encryptionScheme: "cbcs"
511
514
  }],
512
515
  audioCapabilities: [{
513
- contentType: k,
516
+ contentType: A,
514
517
  robustness: "SW_SECURE_CRYPTO",
515
518
  encryptionScheme: "cbcs"
516
519
  }]
517
520
  }];
518
521
  }
519
- function A(e, t) {
522
+ function j(e, t) {
520
523
  return new Promise((n, r) => {
521
524
  let i = setTimeout(() => r(/* @__PURE__ */ Error("probe-timeout")), t);
522
525
  e.then((e) => {
@@ -526,7 +529,7 @@ function A(e, t) {
526
529
  });
527
530
  });
528
531
  }
529
- async function j(e = {}) {
532
+ async function M(e = {}) {
530
533
  let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? he, r = {};
531
534
  if (typeof window < "u" && window.isSecureContext === !1) return {
532
535
  verdict: "insecure-context",
@@ -550,7 +553,7 @@ async function j(e = {}) {
550
553
  break;
551
554
  }
552
555
  try {
553
- return await A((await A(i(n, _e(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
556
+ return await j((await j(i(n, _e(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
554
557
  verdict: "supported",
555
558
  system: e,
556
559
  detail: r
@@ -574,17 +577,17 @@ async function j(e = {}) {
574
577
  }
575
578
  //#endregion
576
579
  //#region src/core/manifest.ts
577
- var M = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
578
- function N(e) {
579
- return M.test(e);
580
+ var N = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
581
+ function P(e) {
582
+ return N.test(e);
580
583
  }
581
584
  //#endregion
582
585
  //#region src/core/stats.ts
583
- var P = { droppedFrames: 0 };
584
- function F(e) {
586
+ var F = { droppedFrames: 0 };
587
+ function I(e) {
585
588
  return e.getVideoPlaybackQuality?.()?.droppedVideoFrames ?? 0;
586
589
  }
587
- function I(e, t) {
590
+ function ve(e, t) {
588
591
  let n = [
589
592
  ["playing", () => t("playing")],
590
593
  ["waiting", () => t("buffering")],
@@ -601,7 +604,7 @@ function I(e, t) {
601
604
  }
602
605
  //#endregion
603
606
  //#region src/core/engine/native.ts
604
- function ve(e) {
607
+ function ye(e) {
605
608
  try {
606
609
  let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
607
610
  return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
@@ -636,7 +639,7 @@ var z = class {
636
639
  if (!n.drm?.fairplay) {
637
640
  r({
638
641
  type: "error",
639
- error: new t("INTERNAL", 0, "native HLS engine cannot satisfy this DRM (Widevine requires the hls.js engine); use engine \"auto\"/\"hls\" on a Widevine-capable browser"),
642
+ error: new t("DRM_CLIENT", 0, "native HLS engine cannot satisfy this DRM (Widevine requires the hls.js engine); use engine \"auto\"/\"hls\" on a Widevine-capable browser"),
640
643
  fatal: !0
641
644
  });
642
645
  return;
@@ -647,23 +650,23 @@ var z = class {
647
650
  type: "error",
648
651
  error: new t("INTERNAL", 0, "native HLS playback error"),
649
652
  fatal: !0
650
- }), e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = ye(n.manifestUrl, n.analytics?.sid), e.load();
653
+ }), e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = be(n.manifestUrl, n.analytics?.sid), e.load();
651
654
  }
652
655
  armFairPlay() {
653
- let { video: e, descriptor: n, credential: r, deviceId: i, userId: a, emit: o } = this.deps, s = n.drm?.fairplay;
654
- if (!s) return;
655
- this.fairplayLicenseUrl = s.licenseUrl;
656
- let c = (e, n, r) => o({
656
+ let { video: e, descriptor: n, credential: i, deviceId: a, userId: o, emit: s } = this.deps, c = n.drm?.fairplay;
657
+ if (!c) return;
658
+ this.fairplayLicenseUrl = c.licenseUrl;
659
+ let l = (e, n, i) => s({
657
660
  type: "error",
658
- error: new t(n === 403 ? "FORBIDDEN" : "INTERNAL", n, e),
659
- fatal: r
661
+ error: new t(r(n), n, e),
662
+ fatal: i
660
663
  });
661
664
  this.onEncrypted = (t) => {
662
- let o = t;
663
- !o.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV = ve(o.initData), (async () => {
665
+ let r = t;
666
+ !r.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV = ye(r.initData), (async () => {
664
667
  try {
665
668
  let t = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
666
- initDataTypes: [o.initDataType],
669
+ initDataTypes: [r.initDataType],
667
670
  videoCapabilities: [{
668
671
  contentType: "application/vnd.apple.mpegurl",
669
672
  robustness: ""
@@ -671,36 +674,39 @@ var z = class {
671
674
  distinctiveIdentifier: "not-allowed",
672
675
  persistentState: "not-allowed",
673
676
  sessionTypes: ["temporary"]
674
- }])).createMediaKeys(), l = await fetch(s.certificateUrl, { headers: { "x-api-key": r.apiKey } });
675
- if (!l.ok) throw Error(`certificate ${l.status}`);
676
- await t.setServerCertificate(await l.arrayBuffer()), await e.setMediaKeys(t);
677
+ }])).createMediaKeys(), s = await fetch(c.certificateUrl, { headers: { "x-api-key": i.apiKey } });
678
+ if (!s.ok) {
679
+ this.fairplayArmed = !1, l(`FairPlay certificate request failed: ${s.status}`, s.status, !0);
680
+ return;
681
+ }
682
+ await t.setServerCertificate(await s.arrayBuffer()), await e.setMediaKeys(t);
677
683
  let u = t.createSession();
678
684
  this.keySession = u, u.addEventListener("message", (e) => {
679
685
  (async () => {
680
686
  try {
681
- let t = await fetch(this.fairplayLicenseUrl ?? s.licenseUrl, {
687
+ let t = await fetch(this.fairplayLicenseUrl ?? c.licenseUrl, {
682
688
  method: "POST",
683
689
  body: e.message,
684
690
  headers: {
685
- "x-api-key": r.apiKey,
691
+ "x-api-key": i.apiKey,
686
692
  "X-Match-Urn": n.matchUrn,
687
- "X-Device-Id": i,
688
- ...a ? { "X-User-Id": a } : {},
693
+ "X-Device-Id": a,
694
+ ...o ? { "X-User-Id": o } : {},
689
695
  ...this.fairplayKeyIV ? { "X-Key-Iv": this.fairplayKeyIV } : {}
690
696
  }
691
697
  });
692
698
  if (!t.ok) {
693
- c(`FairPlay license request failed: ${t.status}`, t.status, !0);
699
+ l(`FairPlay license request failed: ${t.status}`, t.status, !0);
694
700
  return;
695
701
  }
696
702
  await u.update(new Uint8Array(await t.arrayBuffer()));
697
703
  } catch (e) {
698
- c(`FairPlay license request failed: ${String(e)}`, 0, !0);
704
+ l(`FairPlay license request failed: ${String(e)}`, 0, !0);
699
705
  }
700
706
  })();
701
- }), await u.generateRequest(o.initDataType, o.initData);
707
+ }), await u.generateRequest(r.initDataType, r.initData);
702
708
  } catch (e) {
703
- this.fairplayArmed = !1, c(`FairPlay key session failed: ${String(e)}`, 0, !0);
709
+ this.fairplayArmed = !1, l(`FairPlay key session failed: ${String(e)}`, 0, !0);
704
710
  }
705
711
  })());
706
712
  }, e.addEventListener("encrypted", this.onEncrypted);
@@ -752,7 +758,7 @@ var z = class {
752
758
  e.seekable.length > 0 && (e.currentTime = e.seekable.end(e.seekable.length - 1));
753
759
  }
754
760
  getStats() {
755
- let e = this.deps.video, t = { droppedFrames: F(e) };
761
+ let e = this.deps.video, t = { droppedFrames: I(e) };
756
762
  return e.duration === Infinity && (t.isLive = !0, e.seekable.length > 0 && (t.atLiveEdge = e.currentTime >= e.seekable.end(e.seekable.length - 1) - 2)), t;
757
763
  }
758
764
  stop() {
@@ -772,25 +778,25 @@ var z = class {
772
778
  } catch {}
773
779
  }
774
780
  };
775
- function ye(e, t) {
781
+ function be(e, t) {
776
782
  return t ? `${e}${e.includes("?") ? "&" : "?"}CMCD=${encodeURIComponent(`sid="${t}"`)}` : e;
777
783
  }
778
784
  //#endregion
779
785
  //#region src/core/engine/hls.ts
780
- function be() {
786
+ function xe() {
781
787
  return e.isSupported();
782
788
  }
783
- var xe = 3, Se = 5, Ce = 3, we = 3e4, Te = 12e3, Ee = 3e3, B = 6e3, De = 4e3, Oe = 2, ke = 5, Ae = 2, je = 9, Me = .5;
784
- function Ne(e) {
789
+ var Se = 3, Ce = 5, we = 3, Te = 3e4, Ee = 12e3, De = 3e3, B = 6e3, Oe = 4e3, ke = 2, Ae = 5, je = 2, Me = 9, Ne = .5;
790
+ function Pe(e) {
785
791
  if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
786
792
  delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
787
793
  return;
788
794
  }
789
- e.liveSyncDuration ??= Ae;
790
- let t = Math.max(12, e.liveSyncDuration + je);
791
- (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Pe(e, e.liveSyncDuration);
795
+ e.liveSyncDuration ??= je;
796
+ let t = Math.max(12, e.liveSyncDuration + Me);
797
+ (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Fe(e, e.liveSyncDuration);
792
798
  }
793
- function Pe(e, t) {
799
+ function Fe(e, t) {
794
800
  let n = (e, t, n) => Math.min(n, Math.max(t, e));
795
801
  e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
796
802
  }
@@ -831,7 +837,7 @@ var V = class {
831
837
  if (e.drmEnabled && !o) {
832
838
  i({
833
839
  type: "error",
834
- error: new t("INTERNAL", 0, e.drm?.fairplay?.licenseUrl && a ? "FairPlay cannot be played on the hls.js engine (WebKit binds keys via the media sinf box); use engine \"auto\" or \"native\"" : "DRM is required but none of the stream’s key systems is usable on this platform (WebKit takes FairPlay only; other browsers take Widevine only)"),
840
+ error: new t("DRM_CLIENT", 0, e.drm?.fairplay?.licenseUrl && a ? "FairPlay cannot be played on the hls.js engine (WebKit binds keys via the media sinf box); use engine \"auto\" or \"native\"" : "DRM is required but none of the stream’s key systems is usable on this platform (WebKit takes FairPlay only; other browsers take Widevine only)"),
835
841
  fatal: !0
836
842
  });
837
843
  return;
@@ -851,7 +857,7 @@ var V = class {
851
857
  let t = e.liveSyncPosition;
852
858
  if (typeof t != "number" || !Number.isFinite(t)) return;
853
859
  let { video: n } = this.deps;
854
- n.currentTime <= t + Me || (n.currentTime = t);
860
+ n.currentTime <= t + Ne || (n.currentTime = t);
855
861
  }
856
862
  startHls() {
857
863
  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 = {
@@ -887,14 +893,14 @@ var V = class {
887
893
  } } : {},
888
894
  ...c
889
895
  };
890
- Ne(d);
896
+ Pe(d);
891
897
  let f = new e(d);
892
898
  this.hls = f, f.on(e.Events.MANIFEST_PARSED, () => {
893
899
  this.mediaRecoveries = 0, this.networkRecoveries = 0, l({ type: "ready" });
894
900
  }), f.on(e.Events.FRAG_BUFFERED, (e, t) => {
895
901
  t?.frag?.type === "main" && (this.mediaRecoveries > 0 || this.networkRecoveries > 0 || this.licenseRecoveries > 0 || this.starvationKicks > 0) && !this.recoveryResetTimer && (this.recoveryResetTimer = setTimeout(() => {
896
902
  this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.recoveryResetTimer = null;
897
- }, we));
903
+ }, Te));
898
904
  }), f.on(e.Events.FRAG_LOADING, (e, t) => {
899
905
  t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
900
906
  }), f.on(e.Events.FRAG_LOADED, (e, t) => {
@@ -910,7 +916,7 @@ var V = class {
910
916
  }));
911
917
  }), f.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.endWatchdog = setInterval(() => {
912
918
  this.checkEndStale(), this.checkStarvation();
913
- }, Ee), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
919
+ }, De), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
914
920
  this.renditionSwitches += 1, l({
915
921
  type: "qualitychange",
916
922
  index: t.level
@@ -981,7 +987,7 @@ var V = class {
981
987
  }
982
988
  getStats() {
983
989
  let e = this.hls, t = {
984
- droppedFrames: F(this.deps.video),
990
+ droppedFrames: I(this.deps.video),
985
991
  lowLatency: this.detectedLowLatency,
986
992
  bytesLoaded: this.bytesLoaded,
987
993
  playlistBytesLoaded: this.playlistBytesLoaded,
@@ -1014,7 +1020,7 @@ var V = class {
1014
1020
  return;
1015
1021
  }
1016
1022
  let e = Date.now() - this.lastAdvanceMs;
1017
- !this.chaseSuspended && e > B && this.suspendChasing(), e > Te && this.emitEnded("stalled");
1023
+ !this.chaseSuspended && e > B && this.suspendChasing(), e > Ee && this.emitEnded("stalled");
1018
1024
  }
1019
1025
  checkStarvation() {
1020
1026
  let e = this.hls, { video: t } = this.deps;
@@ -1023,12 +1029,12 @@ var V = class {
1023
1029
  this.starvedTicks = 0;
1024
1030
  return;
1025
1031
  }
1026
- let n = this.lastAdvanceMs !== 0 && Date.now() - this.lastAdvanceMs < B, r = this.lastFragLoadingMs !== 0 && Date.now() - this.lastFragLoadingMs > De;
1032
+ let n = this.lastAdvanceMs !== 0 && Date.now() - this.lastAdvanceMs < B, r = this.lastFragLoadingMs !== 0 && Date.now() - this.lastFragLoadingMs > Oe;
1027
1033
  if (!(t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused && n && r)) {
1028
1034
  this.starvedTicks = 0;
1029
1035
  return;
1030
1036
  }
1031
- if (!(++this.starvedTicks < Oe) && (this.starvedTicks = 0, !(this.starvationKicks >= ke))) {
1037
+ if (!(++this.starvedTicks < ke) && (this.starvedTicks = 0, !(this.starvationKicks >= Ae))) {
1032
1038
  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");
1033
1039
  try {
1034
1040
  e.stopLoad(), e.startLoad(-1);
@@ -1077,7 +1083,7 @@ var V = class {
1077
1083
  scheduleLicenseRetry(t) {
1078
1084
  if (!(t.details === e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED || t.details === e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED)) return !1;
1079
1085
  let n = t.response?.code ?? 0;
1080
- if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Ce) return !1;
1086
+ if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= we) return !1;
1081
1087
  this.licenseRecoveries += 1;
1082
1088
  let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
1083
1089
  return this.licenseRetryTimer = setTimeout(() => {
@@ -1105,7 +1111,7 @@ var V = class {
1105
1111
  return;
1106
1112
  }
1107
1113
  if (t.type === e.ErrorTypes.NETWORK_ERROR) {
1108
- if (this.networkRecoveries < Se) {
1114
+ if (this.networkRecoveries < Ce) {
1109
1115
  this.networkRecoveries += 1;
1110
1116
  let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
1111
1117
  this.networkRetryTimer = setTimeout(() => {
@@ -1121,7 +1127,7 @@ var V = class {
1121
1127
  return;
1122
1128
  }
1123
1129
  if (t.type === e.ErrorTypes.MEDIA_ERROR) {
1124
- if (this.mediaRecoveries < xe) {
1130
+ if (this.mediaRecoveries < Se) {
1125
1131
  this.mediaRecoveries += 1, n.recoverMediaError();
1126
1132
  return;
1127
1133
  }
@@ -1140,10 +1146,10 @@ var V = class {
1140
1146
  }
1141
1147
  }
1142
1148
  toError(e, n = "PLAYBACK") {
1143
- let r = e.response?.code ?? 0, i = e.details ?? e.type, a = e.reason ?? e.error?.message ?? "";
1144
- return new t(r === 401 ? "UNAUTHORIZED" : r === 403 || n === "DRM" ? "FORBIDDEN" : "INTERNAL", r, `${n} error: ${i}${a ? ` (${a})` : ""}`);
1149
+ let i = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
1150
+ return new t(n === "DRM" ? r(i) : i === 401 ? "UNAUTHORIZED" : i === 403 ? "FORBIDDEN" : "INTERNAL", i, `${n} error: ${a}${o ? ` (${o})` : ""}`);
1145
1151
  }
1146
- }, Fe = 15e3, Ie = 100, H = 1, U = {
1152
+ }, Ie = 15e3, Le = 100, H = 1, U = {
1147
1153
  idle: "PLAYER_STATE_IDLE",
1148
1154
  waiting: "PLAYER_STATE_WAITING",
1149
1155
  loading: "PLAYER_STATE_LOADING",
@@ -1153,7 +1159,7 @@ var V = class {
1153
1159
  ended: "PLAYER_STATE_ENDED",
1154
1160
  error: "PLAYER_STATE_ERROR"
1155
1161
  };
1156
- function Le(e) {
1162
+ function Re(e) {
1157
1163
  try {
1158
1164
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
1159
1165
  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);
@@ -1161,7 +1167,7 @@ function Le(e) {
1161
1167
  return;
1162
1168
  }
1163
1169
  }
1164
- var Re = class {
1170
+ var ze = class {
1165
1171
  opts;
1166
1172
  url;
1167
1173
  now;
@@ -1196,7 +1202,7 @@ var Re = class {
1196
1202
  constructor(e) {
1197
1203
  this.opts = {
1198
1204
  ...e,
1199
- heartbeatMs: e.heartbeatMs ?? Fe
1205
+ heartbeatMs: e.heartbeatMs ?? Ie
1200
1206
  }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`, this.now = e.now ?? (() => Date.now()), this.bornMs = this.now();
1201
1207
  }
1202
1208
  start() {
@@ -1288,7 +1294,7 @@ var Re = class {
1288
1294
  };
1289
1295
  }
1290
1296
  enqueue(e) {
1291
- for (this.pending.push(this.wrap(e)); this.pending.length > Ie;) {
1297
+ for (this.pending.push(this.wrap(e)); this.pending.length > Le;) {
1292
1298
  let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
1293
1299
  this.pending.splice(e >= 0 ? e : 0, 1);
1294
1300
  }
@@ -1301,14 +1307,16 @@ var Re = class {
1301
1307
  if (e && e(this.url, t)) return;
1302
1308
  }
1303
1309
  let n = this.opts.fetchFn ?? (typeof fetch < "u" ? fetch : void 0);
1304
- n && n(this.url, {
1310
+ if (!n) return;
1311
+ let r = { "content-type": "application/json" };
1312
+ this.opts.apiKey && (r["x-api-key"] = this.opts.apiKey), n(this.url, {
1305
1313
  method: "POST",
1306
- headers: { "content-type": "application/json" },
1314
+ headers: r,
1307
1315
  body: t,
1308
1316
  keepalive: !0
1309
1317
  }).catch(() => {});
1310
1318
  }
1311
- }, ze = "1.12.0", W = {
1319
+ }, Be = "1.14.0", W = {
1312
1320
  accent: "#E1B600",
1313
1321
  accentText: "#1B1C23",
1314
1322
  background: "#0e0f13",
@@ -1341,15 +1349,15 @@ function K(e, t) {
1341
1349
  }
1342
1350
  //#endregion
1343
1351
  //#region src/ui/styles.ts
1344
- var q = "havik-player-styles", Be = "\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";
1345
- function Ve(e = document) {
1352
+ var q = "havik-player-styles", Ve = "\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";
1353
+ function He(e = document) {
1346
1354
  if (e.getElementById(q)) return;
1347
1355
  let t = e.createElement("style");
1348
- t.id = q, t.textContent = Be, e.head.appendChild(t);
1356
+ t.id = q, t.textContent = Ve, e.head.appendChild(t);
1349
1357
  }
1350
1358
  //#endregion
1351
1359
  //#region src/ui/icons.ts
1352
- var He = {
1360
+ var Ue = {
1353
1361
  play: "<path d=\"M8 5v14l11-7z\"/>",
1354
1362
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
1355
1363
  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\"/>",
@@ -1363,20 +1371,20 @@ var He = {
1363
1371
  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\"/>"
1364
1372
  };
1365
1373
  function J(e) {
1366
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${He[e]}</svg>`;
1374
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${Ue[e]}</svg>`;
1367
1375
  }
1368
1376
  //#endregion
1369
1377
  //#region src/ui/controls.ts
1370
- var Ue = 3e3, We = /* @__PURE__ */ new Set([
1378
+ var We = 3e3, Ge = /* @__PURE__ */ new Set([
1371
1379
  "GONE",
1372
1380
  "NOT_FOUND",
1373
1381
  "INVALID_URN",
1374
1382
  "UNAUTHORIZED"
1375
1383
  ]);
1376
- function Ge(e) {
1377
- return new Ke(e);
1384
+ function Ke(e) {
1385
+ return new qe(e);
1378
1386
  }
1379
- var Ke = class {
1387
+ var qe = class {
1380
1388
  root;
1381
1389
  video;
1382
1390
  player;
@@ -1416,7 +1424,7 @@ var Ke = class {
1416
1424
  scrubbing = !1;
1417
1425
  offFns = [];
1418
1426
  constructor(e) {
1419
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Ue, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.errorMessage = e.errorMessage, Ve(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");
1427
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? We, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.errorMessage = e.errorMessage, He(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");
1420
1428
  let t = (e, t, n) => {
1421
1429
  let r = this.doc.createElement(e);
1422
1430
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -1503,7 +1511,7 @@ var Ke = class {
1503
1511
  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);
1504
1512
  } else {
1505
1513
  let n = this.doc.createElement("div");
1506
- 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 && !We.has(this.lastError.code)) {
1514
+ 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 && !Ge.has(this.lastError.code)) {
1507
1515
  let e = this.doc.createElement("button");
1508
1516
  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);
1509
1517
  }
@@ -1594,17 +1602,17 @@ var Ke = class {
1594
1602
  renderQualityMenu() {
1595
1603
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
1596
1604
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
1597
- for (let n of e) this.menu.append(this.menuItem(qe(n), t === n.index, () => this.player.setQuality(n.index)));
1605
+ for (let n of e) this.menu.append(this.menuItem(Je(n), t === n.index, () => this.player.setQuality(n.index)));
1598
1606
  }
1599
1607
  renderAudioMenu() {
1600
1608
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
1601
1609
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
1602
- for (let t of e) this.menu.append(this.menuItem(Je(t), t.default, () => this.player.setAudioTrack(t.id)));
1610
+ for (let t of e) this.menu.append(this.menuItem(Ye(t), t.default, () => this.player.setAudioTrack(t.id)));
1603
1611
  }
1604
1612
  renderCcMenu() {
1605
1613
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
1606
1614
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
1607
- for (let t of e) this.menu.append(this.menuItem(Ye(t), !1, () => this.player.setTextTrack(t.id)));
1615
+ for (let t of e) this.menu.append(this.menuItem(Xe(t), !1, () => this.player.setTextTrack(t.id)));
1608
1616
  }
1609
1617
  onTracksChanged() {
1610
1618
  this.openMenu && this.renderMenu(this.openMenu);
@@ -1710,27 +1718,27 @@ function Y(e) {
1710
1718
  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");
1711
1719
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
1712
1720
  }
1713
- function qe(e) {
1721
+ function Je(e) {
1714
1722
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
1715
1723
  }
1716
- function Je(e) {
1724
+ function Ye(e) {
1717
1725
  return e.name || e.lang || `Track ${e.id}`;
1718
1726
  }
1719
- function Ye(e) {
1727
+ function Xe(e) {
1720
1728
  return e.name || e.lang || `Track ${e.id}`;
1721
1729
  }
1722
1730
  //#endregion
1723
1731
  //#region src/managed/index.ts
1724
- var Xe = 6e3, X = 45e3, Z = 2e3, Ze = 3, Qe = 2e3, $e = 480 * 1e3;
1732
+ var Ze = 6e3, X = 45e3, Qe = 2e3, $e = 3, Z = 2e3, et = 480 * 1e3;
1725
1733
  async function Q(e) {
1726
1734
  if (!e.video || typeof e.video.play != "function") throw new t("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
1727
1735
  if (!e.baseUrl) throw new t("INTERNAL", 0, "createPlayer: baseUrl is required");
1728
1736
  if (!e.matchUrn) throw new t("INVALID_URN", 0, "createPlayer: matchUrn is required");
1729
1737
  if (!e.credential) throw new t("INTERNAL", 0, "createPlayer: credential is required");
1730
- let n = new et(e);
1738
+ let n = new tt(e);
1731
1739
  return await n.start(), n;
1732
1740
  }
1733
- var et = class {
1741
+ var tt = class {
1734
1742
  opts;
1735
1743
  abort = new AbortController();
1736
1744
  listeners = {};
@@ -1766,13 +1774,13 @@ var et = class {
1766
1774
  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");
1767
1775
  let e = ++this.loadGen, n = this.resolveOptions();
1768
1776
  try {
1769
- let t = await v(n);
1777
+ let t = await y(n);
1770
1778
  if (await this.onResolved(t, e), e === this.loadGen && this._state === "error" && this._lastError) {
1771
1779
  let e = this._lastError;
1772
1780
  throw this.destroy(), e;
1773
1781
  }
1774
1782
  } catch (n) {
1775
- if (this.opts.waitForLive && n instanceof t && i(n.code)) {
1783
+ if (this.opts.waitForLive && n instanceof t && a(n.code)) {
1776
1784
  this.setState("waiting"), this.beginArming(e, n.liveStartsAtMs);
1777
1785
  return;
1778
1786
  }
@@ -1788,11 +1796,11 @@ var et = class {
1788
1796
  };
1789
1797
  }
1790
1798
  beginArming(e, t) {
1791
- let n = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? w(this.opts.baseUrl);
1799
+ let n = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? T(this.opts.baseUrl);
1792
1800
  n ? this.armWithSse(e, n, t) : this.beginArmingByPolling(e);
1793
1801
  }
1794
1802
  beginArmingByPolling(e) {
1795
- y({
1803
+ b({
1796
1804
  ...this.resolveOptions(),
1797
1805
  signal: this.abort.signal,
1798
1806
  waitForLive: this.buildWaitConfig(e)
@@ -1803,11 +1811,11 @@ var et = class {
1803
1811
  });
1804
1812
  }
1805
1813
  armWithSse(e, n, r) {
1806
- let a = !1, o = !1, s = new AbortController();
1814
+ let i = !1, o = !1, s = new AbortController();
1807
1815
  this.armFallbackCtrl = s, this.setState("waiting");
1808
1816
  let c = this.opts.waitForLive, l = c && c !== !0 ? c.kickoffAt : void 0, u = typeof l == "number" ? l : typeof l == "string" ? Date.parse(l) : NaN, d = r ?? (Number.isFinite(u) ? u : void 0);
1809
1817
  setTimeout(() => {
1810
- a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
1818
+ i || this.destroyed || e !== this.loadGen || this.emit("waiting", {
1811
1819
  phase: "tooEarly",
1812
1820
  retryInMs: X,
1813
1821
  attempt: 1,
@@ -1816,8 +1824,8 @@ var et = class {
1816
1824
  });
1817
1825
  }, 0);
1818
1826
  let f = () => this.destroyed || this.abort.signal.aborted || e !== this.loadGen, p = async (n) => {
1819
- if (!(a || f())) {
1820
- a = !0, this.closeArming(), s.abort();
1827
+ if (!(i || f())) {
1828
+ i = !0, this.closeArming(), s.abort();
1821
1829
  try {
1822
1830
  await this.onResolved(n, e);
1823
1831
  } catch (e) {
@@ -1826,29 +1834,29 @@ var et = class {
1826
1834
  }
1827
1835
  }
1828
1836
  }, m = () => {
1829
- a || o || f() || (o = !0, y({
1837
+ i || o || f() || (o = !0, b({
1830
1838
  ...this.resolveOptions(),
1831
1839
  signal: s.signal,
1832
1840
  waitForLive: this.buildWaitConfig(e)
1833
1841
  }).then((e) => p(e)).catch((e) => {
1834
- a || f() || s.signal.aborted || (a = !0, this.closeArming(), this.fail(e instanceof t ? e : new t("INTERNAL", 0, String(e))));
1842
+ i || f() || s.signal.aborted || (i = !0, this.closeArming(), this.fail(e instanceof t ? e : new t("INTERNAL", 0, String(e))));
1835
1843
  }));
1836
1844
  }, h = () => {
1837
- a || f() || v(this.resolveOptions()).then((e) => p(e)).catch((e) => {
1838
- a || f() || e instanceof t && i(e.code) || (a = !0, this.closeArming(), s.abort(), this.fail(e instanceof t ? e : new t("INTERNAL", 0, String(e))));
1845
+ i || f() || y(this.resolveOptions()).then((e) => p(e)).catch((e) => {
1846
+ i || f() || e instanceof t && a(e.code) || (i = !0, this.closeArming(), s.abort(), this.fail(e instanceof t ? e : new t("INTERNAL", 0, String(e))));
1839
1847
  });
1840
1848
  }, g = (e) => {
1841
1849
  this.armSseTimer && clearTimeout(this.armSseTimer), this.armSseTimer = setTimeout(() => {
1842
- !a && !f() && m();
1850
+ !i && !f() && m();
1843
1851
  }, e);
1844
1852
  };
1845
- g(Xe), this.armingSub = C({
1853
+ g(Ze), this.armingSub = w({
1846
1854
  eventsBaseUrl: n,
1847
1855
  matchUrn: this.opts.matchUrn,
1848
1856
  credential: this.opts.credential,
1849
1857
  onAlive: () => g(X),
1850
1858
  onState: (e) => {
1851
- a || f() || (e.state === "live" || e.state === "gone") && setTimeout(h, Math.random() * Z);
1859
+ i || f() || (e.state === "live" || e.state === "gone") && setTimeout(h, Math.random() * Qe);
1852
1860
  },
1853
1861
  onError: () => m()
1854
1862
  });
@@ -1868,10 +1876,10 @@ var et = class {
1868
1876
  async onResolved(e, t) {
1869
1877
  if (this.destroyed || t !== this.loadGen) return;
1870
1878
  this._descriptor = e, this.endedHandled = !1;
1871
- let n = await a(this.opts.credential);
1879
+ let n = await o(this.opts.credential);
1872
1880
  if (this.destroyed || t !== this.loadGen) return;
1873
1881
  this.setState("loading");
1874
- let r = this.createEngine(e, n), i = I(this.opts.video, (e) => {
1882
+ let r = this.createEngine(e, n), i = ve(this.opts.video, (e) => {
1875
1883
  this._state === "ended" || this._state === "error" || this.setState(e);
1876
1884
  });
1877
1885
  if (await r.load(), this.destroyed || t !== this.loadGen) {
@@ -1882,32 +1890,33 @@ var et = class {
1882
1890
  i(), r.destroy();
1883
1891
  return;
1884
1892
  }
1885
- this.engine = r, this.detachMedia = i, this.opts.maxBitrate != null && r.setMaxBitrate(this.opts.maxBitrate), this.startStats(), this.scheduleLicenseRefresh(), this.startLiveState(t), this.startAnalytics(e, r);
1893
+ this.engine = r, this.detachMedia = i, this.opts.maxBitrate != null && r.setMaxBitrate(this.opts.maxBitrate), this.startStats(), this.scheduleLicenseRefresh(), this.startLiveState(t), this.startAnalytics(e, r, n.apiKey);
1886
1894
  }
1887
- startAnalytics(e, t) {
1895
+ startAnalytics(e, t, n) {
1888
1896
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachSeekListener?.(), this.opts.analytics === !1) return;
1889
- let n = e.analytics?.sid;
1890
- if (!n) return;
1891
- let r = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? Le(this.opts.baseUrl);
1897
+ let r = e.analytics?.sid;
1892
1898
  if (!r) return;
1893
- let i = this.opts.video, a = new Re({
1894
- endpoint: r,
1895
- sid: n,
1899
+ let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? Re(this.opts.baseUrl);
1900
+ if (!i) return;
1901
+ let a = this.opts.video, o = new ze({
1902
+ endpoint: i,
1903
+ sid: r,
1904
+ apiKey: n,
1896
1905
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
1897
- sdkVersion: ze,
1906
+ sdkVersion: Be,
1898
1907
  sample: () => {
1899
- let e = this.engine?.getStats() ?? P, t = i.buffered.length ? Math.max(0, i.buffered.end(i.buffered.length - 1) - i.currentTime) : 0;
1908
+ let e = this.engine?.getStats() ?? F, t = a.buffered.length ? Math.max(0, a.buffered.end(a.buffered.length - 1) - a.currentTime) : 0;
1900
1909
  return {
1901
1910
  ...e,
1902
1911
  bufferedMs: Math.round(t * 1e3),
1903
1912
  state: this._state,
1904
- positionMs: Math.round(i.currentTime * 1e3)
1913
+ positionMs: Math.round(a.currentTime * 1e3)
1905
1914
  };
1906
1915
  }
1907
1916
  });
1908
- this.analyticsSession = a;
1909
- let o = () => a.noteSeek();
1910
- i.addEventListener("seeking", o), this.detachSeekListener = () => i.removeEventListener("seeking", o), a.start();
1917
+ this.analyticsSession = o;
1918
+ let s = () => o.noteSeek();
1919
+ a.addEventListener("seeking", s), this.detachSeekListener = () => a.removeEventListener("seeking", s), o.start();
1911
1920
  }
1912
1921
  createEngine(e, n) {
1913
1922
  let r = { ...this.opts.hlsConfig };
@@ -1916,7 +1925,7 @@ var et = class {
1916
1925
  video: this.opts.video,
1917
1926
  descriptor: e,
1918
1927
  credential: n,
1919
- deviceId: D(),
1928
+ deviceId: O(),
1920
1929
  userId: this.opts.userId,
1921
1930
  lowLatency: this.opts.lowLatency === void 0 || this.opts.lowLatency === "auto" || this.opts.lowLatency,
1922
1931
  snapToLiveOnRefocus: this.opts.snapToLiveOnRefocus,
@@ -1927,7 +1936,7 @@ var et = class {
1927
1936
  if (a === "native") return new z(i);
1928
1937
  if (a === "hls") return new V(i);
1929
1938
  if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && L(this.opts.video) && R()) return new z(i);
1930
- if (be()) return new V(i);
1939
+ if (xe()) return new V(i);
1931
1940
  if (L(this.opts.video)) return new z(i);
1932
1941
  throw new t("INTERNAL", 0, "no supported HLS playback engine in this browser");
1933
1942
  }
@@ -1958,11 +1967,11 @@ var et = class {
1958
1967
  }
1959
1968
  startLiveState(e) {
1960
1969
  if (this.opts.liveStateEvents === !1) return;
1961
- let n = this.opts.eventsBaseUrl ?? w(this.opts.baseUrl);
1970
+ let n = this.opts.eventsBaseUrl ?? T(this.opts.baseUrl);
1962
1971
  if (!n) return;
1963
1972
  this.closeLiveState();
1964
1973
  let r = 0, i = !1;
1965
- this.liveStateSub = C({
1974
+ this.liveStateSub = w({
1966
1975
  eventsBaseUrl: n,
1967
1976
  matchUrn: this.opts.matchUrn,
1968
1977
  credential: this.opts.credential,
@@ -1973,7 +1982,7 @@ var et = class {
1973
1982
  r = 0, i = !1;
1974
1983
  },
1975
1984
  onError: () => {
1976
- this.destroyed || e !== this.loadGen || i || (r += 1, r >= Ze && (i = !0, this.emit("warning", new t("NETWORK", 0, `live-state events unreachable at ${n} 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.`))));
1985
+ this.destroyed || e !== this.loadGen || i || (r += 1, r >= $e && (i = !0, this.emit("warning", new t("NETWORK", 0, `live-state events unreachable at ${n} 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.`))));
1977
1986
  }
1978
1987
  });
1979
1988
  }
@@ -1995,7 +2004,7 @@ var et = class {
1995
2004
  }
1996
2005
  t.controls = !1;
1997
2006
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
1998
- r && r.insertBefore(n, t), n.appendChild(t), K(n, this.opts.theme), this.container = n, this.controlBar = Ge({
2007
+ r && r.insertBefore(n, t), n.appendChild(t), K(n, this.opts.theme), this.container = n, this.controlBar = Ke({
1999
2008
  root: n,
2000
2009
  video: t,
2001
2010
  player: this,
@@ -2018,7 +2027,7 @@ var et = class {
2018
2027
  }
2019
2028
  startStats() {
2020
2029
  this.stopStats();
2021
- let e = Math.max(500, this.opts.statsIntervalMs ?? Qe);
2030
+ let e = Math.max(500, this.opts.statsIntervalMs ?? Z);
2022
2031
  this.statsTimer = setInterval(() => {
2023
2032
  if (!this.engine) return;
2024
2033
  let e = this.engine.getStats();
@@ -2030,7 +2039,7 @@ var et = class {
2030
2039
  }
2031
2040
  scheduleLicenseRefresh() {
2032
2041
  this.stopRefresh();
2033
- let e = this.opts.licenseRefreshMs ?? $e;
2042
+ let e = this.opts.licenseRefreshMs ?? et;
2034
2043
  !e || !this._descriptor?.drmEnabled || (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
2035
2044
  }
2036
2045
  stopRefresh() {
@@ -2039,7 +2048,7 @@ var et = class {
2039
2048
  async refreshLicense() {
2040
2049
  let e = this.engine;
2041
2050
  if (!(this.destroyed || !e)) try {
2042
- let t = await v(this.resolveOptions());
2051
+ let t = await y(this.resolveOptions());
2043
2052
  if (this.destroyed || this.engine !== e) return;
2044
2053
  let n = t.drm?.widevine?.licenseUrl, r = t.drm?.fairplay?.licenseUrl;
2045
2054
  (n || r) && (e.setLicenseUrls({
@@ -2047,7 +2056,7 @@ var et = class {
2047
2056
  fairplay: r
2048
2057
  }), this._descriptor = t);
2049
2058
  } catch (e) {
2050
- e instanceof t && !i(e.code) && (this.stopRefresh(), this.destroyed || this.emit("warning", e));
2059
+ e instanceof t && !a(e.code) && (this.stopRefresh(), this.destroyed || this.emit("warning", e));
2051
2060
  }
2052
2061
  }
2053
2062
  async play() {
@@ -2067,7 +2076,7 @@ var et = class {
2067
2076
  this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
2068
2077
  }
2069
2078
  getStats() {
2070
- return this.engine?.getStats() ?? P;
2079
+ return this.engine?.getStats() ?? F;
2071
2080
  }
2072
2081
  getQualityLevels() {
2073
2082
  return this.engine?.getQualityLevels() ?? [];
@@ -2113,7 +2122,7 @@ var et = class {
2113
2122
  let e = ++this.loadGen;
2114
2123
  this.stopStats(), this.stopRefresh(), this.closeArming(), this.closeLiveState(), this.detachMedia?.(), this.engine?.destroy(), this.engine = null;
2115
2124
  try {
2116
- let t = await y({
2125
+ let t = await b({
2117
2126
  ...this.resolveOptions(),
2118
2127
  waitForLive: this.opts.waitForLive ? this.buildWaitConfig(e) : void 0
2119
2128
  });
@@ -2174,7 +2183,7 @@ function $(e) {
2174
2183
  }
2175
2184
  //#endregion
2176
2185
  //#region src/embed/host.ts
2177
- function tt(e) {
2186
+ function nt(e) {
2178
2187
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
2179
2188
  if (!e.src) throw Error("mountEmbed: opts.src is required");
2180
2189
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -2233,8 +2242,8 @@ function tt(e) {
2233
2242
  }
2234
2243
  //#endregion
2235
2244
  //#region src/embed/page.ts
2236
- var nt = /^[A-Za-z0-9:_.-]{1,128}$/;
2237
- function rt() {
2245
+ var rt = /^[A-Za-z0-9:_.-]{1,128}$/;
2246
+ function it() {
2238
2247
  let e = window.HAVIK_EMBED_CONFIG;
2239
2248
  if (e) return {
2240
2249
  baseUrl: e.baseUrl ?? "",
@@ -2256,9 +2265,9 @@ function rt() {
2256
2265
  waitForLive: !0
2257
2266
  };
2258
2267
  }
2259
- async function it() {
2260
- let e = rt();
2261
- ot();
2268
+ async function at() {
2269
+ let e = it();
2270
+ st();
2262
2271
  let n = document.createElement("video");
2263
2272
  n.setAttribute("playsinline", ""), n.muted = e.muted ?? !0, n.style.cssText = "width:100%;height:100%;object-fit:contain;background:#000;aspect-ratio:auto", document.body.appendChild(n);
2264
2273
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -2313,7 +2322,7 @@ async function it() {
2313
2322
  code: e.code,
2314
2323
  message: e.message
2315
2324
  })), e.on("autoplayblocked", () => {
2316
- i({ type: "oddin:autoplayblocked" }), at(n, e);
2325
+ i({ type: "oddin:autoplayblocked" }), ot(n, e);
2317
2326
  });
2318
2327
  }, s = async (r) => {
2319
2328
  a?.destroy(), a = null;
@@ -2379,7 +2388,7 @@ async function it() {
2379
2388
  a?.retry();
2380
2389
  break;
2381
2390
  case "oddin:load":
2382
- typeof e.matchUrn == "string" && nt.test(e.matchUrn) && s(e.matchUrn);
2391
+ typeof e.matchUrn == "string" && rt.test(e.matchUrn) && s(e.matchUrn);
2383
2392
  break;
2384
2393
  case "oddin:destroy":
2385
2394
  c();
@@ -2393,18 +2402,18 @@ async function it() {
2393
2402
  protocol: 1
2394
2403
  });
2395
2404
  }
2396
- function at(e, t) {
2405
+ function ot(e, t) {
2397
2406
  if (document.getElementById("havik-tap")) return;
2398
2407
  let n = document.createElement("button");
2399
2408
  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", () => {
2400
2409
  n.remove(), t.play();
2401
2410
  }), document.body.appendChild(n);
2402
2411
  }
2403
- function ot() {
2412
+ function st() {
2404
2413
  let e = document.createElement("style");
2405
2414
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
2406
2415
  }
2407
2416
  //#endregion
2408
- export { W as ODDIN_THEME, t as PlaybackError, K as applyTheme, it as bootEmbed, fe as clearDeviceId, Q as createPlayer, w as deriveEventsBaseUrl, j as detectDrmSupport, x as fetchCatalog, ce as fetchTournament, ue as flattenMatches, D as getDeviceId, N as isLowLatencyManifest, $ as isOddinMessage, me as licenseRequestHeaders, tt as mountEmbed, _ as pollToLive, y as resolveStream, C as subscribeLiveState, de as watchStatus };
2417
+ export { W as ODDIN_THEME, t as PlaybackError, K as applyTheme, at as bootEmbed, fe as clearDeviceId, Q as createPlayer, T as deriveEventsBaseUrl, M as detectDrmSupport, C as fetchCatalog, ce as fetchTournament, ue as flattenMatches, O as getDeviceId, P as isLowLatencyManifest, $ as isOddinMessage, me as licenseRequestHeaders, nt as mountEmbed, v as pollToLive, b as resolveStream, w as subscribeLiveState, de as watchStatus };
2409
2418
 
2410
2419
  //# sourceMappingURL=index.js.map