@oddin-gg/havik-player 2.10.0 → 2.11.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
@@ -46,9 +46,32 @@ async function s(e) {
46
46
  function c(e) {
47
47
  return e === "live" || e === "ended" || e === "upcoming" || e === "pending" ? e : "upcoming";
48
48
  }
49
+ function l(e) {
50
+ if (!Array.isArray(e)) return;
51
+ let t = [];
52
+ for (let n of e) {
53
+ if (!n || typeof n != "object") continue;
54
+ let e = typeof n.urn == "string" ? n.urn : "", r = typeof n.name == "string" ? n.name : "";
55
+ if (!e && !r) continue;
56
+ let i = {
57
+ urn: e,
58
+ name: r,
59
+ isHome: n.isHome === !0
60
+ };
61
+ typeof n.logoUrl == "string" && u(n.logoUrl) && (i.logoUrl = n.logoUrl), t.push(i);
62
+ }
63
+ return t.length > 0 ? t : void 0;
64
+ }
65
+ function u(e) {
66
+ try {
67
+ return new URL(e).protocol === "https:";
68
+ } catch {
69
+ return !1;
70
+ }
71
+ }
49
72
  //#endregion
50
73
  //#region src/core/http.ts
51
- function l(e, t) {
74
+ function d(e, t) {
52
75
  let r = e.replace(/\/+$/, ""), i;
53
76
  try {
54
77
  i = new URL(r + t);
@@ -58,14 +81,14 @@ function l(e, t) {
58
81
  if (i.protocol !== "https:" && i.protocol !== "http:") throw new n("INTERNAL", 0, `baseUrl must be http(s), got: ${JSON.stringify(e)}`);
59
82
  return i.toString();
60
83
  }
61
- function u(e) {
84
+ function f(e) {
62
85
  if (!e) return;
63
86
  let t = Number(e);
64
87
  if (Number.isFinite(t)) return Math.max(0, Math.round(t * 1e3));
65
88
  let n = Date.parse(e);
66
89
  if (!Number.isNaN(n)) return Math.max(0, n - Date.now());
67
90
  }
68
- async function d(e, t) {
91
+ async function p(e, t) {
69
92
  let r = await s(t.credential), i = t.headers ?? new Headers();
70
93
  if (t.apiHeaders) for (let [e, n] of Object.entries(t.apiHeaders)) i.has(e) || i.set(e, n);
71
94
  i.set("x-api-key", r.apiKey);
@@ -80,49 +103,49 @@ async function d(e, t) {
80
103
  mode: "cors"
81
104
  });
82
105
  } catch (e) {
83
- throw t.signal?.aborted ? new n("ABORTED", 0, "request aborted", { cause: e }) : new n("NETWORK", 0, `network request failed: ${f(e)}`, { cause: e });
106
+ throw t.signal?.aborted ? new n("ABORTED", 0, "request aborted", { cause: e }) : new n("NETWORK", 0, `network request failed: ${m(e)}`, { cause: e });
84
107
  }
85
108
  }
86
- function f(e) {
109
+ function m(e) {
87
110
  return e instanceof Error ? e.message : String(e);
88
111
  }
89
112
  //#endregion
90
113
  //#region src/core/waitForLive.ts
91
- var p = 1e3, m = 3e4, h = 6e5, g = 3e5, _ = 1e3, ee = {
114
+ var h = 1e3, g = 3e4, _ = 6e5, ee = 3e5, te = 1e3, ne = {
92
115
  TOO_EARLY: "tooEarly",
93
116
  UNAVAILABLE: "unavailable",
94
117
  RATE_LIMITED: "rateLimited",
95
118
  NETWORK: "network"
96
119
  };
97
- function te(e) {
98
- let t = e === !0 || e === !1 ? {} : e, n = Math.max(_, t.floorMs ?? p), r = Math.max(n, t.ceilMs ?? m), i = Math.max(0, t.imminentMs ?? h), a = Math.max(r, t.sanityFloorMs ?? g);
120
+ function re(e) {
121
+ let t = e === !0 || e === !1 ? {} : e, n = Math.max(te, t.floorMs ?? h), r = Math.max(n, t.ceilMs ?? g), i = Math.max(0, t.imminentMs ?? _), a = Math.max(r, t.sanityFloorMs ?? ee);
99
122
  return {
100
123
  timeoutMs: t.timeoutMs,
101
124
  floorMs: n,
102
125
  ceilMs: r,
103
126
  imminentMs: i,
104
127
  sanityFloorMs: a,
105
- kickoffMs: ne(t.kickoffAt),
128
+ kickoffMs: ie(t.kickoffAt),
106
129
  jitter: t.jitter ?? !0,
107
130
  onState: t.onState
108
131
  };
109
132
  }
110
- function ne(e) {
133
+ function ie(e) {
111
134
  if (e == null) return;
112
135
  if (typeof e == "number") return Number.isFinite(e) ? e : void 0;
113
136
  let t = Date.parse(e);
114
137
  return Number.isFinite(t) ? t : void 0;
115
138
  }
116
- function re(e, t, n) {
139
+ function ae(e, t, n) {
117
140
  return Math.min(n, Math.max(t, e));
118
141
  }
119
- function ie(e) {
142
+ function oe(e) {
120
143
  return Math.round(e * (1 + .15 * Math.random()));
121
144
  }
122
- function ae(e, t, n, r) {
145
+ function se(e, t, n, r) {
123
146
  let i = r ?? n.kickoffMs, a = i == null ? void 0 : Math.max(0, i - t), o = Math.max(n.floorMs, e ?? 0);
124
147
  if (a == null || a <= n.imminentMs) return {
125
- delay: re(e ?? n.floorMs, o, n.ceilMs),
148
+ delay: ae(e ?? n.floorMs, o, n.ceilMs),
126
149
  ttkMs: a
127
150
  };
128
151
  let s = Math.min(Math.floor(a / 10), n.sanityFloorMs, a);
@@ -131,7 +154,7 @@ function ae(e, t, n, r) {
131
154
  ttkMs: a
132
155
  };
133
156
  }
134
- function oe(e, t) {
157
+ function ce(e, t) {
135
158
  return new Promise((r, i) => {
136
159
  if (t?.aborted) {
137
160
  i(new n("ABORTED", 0, "wait-for-live aborted"));
@@ -146,7 +169,7 @@ function oe(e, t) {
146
169
  });
147
170
  }
148
171
  async function v(e, t, r) {
149
- let i = te(t), a = Date.now(), s = i.timeoutMs == null ? Infinity : a + i.timeoutMs, c = 0, l;
172
+ let i = re(t), a = Date.now(), s = i.timeoutMs == null ? Infinity : a + i.timeoutMs, c = 0, l;
150
173
  for (;;) {
151
174
  if (r?.aborted) throw new n("ABORTED", 0, "wait-for-live aborted");
152
175
  try {
@@ -154,18 +177,18 @@ async function v(e, t, r) {
154
177
  } catch (e) {
155
178
  if (!(e instanceof n) || !o(e.code)) throw e;
156
179
  c += 1, e.liveStartsAtMs != null && Number.isFinite(e.liveStartsAtMs) && (l = e.liveStartsAtMs);
157
- let t = Date.now(), u = ae(e.retryAfterMs, t, i, l), d = u.ttkMs, f = u.delay;
158
- 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`, {
180
+ let t = Date.now(), u = se(e.retryAfterMs, t, i, l), d = u.ttkMs, f = u.delay;
181
+ if (i.jitter && (f = oe(f)), t + f > s) throw new n("TIMEOUT", e.httpStatus, `timed out after ${i.timeoutMs}ms waiting for the stream to go live`, {
159
182
  retryAfterMs: e.retryAfterMs,
160
183
  cause: e
161
184
  });
162
185
  i.onState?.({
163
- phase: ee[e.code] ?? "unavailable",
186
+ phase: ne[e.code] ?? "unavailable",
164
187
  retryInMs: f,
165
188
  attempt: c,
166
189
  elapsedMs: t - a,
167
190
  ttkMs: d
168
- }), await oe(f, r);
191
+ }), await ce(f, r);
169
192
  }
170
193
  }
171
194
  }
@@ -173,20 +196,20 @@ async function v(e, t, r) {
173
196
  //#region src/core/resolve.ts
174
197
  async function y(e) {
175
198
  if (!e.matchUrn || typeof e.matchUrn != "string") throw new n("INVALID_URN", 0, "resolveStream: a non-empty matchUrn is required");
176
- let t = await d(l(e.baseUrl, `/v1/playback/${encodeURIComponent(e.matchUrn)}`), {
199
+ let t = await p(d(e.baseUrl, `/v1/playback/${encodeURIComponent(e.matchUrn)}`), {
177
200
  credential: e.credential,
178
201
  signal: e.signal,
179
202
  apiHeaders: e.apiHeaders
180
203
  });
181
- if (!t.ok) throw await se(t);
182
- return le(await t.json(), e.matchUrn);
204
+ if (!t.ok) throw await le(t);
205
+ return de(await t.json(), e.matchUrn);
183
206
  }
184
207
  function b(e) {
185
208
  let t = () => y(e);
186
209
  return e.waitForLive ? v(t, e.waitForLive, e.signal) : t();
187
210
  }
188
- async function se(e) {
189
- let t = u(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;
211
+ async function le(e) {
212
+ let t = f(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;
190
213
  try {
191
214
  let t = await e.json();
192
215
  if (t?.error?.message && (a = t.error.message), t?.error?.code && (o = t.error.code), typeof t?.liveStartsAt == "string") {
@@ -201,7 +224,7 @@ async function se(e) {
201
224
  liveStartsAtMs: s
202
225
  });
203
226
  }
204
- var ce = /* @__PURE__ */ new Set([
227
+ var ue = /* @__PURE__ */ new Set([
205
228
  "matchUrn",
206
229
  "protocol",
207
230
  "drmEnabled",
@@ -222,7 +245,7 @@ function x(e, t) {
222
245
  if (r.protocol !== "https:" && r.protocol !== "http:") throw new n("INTERNAL", 0, `playback ${e} has a disallowed scheme: ${r.protocol}`);
223
246
  return t;
224
247
  }
225
- function le(e, t) {
248
+ function de(e, t) {
226
249
  if (!e.manifestUrl) throw new n("INTERNAL", 0, "playback response is missing manifestUrl");
227
250
  let r = e.drmEnabled ?? !!e.drm, i = {
228
251
  matchUrn: e.matchUrn ?? t,
@@ -239,7 +262,7 @@ function le(e, t) {
239
262
  }), (t.widevine || t.fairplay) && (i.drm = t);
240
263
  }
241
264
  let a = {};
242
- for (let [t, n] of Object.entries(e)) ce.has(t) || (a[t] = n);
265
+ for (let [t, n] of Object.entries(e)) ue.has(t) || (a[t] = n);
243
266
  return Object.keys(a).length > 0 && (i.extensions = a), i;
244
267
  }
245
268
  //#endregion
@@ -247,37 +270,37 @@ function le(e, t) {
247
270
  function S(e) {
248
271
  return e.ownerDocument ?? document;
249
272
  }
250
- function ue(e) {
273
+ function C(e) {
251
274
  return e.fullscreenElement ?? e.webkitFullscreenElement ?? null;
252
275
  }
253
- function C(e, t) {
254
- let n = ue(S(e));
276
+ function w(e, t) {
277
+ let n = C(S(e));
255
278
  return n != null && (n === e || t != null && n === t) || e.webkitDisplayingFullscreen === !0;
256
279
  }
257
- async function w(e, t) {
280
+ async function T(e, t) {
258
281
  let n = t ?? e;
259
282
  if (typeof n.requestFullscreen == "function") return await n.requestFullscreen(), !0;
260
283
  if (typeof n.webkitRequestFullscreen == "function") return n.webkitRequestFullscreen(), !0;
261
284
  let r = e;
262
285
  return r.webkitSupportsFullscreen && typeof r.webkitEnterFullscreen == "function" ? (r.webkitEnterFullscreen(), !0) : !1;
263
286
  }
264
- async function T(e, t) {
287
+ async function E(e, t) {
265
288
  let n = e;
266
289
  if (n.webkitDisplayingFullscreen) {
267
290
  n.webkitExitFullscreen?.();
268
291
  return;
269
292
  }
270
- let r = S(e), i = ue(r);
293
+ let r = S(e), i = C(r);
271
294
  i != null && (i === e || t != null && i === t) && (typeof r.exitFullscreen == "function" ? await r.exitFullscreen() : r.webkitExitFullscreen?.());
272
295
  }
273
296
  //#endregion
274
297
  //#region src/core/catalog.ts
275
- async function de(e) {
298
+ async function fe(e) {
276
299
  let t = new URLSearchParams();
277
300
  e.status && t.set("status", Array.isArray(e.status) ? e.status.join(",") : e.status), e.sport && t.set("sport", e.sport);
278
- let i = t.toString(), a = l(e.baseUrl, "/v1/catalog" + (i ? `?${i}` : "")), o = new Headers();
301
+ let i = t.toString(), a = d(e.baseUrl, "/v1/catalog" + (i ? `?${i}` : "")), o = new Headers();
279
302
  e.etag && o.set("If-None-Match", e.etag);
280
- let s = await d(a, {
303
+ let s = await p(a, {
281
304
  credential: e.credential,
282
305
  signal: e.signal,
283
306
  headers: o,
@@ -290,12 +313,12 @@ async function de(e) {
290
313
  };
291
314
  if (!s.ok) throw new n(r(s.status), s.status, `catalog request failed (HTTP ${s.status})`);
292
315
  return {
293
- tournaments: fe(await s.json()),
316
+ tournaments: pe(await s.json()),
294
317
  etag: s.headers.get("etag") ?? void 0,
295
318
  notModified: !1
296
319
  };
297
320
  }
298
- function fe(e) {
321
+ function pe(e) {
299
322
  return (e.tournaments ?? []).map((e) => {
300
323
  let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
301
324
  return {
@@ -311,15 +334,16 @@ function fe(e) {
311
334
  datePlannedStart: e.datePlannedStart,
312
335
  tournamentUrn: t,
313
336
  tournamentName: n,
314
- sport: r
337
+ sport: r,
338
+ teams: l(e.teams)
315
339
  }))
316
340
  };
317
341
  });
318
342
  }
319
- async function pe(e) {
320
- let t = l(e.baseUrl, `/v1/tournaments/${encodeURIComponent(e.urn)}`), i = new Headers();
343
+ async function me(e) {
344
+ let t = d(e.baseUrl, `/v1/tournaments/${encodeURIComponent(e.urn)}`), i = new Headers();
321
345
  e.etag && i.set("If-None-Match", e.etag);
322
- let a = await d(t, {
346
+ let a = await p(t, {
323
347
  credential: e.credential,
324
348
  signal: e.signal,
325
349
  headers: i,
@@ -336,13 +360,13 @@ async function pe(e) {
336
360
  };
337
361
  if (!a.ok) throw new n(r(a.status), a.status, `tournament request failed (HTTP ${a.status})`);
338
362
  return {
339
- tournament: me(await a.json()),
363
+ tournament: he(await a.json()),
340
364
  etag: a.headers.get("etag") ?? void 0,
341
365
  notModified: !1,
342
366
  notFound: !1
343
367
  };
344
368
  }
345
- function me(e) {
369
+ function he(e) {
346
370
  let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
347
371
  return {
348
372
  urn: t,
@@ -357,23 +381,24 @@ function me(e) {
357
381
  datePlannedStart: e.datePlannedStart,
358
382
  tournamentUrn: t,
359
383
  tournamentName: n,
360
- sport: r
384
+ sport: r,
385
+ teams: l(e.teams)
361
386
  }))
362
387
  };
363
388
  }
364
- function he(e) {
389
+ function ge(e) {
365
390
  return e.tournaments.flatMap((e) => e.matches);
366
391
  }
367
392
  //#endregion
368
393
  //#region src/core/poll.ts
369
- function ge(e) {
394
+ function _e(e) {
370
395
  let t = Math.max(2e3, e.intervalMs ?? 1e4), i = new AbortController(), a, o, s, u = !1, f = () => {
371
- u || (a = setTimeout(p, t));
372
- }, p = async () => {
396
+ u || (a = setTimeout(m, t));
397
+ }, m = async () => {
373
398
  try {
374
- let t = l(e.baseUrl, `/v1/matches/${encodeURIComponent(e.matchUrn)}`), a = new Headers();
399
+ let t = d(e.baseUrl, `/v1/matches/${encodeURIComponent(e.matchUrn)}`), a = new Headers();
375
400
  o && a.set("If-None-Match", o);
376
- let u = await d(t, {
401
+ let u = await p(t, {
377
402
  credential: e.credential,
378
403
  signal: i.signal,
379
404
  headers: a,
@@ -382,13 +407,18 @@ function ge(e) {
382
407
  if (u.status !== 304) {
383
408
  if (!u.ok) throw new n(r(u.status), u.status, `match status request failed (HTTP ${u.status})`);
384
409
  o = u.headers.get("etag") ?? o;
385
- let t = await u.json(), i = c(t.status), a = typeof t.status == "string" ? t.status : i;
386
- a !== s && (s = a, e.onChange({
410
+ let t = await u.json(), i = c(t.status), a = typeof t.status == "string" ? t.status : i, d = l(t.teams), f = JSON.stringify([
411
+ a,
412
+ t.datePlannedStart ?? null,
413
+ d ?? null
414
+ ]);
415
+ f !== s && (s = f, e.onChange({
387
416
  matchUrn: t.matchUrn ?? e.matchUrn,
388
417
  matchName: t.matchName,
389
418
  status: i,
390
419
  statusRaw: t.status,
391
- datePlannedStart: t.datePlannedStart
420
+ datePlannedStart: t.datePlannedStart,
421
+ teams: d
392
422
  }));
393
423
  }
394
424
  } catch (t) {
@@ -397,21 +427,21 @@ function ge(e) {
397
427
  f();
398
428
  }
399
429
  };
400
- return p(), { stop() {
430
+ return m(), { stop() {
401
431
  u = !0, i.abort(), a && clearTimeout(a);
402
432
  } };
403
433
  }
404
434
  //#endregion
405
435
  //#region src/core/events.ts
406
- var _e = 45e3;
407
- function E(e) {
408
- let t = Math.max(250, e.minBackoffMs ?? 1e3), n = Math.max(t, e.maxBackoffMs ?? 15e3), r = e.idleTimeoutMs ?? _e, i = Number.isFinite(r) && r > 0 ? r : 0, a = new AbortController(), o = !1, c = (e) => new Promise((t) => {
436
+ var ve = 45e3;
437
+ function D(e) {
438
+ let t = Math.max(250, e.minBackoffMs ?? 1e3), n = Math.max(t, e.maxBackoffMs ?? 15e3), r = e.idleTimeoutMs ?? ve, i = Number.isFinite(r) && r > 0 ? r : 0, a = new AbortController(), o = !1, c = (e) => new Promise((t) => {
409
439
  if (a.signal.aborted) return t();
410
440
  let n = setTimeout(t, e);
411
441
  a.signal.addEventListener("abort", () => {
412
442
  clearTimeout(n), t();
413
443
  }, { once: !0 });
414
- }), u = (t) => {
444
+ }), l = (t) => {
415
445
  e.onAlive?.();
416
446
  let n = "message", r = [];
417
447
  for (let e of t.split("\n")) {
@@ -424,9 +454,9 @@ function E(e) {
424
454
  let t = JSON.parse(r.join("\n"));
425
455
  t && typeof t.state == "string" && e.onState(t);
426
456
  } catch {}
427
- }, d = /\r\n\r\n|\n\n|\r\r/, f = async (e, t, n) => {
428
- let r = e.getReader(), a = new TextDecoder(), s = "", c = !1, l, f = () => {
429
- i && (clearTimeout(l), l = setTimeout(() => {
457
+ }, u = /\r\n\r\n|\n\n|\r\r/, f = async (e, t, n) => {
458
+ let r = e.getReader(), a = new TextDecoder(), s = "", c = !1, d, f = () => {
459
+ i && (clearTimeout(d), d = setTimeout(() => {
430
460
  n(), r.cancel().catch(() => {});
431
461
  }, i));
432
462
  };
@@ -436,27 +466,27 @@ function E(e) {
436
466
  if (n) return;
437
467
  e && e.length > 0 && (f(), c || (c = !0, t())), s += a.decode(e, { stream: !0 });
438
468
  let i;
439
- for (; (i = d.exec(s)) !== null;) u(s.slice(0, i.index)), s = s.slice(i.index + i[0].length);
469
+ for (; (i = u.exec(s)) !== null;) l(s.slice(0, i.index)), s = s.slice(i.index + i[0].length);
440
470
  if (o) return;
441
471
  }
442
472
  } finally {
443
- clearTimeout(l), await r.cancel().catch(() => {});
473
+ clearTimeout(d), await r.cancel().catch(() => {});
444
474
  }
445
475
  };
446
476
  return (async () => {
447
477
  let r = t;
448
478
  for (; !o;) {
449
- let u = new AbortController(), d = () => u.abort();
450
- a.signal.addEventListener("abort", d, { once: !0 });
479
+ let l = new AbortController(), u = () => l.abort();
480
+ a.signal.addEventListener("abort", u, { once: !0 });
451
481
  let p = null, m, h = () => {
452
- p = `no data for ${i}ms`, u.abort();
482
+ p = `no data for ${i}ms`, l.abort();
453
483
  };
454
484
  try {
455
485
  let n = await s(e.credential);
456
486
  if (o) return;
457
- let a = l(e.eventsBaseUrl, `/v1/events/${encodeURIComponent(e.matchUrn)}`);
487
+ let a = d(e.eventsBaseUrl, `/v1/events/${encodeURIComponent(e.matchUrn)}`);
458
488
  i && (m = setTimeout(() => {
459
- p = `no response headers within ${i}ms`, u.abort();
489
+ p = `no response headers within ${i}ms`, l.abort();
460
490
  }, i));
461
491
  let c = await fetch(a, {
462
492
  method: "GET",
@@ -464,7 +494,7 @@ function E(e) {
464
494
  "x-api-key": n.apiKey,
465
495
  accept: "text/event-stream"
466
496
  },
467
- signal: u.signal,
497
+ signal: l.signal,
468
498
  cache: "no-store",
469
499
  credentials: "omit",
470
500
  mode: "cors"
@@ -477,7 +507,7 @@ function E(e) {
477
507
  if (o || a.signal.aborted) return;
478
508
  p || e.onError?.(t);
479
509
  } finally {
480
- clearTimeout(m), a.signal.removeEventListener("abort", d), u.abort();
510
+ clearTimeout(m), a.signal.removeEventListener("abort", u), l.abort();
481
511
  }
482
512
  if (o) return;
483
513
  p && e.onError?.(/* @__PURE__ */ Error(`live-state subscribe idle: ${p} — reconnecting`)), await c(r), r = Math.min(n, r * 2);
@@ -486,7 +516,7 @@ function E(e) {
486
516
  o || (o = !0, a.abort());
487
517
  } };
488
518
  }
489
- function D(e) {
519
+ function O(e) {
490
520
  try {
491
521
  let t = new URL(e);
492
522
  return t.hostname = `events.${t.hostname}`, t.pathname = "", t.search = "", t.origin;
@@ -496,27 +526,27 @@ function D(e) {
496
526
  }
497
527
  //#endregion
498
528
  //#region src/core/drm/deviceId.ts
499
- var O = "havik.player.deviceId", k;
500
- function A() {
501
- if (k) return k;
529
+ var k = "havik.player.deviceId", A;
530
+ function j() {
531
+ if (A) return A;
502
532
  try {
503
- let e = globalThis.localStorage?.getItem(O);
504
- if (e) return k = e, e;
533
+ let e = globalThis.localStorage?.getItem(k);
534
+ if (e) return A = e, e;
505
535
  } catch {}
506
- let e = ye();
507
- k = e;
536
+ let e = be();
537
+ A = e;
508
538
  try {
509
- globalThis.localStorage?.setItem(O, e);
539
+ globalThis.localStorage?.setItem(k, e);
510
540
  } catch {}
511
541
  return e;
512
542
  }
513
- function ve() {
514
- k = void 0;
543
+ function ye() {
544
+ A = void 0;
515
545
  try {
516
- globalThis.localStorage?.removeItem(O);
546
+ globalThis.localStorage?.removeItem(k);
517
547
  } catch {}
518
548
  }
519
- function ye() {
549
+ function be() {
520
550
  let e = globalThis.crypto;
521
551
  if (e && typeof e.randomUUID == "function") return e.randomUUID();
522
552
  let t = /* @__PURE__ */ new Uint8Array(16);
@@ -528,21 +558,21 @@ function ye() {
528
558
  }
529
559
  //#endregion
530
560
  //#region src/core/drm/headers.ts
531
- function be(e, t, n = {}) {
561
+ function xe(e, t, n = {}) {
532
562
  let r = {
533
563
  "x-api-key": t.apiKey,
534
564
  "X-Match-Urn": e.matchUrn,
535
- "X-Device-Id": n.deviceId ?? A(),
565
+ "X-Device-Id": n.deviceId ?? j(),
536
566
  "Content-Type": "application/octet-stream"
537
567
  };
538
568
  return n.userId && (r["X-User-Id"] = n.userId), r;
539
569
  }
540
570
  //#endregion
541
571
  //#region src/core/drm/cdm.ts
542
- var j = /* @__PURE__ */ new WeakMap();
543
- function xe(e, t) {
544
- let n = j.get(e);
545
- n || (n = /* @__PURE__ */ new Map(), j.set(e, n));
572
+ var M = /* @__PURE__ */ new WeakMap();
573
+ function Se(e, t) {
574
+ let n = M.get(e);
575
+ n || (n = /* @__PURE__ */ new Map(), M.set(e, n));
546
576
  let r = n.get(t.keySystem);
547
577
  if (r) return r;
548
578
  let i = t.createMediaKeys().catch((e) => {
@@ -550,22 +580,22 @@ function xe(e, t) {
550
580
  });
551
581
  return n.set(t.keySystem, i), i;
552
582
  }
553
- function M() {
583
+ function N() {
554
584
  return typeof navigator < "u" && typeof navigator.requestMediaKeySystemAccess == "function";
555
585
  }
556
- var N = ["SW_SECURE_DECODE", "SW_SECURE_CRYPTO"];
557
- function Se(e) {
586
+ var P = ["SW_SECURE_DECODE", "SW_SECURE_CRYPTO"];
587
+ function Ce(e) {
558
588
  return e.startsWith("com.widevine");
559
589
  }
560
- function Ce(e, t) {
561
- if (!Se(e)) return t;
590
+ function F(e, t) {
591
+ if (!Ce(e)) return t;
562
592
  let n = (e, t) => {
563
593
  let n = e.videoCapabilities;
564
594
  return !n?.length || !n.some((e) => e.robustness) ? !1 : n.every((e) => {
565
- let n = N.indexOf(e.robustness);
566
- return n === -1 || N.indexOf(t) > n;
595
+ let n = P.indexOf(e.robustness);
596
+ return n === -1 || P.indexOf(t) > n;
567
597
  });
568
- }, r = N.flatMap((e) => t.filter((t) => n(t, e)).map((t) => ({
598
+ }, r = P.flatMap((e) => t.filter((t) => n(t, e)).map((t) => ({
569
599
  ...t,
570
600
  videoCapabilities: t.videoCapabilities?.map((t) => ({
571
601
  ...t,
@@ -575,19 +605,86 @@ function Ce(e, t) {
575
605
  return [...t, ...r];
576
606
  }
577
607
  function we(e) {
578
- return (t, n) => M() ? navigator.requestMediaKeySystemAccess(t, Ce(t, n)).then((t) => ({
608
+ return (t, n) => N() ? navigator.requestMediaKeySystemAccess(t, F(t, n)).then((t) => ({
579
609
  keySystem: t.keySystem,
580
610
  getConfiguration: () => t.getConfiguration(),
581
- createMediaKeys: () => xe(e, t)
611
+ createMediaKeys: () => Se(e, t)
582
612
  })) : Promise.reject(/* @__PURE__ */ Error("navigator.requestMediaKeySystemAccess is unavailable — EME needs a secure context (https, or localhost)"));
583
613
  }
614
+ var I = {
615
+ video: "SW_SECURE_DECODE",
616
+ audio: "SW_SECURE_CRYPTO",
617
+ scheme: "cbcs"
618
+ };
619
+ function Te(e) {
620
+ if (!N()) return;
621
+ let t = (e, t) => ({
622
+ contentType: e,
623
+ robustness: t,
624
+ encryptionScheme: I.scheme
625
+ });
626
+ we(e)("com.widevine.alpha", [{
627
+ initDataTypes: ["cenc"],
628
+ distinctiveIdentifier: "optional",
629
+ persistentState: "optional",
630
+ sessionTypes: ["temporary"],
631
+ audioCapabilities: [t("audio/mp4; codecs=\"mp4a.40.2\"", I.audio)],
632
+ videoCapabilities: [t("video/mp4; codecs=\"avc1.42e01e\"", I.video)]
633
+ }]).then((e) => e.createMediaKeys()).catch(() => {});
634
+ }
635
+ function L(e) {
636
+ let t = R(e);
637
+ if (/^avc[13]/.test(t)) return "avc";
638
+ if (/^(hvc1|hev1)/.test(t)) return "hevc";
639
+ if (/^(dvh1|dvhe)/.test(t)) return "dv";
640
+ if (t.startsWith("av01")) return "av1";
641
+ if (/^vp0?9/.test(t)) return "vp9";
642
+ }
643
+ function R(e) {
644
+ return (e ?? "").trim().toLowerCase();
645
+ }
646
+ var Ee = {
647
+ hevc: "hvc1.1.6.L93.90",
648
+ dv: "dvh1.05.06",
649
+ av1: "av01.0.05M.08",
650
+ vp9: "vp09.00.10.08"
651
+ }, De = /* @__PURE__ */ new Map(), z = /* @__PURE__ */ new Map();
652
+ function Oe(e) {
653
+ let t = R(e), n = De.get(t);
654
+ if (n) return n;
655
+ let r = {
656
+ initDataTypes: ["cenc"],
657
+ distinctiveIdentifier: "optional",
658
+ persistentState: "optional",
659
+ sessionTypes: ["temporary"],
660
+ videoCapabilities: [{
661
+ contentType: `video/mp4; codecs="${e.trim()}"`,
662
+ robustness: I.video,
663
+ encryptionScheme: I.scheme
664
+ }]
665
+ }, i = (async () => {
666
+ try {
667
+ return await navigator.requestMediaKeySystemAccess("com.widevine.alpha", F("com.widevine.alpha", [r])), !0;
668
+ } catch {
669
+ return !1;
670
+ }
671
+ })().then((e) => (z.set(t, e), e));
672
+ return De.set(t, i), i;
673
+ }
674
+ function ke() {
675
+ if (N()) for (let e of Object.values(Ee)) Oe(e);
676
+ }
677
+ function Ae(e) {
678
+ let t = L(e);
679
+ if (t) return t === "avc" ? !0 : z.get(R(e)) ?? z.get(R(Ee[t]));
680
+ }
584
681
  //#endregion
585
682
  //#region src/core/drm/support.ts
586
- var Te = 5e3, Ee = "video/mp4; codecs=\"avc1.42E01E\"", De = "audio/mp4; codecs=\"mp4a.40.2\"", Oe = {
683
+ var je = 5e3, Me = "video/mp4; codecs=\"avc1.42E01E\"", B = "audio/mp4; codecs=\"mp4a.40.2\"", Ne = {
587
684
  widevine: ["com.widevine.alpha"],
588
685
  fairplay: ["com.apple.fps"]
589
686
  };
590
- function ke(e) {
687
+ function Pe(e) {
591
688
  return e === "fairplay" ? [{
592
689
  initDataTypes: [
593
690
  "sinf",
@@ -595,30 +692,30 @@ function ke(e) {
595
692
  "cenc"
596
693
  ],
597
694
  videoCapabilities: [{
598
- contentType: Ee,
695
+ contentType: Me,
599
696
  robustness: "",
600
697
  encryptionScheme: "cbcs"
601
698
  }],
602
699
  audioCapabilities: [{
603
- contentType: De,
700
+ contentType: B,
604
701
  robustness: "",
605
702
  encryptionScheme: "cbcs"
606
703
  }]
607
- }] : Ce("com.widevine.alpha", [{
704
+ }] : F("com.widevine.alpha", [{
608
705
  initDataTypes: ["cenc"],
609
706
  videoCapabilities: [{
610
- contentType: Ee,
611
- robustness: "SW_SECURE_DECODE",
612
- encryptionScheme: "cbcs"
707
+ contentType: Me,
708
+ robustness: I.video,
709
+ encryptionScheme: I.scheme
613
710
  }],
614
711
  audioCapabilities: [{
615
- contentType: De,
616
- robustness: "SW_SECURE_CRYPTO",
617
- encryptionScheme: "cbcs"
712
+ contentType: B,
713
+ robustness: I.audio,
714
+ encryptionScheme: I.scheme
618
715
  }]
619
716
  }]);
620
717
  }
621
- function P(e, t) {
718
+ function V(e, t) {
622
719
  return new Promise((n, r) => {
623
720
  let i = setTimeout(() => r(/* @__PURE__ */ Error("probe-timeout")), t);
624
721
  e.then((e) => {
@@ -628,8 +725,8 @@ function P(e, t) {
628
725
  });
629
726
  });
630
727
  }
631
- async function Ae(e = {}) {
632
- let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? Te, r = {};
728
+ async function Fe(e = {}) {
729
+ let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? je, r = {};
633
730
  if (typeof window < "u" && window.isSecureContext === !1) return {
634
731
  verdict: "insecure-context",
635
732
  detail: r
@@ -641,7 +738,7 @@ async function Ae(e = {}) {
641
738
  };
642
739
  let a = !1, o = !1, s = Date.now() + n, c = () => Math.max(1, s - Date.now());
643
740
  for (let e of t) {
644
- let t = Oe[e];
741
+ let t = Ne[e];
645
742
  if (!t) {
646
743
  r[e] = "unknown system";
647
744
  continue;
@@ -652,7 +749,7 @@ async function Ae(e = {}) {
652
749
  break;
653
750
  }
654
751
  try {
655
- return await P((await P(i(n, ke(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
752
+ return await V((await V(i(n, Pe(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
656
753
  verdict: "supported",
657
754
  system: e,
658
755
  detail: r
@@ -676,49 +773,49 @@ async function Ae(e = {}) {
676
773
  }
677
774
  //#endregion
678
775
  //#region src/core/manifest.ts
679
- var je = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
680
- function Me(e) {
681
- return je.test(e);
776
+ var Ie = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
777
+ function Le(e) {
778
+ return Ie.test(e);
682
779
  }
683
780
  //#endregion
684
781
  //#region src/core/env.ts
685
- var F = {
782
+ var H = {
686
783
  integration: "https://feed-dev.oddin-video.gg",
687
784
  production: "https://feed.oddin-video.gg"
688
785
  };
689
- function I(e) {
690
- let t = Object.prototype.hasOwnProperty.call(F, e) ? F[e] : void 0;
786
+ function U(e) {
787
+ let t = Object.prototype.hasOwnProperty.call(H, e) ? H[e] : void 0;
691
788
  if (!t) throw new n("INTERNAL", 0, `unknown env ${JSON.stringify(e)} — expected 'integration' or 'production'`);
692
789
  return t;
693
790
  }
694
791
  //#endregion
695
792
  //#region src/core/stats.ts
696
- var L = { droppedFrames: 0 };
697
- function R(e) {
793
+ var W = { droppedFrames: 0 };
794
+ function G(e) {
698
795
  try {
699
796
  return new URL(e).host;
700
797
  } catch {
701
798
  return "";
702
799
  }
703
800
  }
704
- var Ne = /\.(m3u8|ts|m4s|mp4|cmfv|cmfa)(\?|$)/i;
705
- function Pe(e) {
801
+ var Re = /\.(m3u8|ts|m4s|mp4|cmfv|cmfa)(\?|$)/i;
802
+ function ze(e) {
706
803
  if (typeof performance > "u" || typeof performance.getEntriesByType != "function") return "";
707
804
  let t = "", n = -1, r = performance.getEntriesByType("resource");
708
- for (let i of r) if (!(i.startTime < e) && (i.initiatorType === "video" || i.initiatorType === "audio" || Ne.test(i.name)) && i.startTime >= n) {
709
- let e = R(i.name);
805
+ for (let i of r) if (!(i.startTime < e) && (i.initiatorType === "video" || i.initiatorType === "audio" || Re.test(i.name)) && i.startTime >= n) {
806
+ let e = G(i.name);
710
807
  e && (t = e, n = i.startTime);
711
808
  }
712
809
  return t;
713
810
  }
714
- function z(e) {
811
+ function K(e) {
715
812
  let t = e.getVideoPlaybackQuality?.();
716
813
  return {
717
814
  dropped: t?.droppedVideoFrames ?? 0,
718
815
  totalFrames: t?.totalVideoFrames ?? 0
719
816
  };
720
817
  }
721
- function Fe(e, t) {
818
+ function Be(e, t) {
722
819
  let n = [
723
820
  ["playing", () => t("playing")],
724
821
  ["waiting", () => t("buffering")],
@@ -735,7 +832,7 @@ function Fe(e, t) {
735
832
  }
736
833
  //#endregion
737
834
  //#region src/core/engine/native.ts
738
- function Ie(e) {
835
+ function Ve(e) {
739
836
  try {
740
837
  let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
741
838
  return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
@@ -743,15 +840,15 @@ function Ie(e) {
743
840
  return;
744
841
  }
745
842
  }
746
- function B(e) {
843
+ function q(e) {
747
844
  return !!(e ?? (typeof document < "u" ? document.createElement("video") : void 0))?.canPlayType("application/vnd.apple.mpegurl");
748
845
  }
749
- function V() {
846
+ function J() {
750
847
  if (typeof window > "u") return !1;
751
848
  let e = "WebKitMediaKeys" in window || typeof HTMLMediaElement < "u" && "webkitSetMediaKeys" in HTMLMediaElement.prototype, t = typeof navigator < "u" && /Apple/i.test(navigator.vendor ?? "");
752
849
  return e || t;
753
850
  }
754
- var H = class {
851
+ var Y = class {
755
852
  name = "native";
756
853
  deps;
757
854
  onLoaded;
@@ -787,7 +884,7 @@ var H = class {
787
884
  type: "error",
788
885
  error: new n("INTERNAL", 0, "native HLS playback error"),
789
886
  fatal: !0
790
- }), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = Le(t.manifestUrl, t.analytics?.sid), e.load();
887
+ }), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = He(t.manifestUrl, t.analytics?.sid), e.load();
791
888
  }
792
889
  startLoad() {
793
890
  this.loadStarted || (this.loadStarted = !0, this.restorePreload());
@@ -809,7 +906,7 @@ var H = class {
809
906
  });
810
907
  this.onEncrypted = (n) => {
811
908
  let i = n;
812
- i.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = Ie(i.initData), (async () => {
909
+ i.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = Ve(i.initData), (async () => {
813
910
  try {
814
911
  let n = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
815
912
  initDataTypes: [i.initDataType],
@@ -904,11 +1001,11 @@ var H = class {
904
1001
  e.seekable.length > 0 && (e.currentTime = e.seekable.end(e.seekable.length - 1));
905
1002
  }
906
1003
  getStats() {
907
- let e = this.deps.video, t = z(e), n = {
1004
+ let e = this.deps.video, t = K(e), n = {
908
1005
  droppedFrames: t.dropped,
909
1006
  decodedFrames: t.totalFrames
910
1007
  };
911
- return this.cdnHost ||= Pe(this.builtAt), this.cdnHost && (n.cdnHost = this.cdnHost), this.deps.descriptor.drmEnabled && (n.keySystem = "fairplay"), e.duration === Infinity && (n.isLive = !0, e.seekable.length > 0 && (n.atLiveEdge = e.currentTime >= e.seekable.end(e.seekable.length - 1) - 2)), n;
1008
+ return this.cdnHost ||= ze(this.builtAt), this.cdnHost && (n.cdnHost = this.cdnHost), this.deps.descriptor.drmEnabled && (n.keySystem = "fairplay"), e.duration === Infinity ? (n.isLive = !0, e.seekable.length > 0 && (n.atLiveEdge = e.currentTime >= e.seekable.end(e.seekable.length - 1) - 2)) : Number.isFinite(e.duration) && e.duration > 0 && (n.isLive = !1), n;
912
1009
  }
913
1010
  stop() {
914
1011
  try {
@@ -927,35 +1024,198 @@ var H = class {
927
1024
  } catch {}
928
1025
  }
929
1026
  };
930
- function Le(e, t) {
1027
+ function He(e, t) {
931
1028
  return t ? `${e}${e.includes("?") ? "&" : "?"}CMCD=${encodeURIComponent(`sid="${t}"`)}` : e;
932
1029
  }
933
1030
  //#endregion
1031
+ //#region src/core/engine/prefetch.ts
1032
+ var Ue = 15e3, We = 5e3, Ge = 6;
1033
+ function Ke(e) {
1034
+ try {
1035
+ let t = new URL(e);
1036
+ return t.searchParams.delete("CMCD"), t.toString();
1037
+ } catch {
1038
+ return e;
1039
+ }
1040
+ }
1041
+ var qe = class {
1042
+ cmcd;
1043
+ entries = /* @__PURE__ */ new Map();
1044
+ keyed = /* @__PURE__ */ new Set();
1045
+ perTrack = /* @__PURE__ */ new Map();
1046
+ stats = {
1047
+ started: 0,
1048
+ served: 0,
1049
+ missed: 0,
1050
+ failed: 0,
1051
+ expired: 0
1052
+ };
1053
+ constructor(e = {}) {
1054
+ this.cmcd = e;
1055
+ }
1056
+ keyUsable(e) {
1057
+ this.keyed.add(e);
1058
+ }
1059
+ isActive(e) {
1060
+ return !this.keyed.has(e);
1061
+ }
1062
+ prefetch(e, t, n) {
1063
+ if (!this.isActive(t)) return;
1064
+ let r = Ke(e);
1065
+ if (this.entries.has(r)) return;
1066
+ let i = this.perTrack.get(t) ?? 0;
1067
+ if (i >= Ge) return;
1068
+ this.perTrack.set(t, i + 1);
1069
+ let a = new AbortController(), o = {
1070
+ started: performance.now(),
1071
+ first: 0,
1072
+ bytes: Promise.resolve(null),
1073
+ abort: a
1074
+ };
1075
+ o.bytes = fetch(this.withCmcd(e, t, n), {
1076
+ credentials: "omit",
1077
+ mode: "cors",
1078
+ signal: a.signal
1079
+ }).then(async (e) => (o.first = performance.now(), e.ok ? e.arrayBuffer() : null)).catch(() => null), this.entries.set(r, o), this.stats.started += 1, setTimeout(() => {
1080
+ this.entries.get(r) === o && (this.entries.delete(r), a.abort(), this.stats.expired += 1);
1081
+ }, Ue);
1082
+ }
1083
+ take(e) {
1084
+ let t = Ke(e), n = this.entries.get(t);
1085
+ return n && this.entries.delete(t), n;
1086
+ }
1087
+ destroy() {
1088
+ for (let e of this.entries.values()) e.abort.abort();
1089
+ this.entries.clear();
1090
+ }
1091
+ withCmcd(e, t, n) {
1092
+ let { sessionId: r, contentId: i } = this.cmcd;
1093
+ if (!r && !i) return e;
1094
+ let a = [];
1095
+ i && a.push(`cid="${i}"`), a.push(`ot=${n === "init" ? "i" : t === "audio" ? "a" : "v"}`), a.push("sf=h"), r && a.push(`sid="${r}"`), a.push("st=ll");
1096
+ try {
1097
+ let t = new URL(e);
1098
+ return t.searchParams.set("CMCD", a.join(",")), t.toString();
1099
+ } catch {
1100
+ return e;
1101
+ }
1102
+ }
1103
+ };
1104
+ function Je(t) {
1105
+ let n = e.DefaultConfig?.loader;
1106
+ if (typeof n != "function") return;
1107
+ class r extends n {
1108
+ served = !1;
1109
+ cancelled = !1;
1110
+ stats = Ye();
1111
+ load(e, n, r) {
1112
+ let i = e.rangeEnd ? void 0 : t.take(e.url);
1113
+ if (!i) {
1114
+ super.load(e, n, r);
1115
+ return;
1116
+ }
1117
+ this.served = !0;
1118
+ let a = performance.now(), o = n?.loadPolicy?.maxTimeToFirstByteMs || We, s = setTimeout(() => {
1119
+ !this.cancelled && this.served && (i.abort.abort(), t.stats.failed += 1, this.served = !1, super.load(e, n, r));
1120
+ }, o);
1121
+ i.bytes.then((o) => {
1122
+ if (clearTimeout(s), this.cancelled || !this.served) return;
1123
+ if (!o) {
1124
+ t.stats.failed += 1, this.served = !1, super.load(e, n, r);
1125
+ return;
1126
+ }
1127
+ t.stats.served += 1;
1128
+ let c = this.stats;
1129
+ c.loading.start = a, c.loading.first = Math.max(a, i.first || a), c.loading.end = performance.now(), c.loaded = c.total = o.byteLength, r.onSuccess({
1130
+ url: e.url,
1131
+ data: o
1132
+ }, c, e, null);
1133
+ });
1134
+ }
1135
+ abort() {
1136
+ if (this.served) {
1137
+ this.cancelled = !0, this.stats.aborted = !0;
1138
+ return;
1139
+ }
1140
+ super.abort();
1141
+ }
1142
+ }
1143
+ return r;
1144
+ }
1145
+ function Ye() {
1146
+ return {
1147
+ aborted: !1,
1148
+ loaded: 0,
1149
+ retry: 0,
1150
+ total: 0,
1151
+ chunkCount: 0,
1152
+ bwEstimate: 0,
1153
+ loading: {
1154
+ start: 0,
1155
+ first: 0,
1156
+ end: 0
1157
+ },
1158
+ parsing: {
1159
+ start: 0,
1160
+ end: 0
1161
+ },
1162
+ buffering: {
1163
+ start: 0,
1164
+ first: 0,
1165
+ end: 0
1166
+ }
1167
+ };
1168
+ }
1169
+ function Xe(e) {
1170
+ return e === "audio" ? "audio" : "main";
1171
+ }
1172
+ function Ze(e, t) {
1173
+ let n = [];
1174
+ t.initSegment?.url && !t.initSegment.data && n.push({
1175
+ url: t.initSegment.url,
1176
+ kind: "init"
1177
+ });
1178
+ let r = ((t.type === "audio" ? e.audioTracks?.[e.audioTrack]?.details : typeof t.level == "number" ? e.levels?.[t.level]?.details : void 0)?.partList ?? []).filter((e) => e.fragment.sn === t.sn);
1179
+ if (r.length) for (let e of r) e.url && n.push({
1180
+ url: e.url,
1181
+ kind: "media"
1182
+ });
1183
+ else t.url && n.push({
1184
+ url: t.url,
1185
+ kind: "media"
1186
+ });
1187
+ return n;
1188
+ }
1189
+ //#endregion
934
1190
  //#region src/core/engine/hls.ts
935
- function Re() {
1191
+ function Qe() {
936
1192
  return e.isSupported();
937
1193
  }
938
- var ze = 3, Be = 5, Ve = 3, He = 3e4, Ue = 12e3, We = 3e3, U = 6e3, Ge = 5e3, Ke = 4e3, qe = 2, Je = 5, Ye = 2, Xe = 2, Ze = 2e3, Qe = 3e4, $e = 2, et = 9, tt = .5, nt = 64;
939
- function rt(e) {
1194
+ var $e = 3, et = 5, tt = 3, nt = 3e4, rt = 12e3, it = 3e3, at = 6e3, ot = 5e3, st = 4e3, ct = 2, lt = 5, ut = 2, dt = 2, ft = 4, pt = 4, mt = 2e3, ht = 3e4, gt = 2, _t = 9, vt = .5, yt = 64;
1195
+ function bt(e) {
940
1196
  if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
941
1197
  delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
942
1198
  return;
943
1199
  }
944
- e.liveSyncDuration ??= $e;
945
- let t = Math.max(12, e.liveSyncDuration + et);
946
- (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), it(e, e.liveSyncDuration);
1200
+ e.liveSyncDuration ??= gt;
1201
+ let t = Math.max(12, e.liveSyncDuration + _t);
1202
+ (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), xt(e, e.liveSyncDuration);
947
1203
  }
948
- function it(e, t) {
1204
+ function xt(e, t) {
949
1205
  let n = (e, t, n) => Math.min(n, Math.max(t, e));
950
1206
  e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
951
1207
  }
952
- var W = class {
1208
+ var St = class {
953
1209
  name = "hls";
954
1210
  hls = null;
955
1211
  deps;
956
1212
  licenseUrl;
957
1213
  detectedLowLatency = !1;
958
1214
  bytesLoaded = 0;
1215
+ prefetch = null;
1216
+ reportedFirstPlaylist = !1;
1217
+ reportedFirstBytes = !1;
1218
+ reportedDrmReady = !1;
959
1219
  playlistBytesLoaded = 0;
960
1220
  renditionSwitches = 0;
961
1221
  licenseTimeMs = 0;
@@ -993,6 +1253,11 @@ var W = class {
993
1253
  kickPending = !1;
994
1254
  kickTicks = 0;
995
1255
  starvationRebuilds = 0;
1256
+ kickKind = null;
1257
+ starvedFrames = 0;
1258
+ deadTicks = 0;
1259
+ mainFragsSinceTick = 0;
1260
+ recentMainFrags = [];
996
1261
  chaseSuspended = !1;
997
1262
  savedLiveMaxLatency;
998
1263
  savedMaxSyncRate;
@@ -1008,7 +1273,7 @@ var W = class {
1008
1273
  this.deps = e, this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl, this.loadStarted = e.deferLoad !== !0;
1009
1274
  }
1010
1275
  async load() {
1011
- let { descriptor: e, snapToLiveOnRefocus: t, video: r, emit: i } = this.deps, a = B(r) && V(), o = !!e.drm?.widevine?.licenseUrl && !a;
1276
+ let { descriptor: e, snapToLiveOnRefocus: t, video: r, emit: i } = this.deps, a = q(r) && J(), o = !!e.drm?.widevine?.licenseUrl && !a;
1012
1277
  if (e.drmEnabled && !o) {
1013
1278
  i({
1014
1279
  type: "error",
@@ -1017,7 +1282,7 @@ var W = class {
1017
1282
  });
1018
1283
  return;
1019
1284
  }
1020
- this.startHls(), t !== !1 && typeof document < "u" && (this.onVisibility = () => {
1285
+ e.drmEnabled && o && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && (Te(r), ke()), this.startHls(), t !== !1 && typeof document < "u" && (this.onVisibility = () => {
1021
1286
  let e = this.hls;
1022
1287
  if (document.visibilityState !== "visible" || !e) return;
1023
1288
  let t = e.liveSyncPosition;
@@ -1040,11 +1305,11 @@ var W = class {
1040
1305
  }
1041
1306
  this.loadSuspended = !0;
1042
1307
  }
1043
- }, Ge));
1308
+ }, ot));
1044
1309
  }
1045
1310
  resumeLoading() {
1046
1311
  if (this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.loadSuspended && (this.loadSuspended = !1, !this.stopped && this.hls)) {
1047
- this.lastAdvanceMs = Date.now(), this.lastFragLoadingMs = 0, this.starvedTicks = 0, this.kickPending = !1, this.kickTicks = 0;
1312
+ this.lastAdvanceMs = Date.now(), this.lastFragLoadingMs = 0, this.resetStarvationWindow(), this.clearKick();
1048
1313
  try {
1049
1314
  this.hls.startLoad(-1);
1050
1315
  } catch {}
@@ -1072,11 +1337,11 @@ var W = class {
1072
1337
  let t = e.liveSyncPosition;
1073
1338
  if (typeof t != "number" || !Number.isFinite(t)) return;
1074
1339
  let { video: n } = this.deps;
1075
- n.currentTime <= t + tt || (n.currentTime = t);
1340
+ n.currentTime <= t + vt || (n.currentTime = t);
1076
1341
  }
1077
1342
  startHls() {
1078
1343
  let { video: t, descriptor: n, credential: r, deviceId: i, userId: a, lowLatency: o, debug: s, hlsConfig: c, emit: l } = this.deps;
1079
- this.kickPending = !1, this.kickTicks = 0, this.starvedTicks = 0, this.lastFragLoadingMs = 0, this.lastEndSN = -1, this.lastAdvanceMs = 0;
1344
+ this.clearKick(), this.resetStarvationWindow(), this.lastFragLoadingMs = 0, this.lastEndSN = -1, this.lastAdvanceMs = 0;
1080
1345
  let u = n.drmEnabled && !!(n.drm?.widevine?.licenseUrl || n.drm?.fairplay?.licenseUrl), d = {
1081
1346
  debug: s === !0,
1082
1347
  enableWorker: !o,
@@ -1091,17 +1356,17 @@ var W = class {
1091
1356
  emeEnabled: !0,
1092
1357
  drmSystems: { ...this.licenseUrl ? { "com.widevine.alpha": { licenseUrl: this.licenseUrl } } : {} },
1093
1358
  drmSystemOptions: {
1094
- videoRobustness: "SW_SECURE_DECODE",
1095
- audioRobustness: "SW_SECURE_CRYPTO",
1096
- videoEncryptionScheme: "cbcs",
1097
- audioEncryptionScheme: "cbcs"
1359
+ videoRobustness: I.video,
1360
+ audioRobustness: I.audio,
1361
+ videoEncryptionScheme: I.scheme,
1362
+ audioEncryptionScheme: I.scheme
1098
1363
  },
1099
- ...M() ? { requestMediaKeySystemAccessFunc: we(t) } : {},
1364
+ ...N() ? { requestMediaKeySystemAccessFunc: we(t) } : {},
1100
1365
  licenseXhrSetup: (e, t) => {
1101
1366
  e.open("POST", this.licenseUrl ?? t, !0), e.setRequestHeader("x-api-key", r.apiKey), e.setRequestHeader("X-Match-Urn", n.matchUrn), e.setRequestHeader("X-Device-Id", i), a && e.setRequestHeader("X-User-Id", a), this.licenseRequested = !0;
1102
1367
  let o = performance.now();
1103
1368
  e.addEventListener("loadend", () => {
1104
- this.licenseTimeMs = Math.max(1, Math.round(performance.now() - o));
1369
+ this.licenseTimeMs === 0 && (this.licenseTimeMs = Math.max(1, Math.round(performance.now() - o)));
1105
1370
  });
1106
1371
  }
1107
1372
  } : {},
@@ -1112,39 +1377,54 @@ var W = class {
1112
1377
  } } : {},
1113
1378
  ...c
1114
1379
  };
1115
- rt(d), this.sizeCapManaged = d.capLevelToPlayerSize !== !0, this.loadStarted = d.autoStartLoad !== !1;
1380
+ if (this.prefetch?.destroy(), this.prefetch = null, u && this.deps.mediaDuringKey !== !1 && !d.fLoader) {
1381
+ let e = new qe({
1382
+ sessionId: n.analytics?.sid,
1383
+ contentId: n.matchUrn
1384
+ }), t = Je(e);
1385
+ t && (this.prefetch = e, d.fLoader = t);
1386
+ }
1387
+ bt(d), this.sizeCapManaged = d.capLevelToPlayerSize !== !0, this.loadStarted = d.autoStartLoad !== !1;
1116
1388
  let f = new e(d);
1117
- this.hls = f, f.on(e.Events.MANIFEST_PARSED, () => {
1118
- this.mediaRecoveries = 0, this.networkRecoveries = 0;
1119
- let e = f.levels.map((e) => e.height).filter((e) => !!e);
1120
- this.ladderHeights = e.length ? [...new Set(e)].sort((e, t) => e - t) : void 0, this.ladderTopBitrateKbps = f.levels.length ? Math.round(Math.max(...f.levels.map((e) => e.bitrate)) / 1e3) : 0, this.applySizeCap(), this.loadSuspended = !1, t.paused && this.schedulePauseSuspend(), l({ type: "ready" });
1389
+ if (this.hls = f, f.on(e.Events.MANIFEST_PARSED, () => {
1390
+ this.mediaRecoveries = 0, this.networkRecoveries = 0, u && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && this.dropLevelsWidevineCannotPlay(f), this.measureLadder(f), this.applySizeCap(), this.loadSuspended = !1, t.paused && this.schedulePauseSuspend(), l({ type: "ready" });
1121
1391
  }), f.on(e.Events.FRAG_BUFFERED, (e, t) => {
1122
- t?.frag?.type === "main" && (this.kickPending = !1, this.kickTicks = 0, (this.mediaRecoveries > 0 || this.networkRecoveries > 0 || this.licenseRecoveries > 0 || this.starvationKicks > 0 || this.starvationRebuilds > 0) && !this.recoveryResetTimer && (this.recoveryResetTimer = setTimeout(() => {
1392
+ t?.frag?.type === "main" && (this.kickKind !== "dead" && this.clearKick(), (this.mediaRecoveries > 0 || this.networkRecoveries > 0 || this.licenseRecoveries > 0 || this.starvationKicks > 0 || this.starvationRebuilds > 0) && !this.recoveryResetTimer && (this.recoveryResetTimer = setTimeout(() => {
1123
1393
  this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.starvationRebuilds = 0, this.recoveryResetTimer = null;
1124
- }, He)));
1394
+ }, nt)));
1125
1395
  }), f.on(e.Events.FRAG_LOADING, (e, t) => {
1126
1396
  t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
1127
1397
  }), f.on(e.Events.FRAG_LOADED, (e, t) => {
1398
+ t?.frag?.type === "main" && (this.mainFragsSinceTick += 1);
1128
1399
  let n = t?.frag?.stats, r = n?.loaded;
1129
- typeof r == "number" && (this.bytesLoaded += r);
1400
+ typeof r == "number" && (this.bytesLoaded += r), !this.reportedFirstBytes && typeof r == "number" && r > 0 && (this.reportedFirstBytes = !0, l({ type: "firstBytes" }));
1130
1401
  let i = n?.loading;
1131
- i && i.end > 0 && i.start > 0 && (this.requestCount += 1, this.requestTimesMs.push(Math.max(0, Math.round(i.end - i.start))), this.requestTimesMs.length > nt && this.requestTimesMs.shift());
1402
+ i && i.end > 0 && i.start > 0 && (this.requestCount += 1, this.requestTimesMs.push(Math.max(0, Math.round(i.end - i.start))), this.requestTimesMs.length > yt && this.requestTimesMs.shift());
1132
1403
  let a = t?.frag?.url;
1133
1404
  if (a) {
1134
- let e = R(a);
1405
+ let e = G(a);
1135
1406
  e && (this.cdnHost = e);
1136
1407
  }
1137
1408
  }), f.on(e.Events.LEVEL_LOADED, (e, t) => {
1138
1409
  let n = t?.stats?.loaded;
1139
- if (typeof n == "number" && (this.playlistBytesLoaded += n), this.detectedLowLatency) return;
1410
+ if (typeof n == "number" && (this.playlistBytesLoaded += n), this.reportedFirstPlaylist || (this.reportedFirstPlaylist = !0, l({ type: "firstPlaylist" })), this.detectedLowLatency) return;
1140
1411
  let r = t.details;
1141
1412
  (r?.partList?.length || (r?.partTarget ?? 0) > 0) && (this.detectedLowLatency = !0, l({
1142
1413
  type: "lowLatencyDetected",
1143
1414
  value: !0
1144
1415
  }));
1145
- }), f.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.endWatchdog && clearInterval(this.endWatchdog), this.endWatchdog = setInterval(() => {
1416
+ }), f.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.prefetch) {
1417
+ let t = this.prefetch;
1418
+ f.on(e.Events.KEY_LOADING, (e, n) => {
1419
+ let r = n?.frag, i = Xe(r?.type);
1420
+ if (r && t.isActive(i)) for (let e of Ze(f, r)) t.prefetch(e.url, i, e.kind);
1421
+ }), f.on(e.Events.KEY_LOADED, (e, n) => t.keyUsable(Xe(n?.frag?.type)));
1422
+ }
1423
+ f.on(e.Events.KEY_LOADED, () => {
1424
+ u && !this.reportedDrmReady && (this.reportedDrmReady = !0, l({ type: "drmReady" }));
1425
+ }), this.endWatchdog && clearInterval(this.endWatchdog), this.endWatchdog = setInterval(() => {
1146
1426
  this.checkEndStale(), this.checkStarvation();
1147
- }, We), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1427
+ }, it), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
1148
1428
  this.renditionSwitches += 1;
1149
1429
  let n = this.lastLevelIndex >= 0 ? f.levels[this.lastLevelIndex] : void 0, r = f.levels[t.level];
1150
1430
  n && r && r.bitrate !== n.bitrate && (r.bitrate > n.bitrate ? this.upshifts += 1 : this.downshifts += 1), this.lastLevelIndex = t.level, l({
@@ -1208,11 +1488,33 @@ var W = class {
1208
1488
  return;
1209
1489
  }
1210
1490
  this.capTimer && clearTimeout(this.capTimer);
1211
- let t = e > this.sizeCapLevel ? Ze : Qe;
1491
+ let t = e > this.sizeCapLevel ? mt : ht;
1212
1492
  this.capTimer = setTimeout(() => {
1213
1493
  this.capTimer = null, this.computeSizeCapTarget() === e ? this.applySizeCap() : this.onMediaResize();
1214
1494
  }, t);
1215
1495
  }
1496
+ measureLadder(e) {
1497
+ let t = e.levels.map((e) => e.height).filter((e) => !!e);
1498
+ this.ladderHeights = t.length ? [...new Set(t)].sort((e, t) => e - t) : void 0, this.ladderTopBitrateKbps = e.levels.length ? Math.round(Math.max(...e.levels.map((e) => e.bitrate)) / 1e3) : 0;
1499
+ }
1500
+ dropLevelsWidevineCannotPlay(e) {
1501
+ this.removeLevelsWhere(e, (e) => Ae(e) !== !0);
1502
+ let t = new Set(e.levels.map((e) => e.videoCodec).filter((e) => !!e && L(e) !== "avc" && !!L(e)));
1503
+ for (let n of t) Oe(n).then((t) => {
1504
+ t || this.hls !== e || this.removeLevelsWhere(e, (e) => e === n) && (this.measureLadder(e), this.applySizeCap());
1505
+ });
1506
+ }
1507
+ removeLevelsWhere(e, t) {
1508
+ let n = e.levels ?? [], r = [], i = /* @__PURE__ */ new Set();
1509
+ if (n.forEach((e, n) => {
1510
+ let a = e.videoCodec;
1511
+ a && L(a) && t(a) && (r.push(n), i.add(a));
1512
+ }), !r.length) return !1;
1513
+ let a = [...i].join(", ");
1514
+ if (!n.some((e, t) => r.includes(t) ? !1 : !L(e.videoCodec) || Ae(e.videoCodec) === !0)) return console.warn(`havik-player: every rendition is ${a}, which Widevine may not play on this browser; keeping them`), !1;
1515
+ for (let t = r.length - 1; t >= 0; --t) e.removeLevel(r[t]);
1516
+ return console.info(`havik-player: skipped ${r.length} rendition(s) Widevine cannot play on this browser (${a})`), !0;
1517
+ }
1216
1518
  applySizeCap() {
1217
1519
  if (!this.sizeCapManaged) return;
1218
1520
  this.capTimer &&= (clearTimeout(this.capTimer), null);
@@ -1255,7 +1557,7 @@ var W = class {
1255
1557
  e.widevine && (this.licenseUrl = e.widevine);
1256
1558
  }
1257
1559
  getStats() {
1258
- let e = this.hls, t = z(this.deps.video), n = {
1560
+ let e = this.hls, t = K(this.deps.video), n = {
1259
1561
  droppedFrames: t.dropped,
1260
1562
  decodedFrames: t.totalFrames,
1261
1563
  lowLatency: this.detectedLowLatency,
@@ -1301,32 +1603,41 @@ var W = class {
1301
1603
  return;
1302
1604
  }
1303
1605
  let e = Date.now() - this.lastAdvanceMs;
1304
- !this.chaseSuspended && e > U && this.suspendChasing(), e > Ue && this.emitEnded("stalled");
1606
+ !this.chaseSuspended && e > at && this.suspendChasing(), e > rt && this.emitEnded("stalled");
1607
+ }
1608
+ resetStarvationWindow() {
1609
+ this.starvedTicks = 0, this.deadTicks = 0, this.mainFragsSinceTick = 0, this.recentMainFrags = [];
1610
+ }
1611
+ clearKick() {
1612
+ this.kickPending = !1, this.kickTicks = 0, this.kickKind = null;
1305
1613
  }
1306
1614
  checkStarvation() {
1307
1615
  let e = this.hls, { video: t } = this.deps;
1308
1616
  if (!e || this.endedEmitted || !this.sawLive || this.chaseSuspended) return;
1309
1617
  if (typeof document < "u" && document.hidden || this.loadSuspended) {
1310
- this.starvedTicks = 0;
1618
+ this.resetStarvationWindow();
1311
1619
  return;
1312
1620
  }
1313
- let n = this.lastAdvanceMs !== 0 && Date.now() - this.lastAdvanceMs < U, r = this.lastFragLoadingMs !== 0 && Date.now() - this.lastFragLoadingMs > Ke;
1314
- if (!(t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused && n && r)) {
1315
- this.starvedTicks = 0;
1621
+ let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < at, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > st, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = K(t), c = o - s, l = c !== this.starvedFrames;
1622
+ if (this.starvedFrames = c, !a || l) {
1623
+ (t.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA || l) && this.clearKick(), this.resetStarvationWindow();
1316
1624
  return;
1317
1625
  }
1318
- if (this.kickPending && (this.kickTicks += 1), !(++this.starvedTicks < qe)) {
1319
- if (this.starvedTicks = 0, this.kickPending && this.kickTicks >= Ye && (this.kickPending = !1, this.kickTicks = 0, this.starvationRebuilds < Xe)) {
1320
- this.starvationRebuilds += 1, console.warn("havik-player: restarting the load did not resume video (the wedge is in hls.js state that only a rebuild clears) — rebuilding the player"), this.rebuildInstance("starvation rebuild");
1321
- return;
1322
- }
1323
- if (!(this.starvationKicks >= Je)) {
1324
- this.starvationKicks += 1, this.kickPending = !0, this.kickTicks = 0, console.warn("havik-player: video starved while the live playlist advances (wedged hls.js fragment pipeline) — restarting the load");
1325
- try {
1326
- e.stopLoad(), e.startLoad(-1);
1327
- } catch (e) {
1328
- console.warn("havik-player: starvation kick failed", e);
1329
- }
1626
+ if (!r) {
1627
+ this.resetStarvationWindow();
1628
+ return;
1629
+ }
1630
+ if (this.kickPending && (this.kickKind === "dead" || i) && (this.kickTicks += 1), this.starvedTicks = i ? this.starvedTicks + 1 : 0, this.deadTicks += 1, this.recentMainFrags.push(this.mainFragsSinceTick), this.mainFragsSinceTick = 0, this.recentMainFrags.length > ft && this.recentMainFrags.shift(), this.clearRecoveryReset(), this.kickPending && this.kickTicks >= ut && (this.clearKick(), this.starvationRebuilds < dt)) {
1631
+ this.starvationRebuilds += 1, this.resetStarvationWindow(), this.clearRecoveryReset(), console.warn("havik-player: restarting the load did not resume video (the wedge is in hls.js state that only a rebuild clears) — rebuilding the player"), this.rebuildInstance("starvation rebuild");
1632
+ return;
1633
+ }
1634
+ let u = i && this.starvedTicks >= ct, d = !i && this.deadTicks >= ft && this.recentMainFrags.reduce((e, t) => e + t, 0) >= pt;
1635
+ if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >= lt))) {
1636
+ this.starvationKicks += 1, this.kickPending = !0, this.kickTicks = 0, this.kickKind = d ? "dead" : "quiet", this.clearRecoveryReset(), console.warn(d ? "havik-player: video starved while hls.js keeps loading media it never plays (dead pipeline at the live edge) — restarting the load" : "havik-player: video starved while the live playlist advances (wedged hls.js fragment pipeline) — restarting the load");
1637
+ try {
1638
+ e.stopLoad(), e.startLoad(-1);
1639
+ } catch (e) {
1640
+ console.warn("havik-player: starvation kick failed", e);
1330
1641
  }
1331
1642
  }
1332
1643
  }
@@ -1346,7 +1657,7 @@ var W = class {
1346
1657
  }));
1347
1658
  }
1348
1659
  stop() {
1349
- this.endWatchdog &&= (clearInterval(this.endWatchdog), null), this.kickPending = !1, this.kickTicks = 0, this.stopped = !0, this.loadSuspended = !1, this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null);
1660
+ this.endWatchdog &&= (clearInterval(this.endWatchdog), null), this.clearKick(), this.stopped = !0, this.loadSuspended = !1, this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null);
1350
1661
  try {
1351
1662
  this.hls?.stopLoad();
1352
1663
  } catch {}
@@ -1355,7 +1666,7 @@ var W = class {
1355
1666
  } catch {}
1356
1667
  }
1357
1668
  destroy() {
1358
- if (this.clearRecoveryReset(), this.capTimer &&= (clearTimeout(this.capTimer), null), this.capObserver &&= (this.capObserver.disconnect(), null), this.unwatchDprChanges(), this.endWatchdog &&= (clearInterval(this.endWatchdog), null), this.onVisibility && typeof document < "u" && (document.removeEventListener("visibilitychange", this.onVisibility), this.onVisibility = null), this.onSeeking &&= (this.deps.video.removeEventListener("seeking", this.onSeeking), null), this.onPause &&= (this.deps.video.removeEventListener("pause", this.onPause), null), this.onPlay &&= (this.deps.video.removeEventListener("play", this.onPlay), null), this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.networkRetryTimer &&= (clearTimeout(this.networkRetryTimer), null), this.licenseRetryTimer &&= (clearTimeout(this.licenseRetryTimer), null), this.hls) {
1669
+ if (this.prefetch?.destroy(), this.prefetch = null, this.clearRecoveryReset(), this.capTimer &&= (clearTimeout(this.capTimer), null), this.capObserver &&= (this.capObserver.disconnect(), null), this.unwatchDprChanges(), this.endWatchdog &&= (clearInterval(this.endWatchdog), null), this.onVisibility && typeof document < "u" && (document.removeEventListener("visibilitychange", this.onVisibility), this.onVisibility = null), this.onSeeking &&= (this.deps.video.removeEventListener("seeking", this.onSeeking), null), this.onPause &&= (this.deps.video.removeEventListener("pause", this.onPause), null), this.onPlay &&= (this.deps.video.removeEventListener("play", this.onPlay), null), this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.networkRetryTimer &&= (clearTimeout(this.networkRetryTimer), null), this.licenseRetryTimer &&= (clearTimeout(this.licenseRetryTimer), null), this.hls) {
1359
1670
  try {
1360
1671
  this.hls.destroy();
1361
1672
  } catch (e) {
@@ -1370,7 +1681,7 @@ var W = class {
1370
1681
  scheduleLicenseRetry(t) {
1371
1682
  if (t.details !== e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED && t.details !== e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED) return !1;
1372
1683
  let n = t.response?.code ?? 0;
1373
- if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Ve) return !1;
1684
+ if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= tt) return !1;
1374
1685
  this.licenseRecoveries += 1;
1375
1686
  let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
1376
1687
  return this.licenseRetryTimer = setTimeout(() => {
@@ -1402,7 +1713,7 @@ var W = class {
1402
1713
  return;
1403
1714
  }
1404
1715
  if (t.type === e.ErrorTypes.NETWORK_ERROR) {
1405
- if (this.networkRecoveries < Be) {
1716
+ if (this.networkRecoveries < et) {
1406
1717
  this.networkRecoveries += 1;
1407
1718
  let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
1408
1719
  this.networkRetryTimer = setTimeout(() => {
@@ -1419,7 +1730,7 @@ var W = class {
1419
1730
  return;
1420
1731
  }
1421
1732
  if (t.type === e.ErrorTypes.MEDIA_ERROR) {
1422
- if (this.mediaRecoveries < ze) {
1733
+ if (this.mediaRecoveries < $e) {
1423
1734
  this.mediaRecoveries += 1, n.recoverMediaError();
1424
1735
  return;
1425
1736
  }
@@ -1443,19 +1754,19 @@ var W = class {
1443
1754
  let r = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
1444
1755
  return new n(t === "DRM" ? i(r) : r === 401 ? "UNAUTHORIZED" : r === 403 ? "FORBIDDEN" : "INTERNAL", r, `${t} error: ${a}${o ? ` (${o})` : ""}`);
1445
1756
  }
1446
- }, at = 15e3, ot = 6e5, st = 3e5, ct = 100, G = 1, lt = 1e3, K = 3e4, ut = 1e3, dt = {
1757
+ }, Ct = 15e3, wt = 6e5, Tt = 3e5, Et = 100, X = 1, Dt = 1e3, Ot = 3e4, kt = 1e3, At = {
1447
1758
  inline: "PRESENTATION_INLINE",
1448
1759
  fullscreen: "PRESENTATION_FULLSCREEN",
1449
1760
  pip: "PRESENTATION_PIP"
1450
- }, ft = {
1761
+ }, jt = {
1451
1762
  widevine: "KEY_SYSTEM_WIDEVINE",
1452
1763
  fairplay: "KEY_SYSTEM_FAIRPLAY",
1453
1764
  none: "KEY_SYSTEM_NONE"
1454
1765
  };
1455
- function q(e, t) {
1766
+ function Mt(e, t) {
1456
1767
  return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
1457
1768
  }
1458
- var pt = /* @__PURE__ */ new Set([
1769
+ var Nt = /* @__PURE__ */ new Set([
1459
1770
  "INVALID_URN",
1460
1771
  "NOT_FOUND",
1461
1772
  "GONE",
@@ -1465,7 +1776,7 @@ var pt = /* @__PURE__ */ new Set([
1465
1776
  "FORBIDDEN",
1466
1777
  "RATE_LIMITED",
1467
1778
  "TIMEOUT"
1468
- ]), J = {
1779
+ ]), Z = {
1469
1780
  idle: "PLAYER_STATE_IDLE",
1470
1781
  waiting: "PLAYER_STATE_WAITING",
1471
1782
  loading: "PLAYER_STATE_LOADING",
@@ -1475,7 +1786,7 @@ var pt = /* @__PURE__ */ new Set([
1475
1786
  ended: "PLAYER_STATE_ENDED",
1476
1787
  error: "PLAYER_STATE_ERROR"
1477
1788
  };
1478
- function mt(e) {
1789
+ function Pt(e) {
1479
1790
  try {
1480
1791
  let t = new URL(e), [n, ...r] = t.hostname.split(".");
1481
1792
  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);
@@ -1483,14 +1794,14 @@ function mt(e) {
1483
1794
  return;
1484
1795
  }
1485
1796
  }
1486
- var Y = /* @__PURE__ */ new Set();
1487
- function X(e, t) {
1488
- return Y.has(e) || (Y.add(e), console.warn(`havik-player: ${t}`)), { disabledReason: t };
1797
+ var Ft = /* @__PURE__ */ new Set();
1798
+ function It(e, t) {
1799
+ return Ft.has(e) || (Ft.add(e), console.warn(`havik-player: ${t}`)), { disabledReason: t };
1489
1800
  }
1490
- function ht(e, t) {
1801
+ function Lt(e, t) {
1491
1802
  if (t) return { endpoint: t };
1492
- if (t !== void 0) return X("empty-endpoint", "analytics is disabled: no QoE beacons will be sent. analytics.endpoint was supplied but empty. Pass a real beacons origin, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, or omit the option to derive it from baseUrl (or analytics: false to opt out).");
1493
- let n = mt(e);
1803
+ if (t !== void 0) return It("empty-endpoint", "analytics is disabled: no QoE beacons will be sent. analytics.endpoint was supplied but empty. Pass a real beacons origin, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, or omit the option to derive it from baseUrl (or analytics: false to opt out).");
1804
+ let n = Pt(e);
1494
1805
  if (n) return { endpoint: n };
1495
1806
  let r;
1496
1807
  try {
@@ -1498,9 +1809,9 @@ function ht(e, t) {
1498
1809
  } catch {
1499
1810
  r = e;
1500
1811
  }
1501
- return X(r, `analytics is disabled: no QoE beacons will be sent. The beacons endpoint cannot be derived from the baseUrl host "${r}" — the convention is feed[-dev].<domain> → beacons[-dev].<domain>. Pass an explicit endpoint, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, to createPlayer (or analytics: false to opt out deliberately).`);
1812
+ return It(r, `analytics is disabled: no QoE beacons will be sent. The beacons endpoint cannot be derived from the baseUrl host "${r}" — the convention is feed[-dev].<domain> → beacons[-dev].<domain>. Pass an explicit endpoint, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, to createPlayer (or analytics: false to opt out deliberately).`);
1502
1813
  }
1503
- var gt = class {
1814
+ var Rt = class {
1504
1815
  opts;
1505
1816
  url;
1506
1817
  now;
@@ -1567,6 +1878,8 @@ var gt = class {
1567
1878
  startupTimeMs = 0;
1568
1879
  manifestLoadMs = 0;
1569
1880
  firstFragLoadMs = 0;
1881
+ firstPlaylistMs = 0;
1882
+ drmSetupMs = 0;
1570
1883
  onPagehide = () => this.end("END_REASON_PAGEHIDE");
1571
1884
  onFreeze = () => {
1572
1885
  this.endedFlag || this.flush(!0);
@@ -1578,8 +1891,8 @@ var gt = class {
1578
1891
  constructor(e) {
1579
1892
  this.opts = {
1580
1893
  ...e,
1581
- heartbeatMs: e.heartbeatMs ?? at,
1582
- idleMs: e.idleMs ?? ot
1894
+ heartbeatMs: e.heartbeatMs ?? Ct,
1895
+ idleMs: e.idleMs ?? wt
1583
1896
  }, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`, this.now = e.now ?? (() => Date.now()), this.bornMs = this.now(), this.presentationSinceMs = this.bornMs, this.startupAnchorMs = this.bornMs;
1584
1897
  }
1585
1898
  start() {
@@ -1589,11 +1902,11 @@ var gt = class {
1589
1902
  sdkVersion: this.opts.sdkVersion,
1590
1903
  ...this.opts.environment
1591
1904
  };
1592
- this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), lt), this.lastTickMs = this.now(), this.timer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs), typeof window < "u" && window.addEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.addEventListener("visibilitychange", this.onVisibility), document.addEventListener("freeze", this.onFreeze), document.addEventListener("resume", this.onResume), this.onVisibility());
1905
+ this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), Dt), this.lastTickMs = this.now(), this.timer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs), typeof window < "u" && window.addEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.addEventListener("visibilitychange", this.onVisibility), document.addEventListener("freeze", this.onFreeze), document.addEventListener("resume", this.onResume), this.onVisibility());
1593
1906
  }
1594
1907
  checkSuspension(e) {
1595
1908
  if (this.endedFlag || !this.opts.idleMs) return !1;
1596
- let t = Math.max(this.opts.idleMs, st);
1909
+ let t = Math.max(this.opts.idleMs, Tt);
1597
1910
  return this.lastTickMs > 0 && e - this.lastTickMs >= t ? (this.endIdle(this.lastTickMs), !0) : (this.lastTickMs = e, !1);
1598
1911
  }
1599
1912
  syncIdle(e) {
@@ -1607,21 +1920,21 @@ var gt = class {
1607
1920
  noteStateChange(e, t, n, r) {
1608
1921
  if (this.endedFlag || e === t) return;
1609
1922
  let i = this.now();
1610
- if (t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(G, i - this.startupAnchorMs)), this.firstAudioMs === 0 && !this.isMuted && (this.firstAudioMs = Math.max(G, i - this.startupAnchorMs)), this.isMuted && this.mutedSinceMs === null && (this.mutedSinceMs = i)) : this.playingSinceMs !== null && (this.playingTotalMs += i - this.playingSinceMs, this.playingSinceMs = null, this.mutedSinceMs !== null && (this.mutedTotalMs += i - this.mutedSinceMs, this.mutedSinceMs = null)), this.isPlaying = t === "playing", this.syncActive(), t === "waiting" ? this.waitingSinceMs = i : this.waitingSinceMs !== null && (this.waitingTotalMs += i - this.waitingSinceMs, this.waitingSinceMs = null), t === "buffering" && e === "playing") {
1923
+ if (t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(X, i - this.startupAnchorMs)), this.firstAudioMs === 0 && !this.isMuted && (this.firstAudioMs = Math.max(X, i - this.startupAnchorMs)), this.isMuted && this.mutedSinceMs === null && (this.mutedSinceMs = i)) : this.playingSinceMs !== null && (this.playingTotalMs += i - this.playingSinceMs, this.playingSinceMs = null, this.mutedSinceMs !== null && (this.mutedTotalMs += i - this.mutedSinceMs, this.mutedSinceMs = null)), this.isPlaying = t === "playing", this.syncActive(), t === "waiting" ? this.waitingSinceMs = i : this.waitingSinceMs !== null && (this.waitingTotalMs += i - this.waitingSinceMs, this.waitingSinceMs = null), t === "buffering" && e === "playing") {
1611
1924
  this.stallIsSeekInduced = this.seekInduced(i);
1612
1925
  let e = !this.isHidden && !this.stallIsSeekInduced;
1613
- e && (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, e && this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(G, Math.round(this.playingTotalMs)));
1926
+ e && (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, e && this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(X, Math.round(this.playingTotalMs)));
1614
1927
  } else e === "buffering" && (this.isBuffering = !1, this.stallIsSeekInduced = !1);
1615
1928
  this.syncBuffering(), t === "paused" && (this.pauseCount += 1), this.syncIdle(t);
1616
1929
  let a = {
1617
- fromState: J[e] ?? "PLAYER_STATE_UNSPECIFIED",
1618
- toState: J[t] ?? "PLAYER_STATE_UNSPECIFIED",
1619
- presentation: dt[this.presentation]
1930
+ fromState: Z[e] ?? "PLAYER_STATE_UNSPECIFIED",
1931
+ toState: Z[t] ?? "PLAYER_STATE_UNSPECIFIED",
1932
+ presentation: At[this.presentation]
1620
1933
  }, o = this.causeOf(e, t, i);
1621
1934
  o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
1622
1935
  }
1623
1936
  seekInduced(e) {
1624
- return this.lastSeekMs >= 0 && e - this.lastSeekMs < ut;
1937
+ return this.lastSeekMs >= 0 && e - this.lastSeekMs < kt;
1625
1938
  }
1626
1939
  causeOf(e, t, n) {
1627
1940
  if (t === "error") return "TRANSITION_CAUSE_ERROR";
@@ -1658,19 +1971,25 @@ var gt = class {
1658
1971
  }
1659
1972
  this.muteChanges += 1;
1660
1973
  let r = this.now();
1661
- n ? this.isPlaying && this.mutedSinceMs === null && (this.mutedSinceMs = r) : (this.mutedSinceMs !== null && (this.mutedTotalMs += r - this.mutedSinceMs, this.mutedSinceMs = null), this.firstAudioMs === 0 && this.isPlaying && (this.firstAudioMs = Math.max(G, r - this.startupAnchorMs))), this.isMuted = n, this.syncActive();
1974
+ n ? this.isPlaying && this.mutedSinceMs === null && (this.mutedSinceMs = r) : (this.mutedSinceMs !== null && (this.mutedTotalMs += r - this.mutedSinceMs, this.mutedSinceMs = null), this.firstAudioMs === 0 && this.isPlaying && (this.firstAudioMs = Math.max(X, r - this.startupAnchorMs))), this.isMuted = n, this.syncActive();
1662
1975
  }
1663
1976
  notePresentation(e) {
1664
1977
  this.endedFlag || e === this.presentation || (this.closePresentation(), this.presentation = e);
1665
1978
  }
1666
1979
  notePlayIntent() {
1667
- this.endedFlag || this.startupTimeMs > 0 || (this.startupAnchorMs = this.now(), this.manifestLoadMs > 0 && (this.manifestLoadMs = G), this.firstFragLoadMs > 0 && (this.firstFragLoadMs = G));
1980
+ this.endedFlag || this.startupTimeMs > 0 || (this.startupAnchorMs = this.now(), this.manifestLoadMs > 0 && (this.manifestLoadMs = X), this.firstPlaylistMs > 0 && (this.firstPlaylistMs = X), this.firstFragLoadMs > 0 && (this.firstFragLoadMs = X), this.drmSetupMs > 0 && (this.drmSetupMs = X));
1668
1981
  }
1669
1982
  noteReady() {
1670
- this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(G, this.now() - this.startupAnchorMs));
1983
+ this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(X, this.now() - this.startupAnchorMs));
1984
+ }
1985
+ noteFirstPlaylist() {
1986
+ this.firstPlaylistMs === 0 && (this.firstPlaylistMs = Math.max(X, this.now() - this.startupAnchorMs));
1987
+ }
1988
+ noteDrmReady() {
1989
+ this.drmSetupMs === 0 && (this.drmSetupMs = Math.max(X, this.now() - this.startupAnchorMs));
1671
1990
  }
1672
1991
  noteFirstBytes() {
1673
- this.sawFirstBytes = !0, this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(G, this.now() - this.startupAnchorMs));
1992
+ this.sawFirstBytes = !0, this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(X, this.now() - this.startupAnchorMs));
1674
1993
  }
1675
1994
  noteSeek() {
1676
1995
  this.endedFlag || (this.seekCount += 1, this.lastSeekMs = this.now());
@@ -1689,7 +2008,7 @@ var gt = class {
1689
2008
  let r = n ?? "";
1690
2009
  if (r !== "") return /^fragLoad/i.test(r) ? this.sawFirstBytes ? "ERROR_STAGE_SEGMENT" : "ERROR_STAGE_FIRST_SEGMENT" : /^(manifest|level|audioTrack|subtitleTrack)/i.test(r) ? "ERROR_STAGE_MANIFEST" : /key|licen[cs]e/i.test(r) ? "ERROR_STAGE_DRM" : /parsing|append|buffer|remux|decrypt|media/i.test(r) ? "ERROR_STAGE_DECODE" : "ERROR_STAGE_OTHER";
1691
2010
  if (e === "DRM_CLIENT") return "ERROR_STAGE_DRM";
1692
- if (pt.has(e)) return "ERROR_STAGE_RESOLVE";
2011
+ if (Nt.has(e)) return "ERROR_STAGE_RESOLVE";
1693
2012
  let i = t ?? "";
1694
2013
  return /frag|segment/i.test(i) ? this.sawFirstBytes ? "ERROR_STAGE_SEGMENT" : "ERROR_STAGE_FIRST_SEGMENT" : /manifest|level|playlist/i.test(i) ? "ERROR_STAGE_MANIFEST" : /key|licen[cs]e/i.test(i) ? "ERROR_STAGE_DRM" : /buffer|decode|media|append/i.test(i) ? "ERROR_STAGE_DECODE" : "ERROR_STAGE_OTHER";
1695
2014
  }
@@ -1721,7 +2040,7 @@ var gt = class {
1721
2040
  let l = t.droppedFrames ?? 0;
1722
2041
  l > this.lastDropped && (o.droppedFramesDelta = l - this.lastDropped), this.lastDropped = l;
1723
2042
  let u = t.renditionSwitches ?? 0;
1724
- u > this.lastSwitches && (o.renditionSwitchesDelta = u - this.lastSwitches), this.lastSwitches = u, n > this.lastHbPlayingMs && (o.playingMsDelta = Math.round(n - this.lastHbPlayingMs)), this.lastHbPlayingMs = n, r > this.lastHbHiddenMs && (o.hiddenMsDelta = Math.round(r - this.lastHbHiddenMs)), this.lastHbHiddenMs = r, i > this.lastHbRebufferMs && (o.rebufferMsDelta = Math.round(i - this.lastHbRebufferMs)), this.lastHbRebufferMs = i, this.rebufferCount > this.lastHbRebufferCount && (o.rebufferCountDelta = this.rebufferCount - this.lastHbRebufferCount), this.lastHbRebufferCount = this.rebufferCount, a > this.lastHbActiveMs && (o.activeMsDelta = Math.round(a - this.lastHbActiveMs)), this.lastHbActiveMs = a, typeof t.latencySeconds == "number" && (o.latencyMs = Math.round(t.latencySeconds * 1e3)), typeof t.targetLatencySeconds == "number" && (o.targetLatencyMs = Math.round(t.targetLatencySeconds * 1e3)), typeof t.bandwidthKbps == "number" && (o.bandwidthEstKbps = t.bandwidthKbps), typeof t.levelHeight == "number" && (o.renditionHeight = t.levelHeight), typeof t.renditionBitrateKbps == "number" && (o.renditionBitrateKbps = t.renditionBitrateKbps), t.atLiveEdge === !0 && (o.atLiveEdge = !0), typeof t.bufferedMs == "number" && t.bufferedMs > 0 && (o.bufferedMs = Math.round(t.bufferedMs)), o.playerState = J[t.state] ?? "PLAYER_STATE_UNSPECIFIED";
2043
+ u > this.lastSwitches && (o.renditionSwitchesDelta = u - this.lastSwitches), this.lastSwitches = u, n > this.lastHbPlayingMs && (o.playingMsDelta = Math.round(n - this.lastHbPlayingMs)), this.lastHbPlayingMs = n, r > this.lastHbHiddenMs && (o.hiddenMsDelta = Math.round(r - this.lastHbHiddenMs)), this.lastHbHiddenMs = r, i > this.lastHbRebufferMs && (o.rebufferMsDelta = Math.round(i - this.lastHbRebufferMs)), this.lastHbRebufferMs = i, this.rebufferCount > this.lastHbRebufferCount && (o.rebufferCountDelta = this.rebufferCount - this.lastHbRebufferCount), this.lastHbRebufferCount = this.rebufferCount, a > this.lastHbActiveMs && (o.activeMsDelta = Math.round(a - this.lastHbActiveMs)), this.lastHbActiveMs = a, typeof t.latencySeconds == "number" && (o.latencyMs = Math.round(t.latencySeconds * 1e3)), typeof t.targetLatencySeconds == "number" && (o.targetLatencyMs = Math.round(t.targetLatencySeconds * 1e3)), typeof t.bandwidthKbps == "number" && (o.bandwidthEstKbps = t.bandwidthKbps), typeof t.levelHeight == "number" && (o.renditionHeight = t.levelHeight), typeof t.renditionBitrateKbps == "number" && (o.renditionBitrateKbps = t.renditionBitrateKbps), t.atLiveEdge === !0 && (o.atLiveEdge = !0), typeof t.bufferedMs == "number" && t.bufferedMs > 0 && (o.bufferedMs = Math.round(t.bufferedMs)), o.playerState = Z[t.state] ?? "PLAYER_STATE_UNSPECIFIED";
1725
2044
  let d = this.mutedTotalMs;
1726
2045
  this.mutedSinceMs !== null && (d += e - this.mutedSinceMs), d > this.lastHbMutedMs && (o.mutedMsDelta = Math.round(d - this.lastHbMutedMs)), this.lastHbMutedMs = d;
1727
2046
  let f = t.upshifts ?? 0;
@@ -1736,14 +2055,14 @@ var gt = class {
1736
2055
  if (this.lastRequestCount = g, _ > 0) {
1737
2056
  o.requestCountDelta = _;
1738
2057
  let e = t.requestTimesMs ?? [], n = e.slice(Math.max(0, e.length - _)).sort((e, t) => e - t);
1739
- n.length > 0 && (o.requestTimeP50Ms = q(n, 50), o.requestTimeP95Ms = q(n, 95));
2058
+ n.length > 0 && (o.requestTimeP50Ms = Mt(n, 50), o.requestTimeP95Ms = Mt(n, 95));
1740
2059
  }
1741
2060
  typeof t.latencySeconds == "number" && typeof t.targetLatencySeconds == "number" && (o.driftMs = Math.round((t.latencySeconds - t.targetLatencySeconds) * 1e3)), typeof t.playbackRate == "number" && t.playbackRate > 0 && (o.playbackRate = t.playbackRate), t.qualityPinned === !0 && (o.qualityPinned = !0), t.cdnHost && (o.cdnHost = t.cdnHost), this.maybeEmitMediaInfo(t), this.enqueue({ heartbeat: o }), this.flush(), s > 0 && this.noteFirstBytes();
1742
2061
  }
1743
2062
  maybeEmitMediaInfo(e) {
1744
2063
  if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
1745
2064
  let t = {};
1746
- e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = ft[e.keySystem]), e.drmSecurityLevel && (t.drmSecurityLevel = e.drmSecurityLevel), e.ladderHeights?.length && (t.ladderHeights = e.ladderHeights), e.ladderTopBitrateKbps && (t.ladderTopBitrateKbps = e.ladderTopBitrateKbps), e.lowLatency && (t.lowLatency = !0);
2065
+ e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = jt[e.keySystem]), e.drmSecurityLevel && (t.drmSecurityLevel = e.drmSecurityLevel), e.ladderHeights?.length && (t.ladderHeights = e.ladderHeights), e.ladderTopBitrateKbps && (t.ladderTopBitrateKbps = e.ladderTopBitrateKbps), e.lowLatency && (t.lowLatency = !0);
1747
2066
  let n = JSON.stringify(t);
1748
2067
  n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
1749
2068
  }
@@ -1755,7 +2074,7 @@ var gt = class {
1755
2074
  let r = this.safeSample();
1756
2075
  r && this.maybeEmitMediaInfo(r);
1757
2076
  let i = { reason: e }, a = r?.bytesLoaded ?? this.lastBytes;
1758
- a > 0 && (i.bytesDownloadedTotal = a), this.playingTotalMs > 0 && (i.watchTimeMs = Math.round(this.playingTotalMs)), this.rebufferCount > 0 && (i.rebufferCount = this.rebufferCount), this.rebufferTotalMs > 0 && (i.rebufferTimeMs = Math.round(this.rebufferTotalMs)), this.startupTimeMs > 0 && (i.startupTimeMs = Math.round(this.startupTimeMs)), this.manifestLoadMs > 0 && (i.manifestLoadMs = Math.round(this.manifestLoadMs)), this.firstFragLoadMs > 0 && (i.firstFragLoadMs = Math.round(this.firstFragLoadMs));
2077
+ a > 0 && (i.bytesDownloadedTotal = a), this.playingTotalMs > 0 && (i.watchTimeMs = Math.round(this.playingTotalMs)), this.rebufferCount > 0 && (i.rebufferCount = this.rebufferCount), this.rebufferTotalMs > 0 && (i.rebufferTimeMs = Math.round(this.rebufferTotalMs)), this.startupTimeMs > 0 && (i.startupTimeMs = Math.round(this.startupTimeMs)), this.manifestLoadMs > 0 && (i.manifestLoadMs = Math.round(this.manifestLoadMs)), this.firstPlaylistMs > 0 && (i.firstPlaylistMs = Math.round(this.firstPlaylistMs)), this.firstFragLoadMs > 0 && (i.firstFragLoadMs = Math.round(this.firstFragLoadMs)), this.drmSetupMs > 0 && (i.drmSetupMs = Math.round(this.drmSetupMs));
1759
2078
  let o = r?.licenseTimeMs ?? 0;
1760
2079
  o > 0 && (i.licenseTimeMs = Math.round(o));
1761
2080
  let s = r?.droppedFrames ?? this.lastDropped;
@@ -1763,9 +2082,9 @@ var gt = class {
1763
2082
  let c = r?.positionMs ?? 0;
1764
2083
  c > 0 && (i.exitPositionMs = Math.round(c)), this.activeTotalMs > 0 && (i.activeWatchTimeMs = Math.round(this.activeTotalMs)), this.mutedTotalMs > 0 && (i.mutedTimeMs = Math.round(this.mutedTotalMs));
1765
2084
  let l = e !== "END_REASON_ENDED";
1766
- l && this.lastStallMs > 0 && n - this.lastStallMs <= K && (i.abandonAfterStallMs = Math.max(G, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= K && (i.abandonAfterErrorMs = Math.max(G, Math.round(n - this.lastErrorMs))), this.fullscreenTotalMs > 0 && (i.fullscreenTimeMs = Math.round(this.fullscreenTotalMs)), this.pipTotalMs > 0 && (i.pipTimeMs = Math.round(this.pipTotalMs)), this.volumeChanges > 0 && (i.volumeChanges = this.volumeChanges), this.muteChanges > 0 && (i.muteChanges = this.muteChanges);
2085
+ l && this.lastStallMs > 0 && n - this.lastStallMs <= Ot && (i.abandonAfterStallMs = Math.max(X, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= Ot && (i.abandonAfterErrorMs = Math.max(X, Math.round(n - this.lastErrorMs))), this.fullscreenTotalMs > 0 && (i.fullscreenTimeMs = Math.round(this.fullscreenTotalMs)), this.pipTotalMs > 0 && (i.pipTimeMs = Math.round(this.pipTotalMs)), this.volumeChanges > 0 && (i.volumeChanges = this.volumeChanges), this.muteChanges > 0 && (i.muteChanges = this.muteChanges);
1767
2086
  let u = r?.decodedFrames ?? this.lastDecoded;
1768
- u > 0 && (i.decodedFramesTotal = u), this.startupTimeMs === 0 && (i.failedStage = this.lastErrorStage ?? (this.manifestLoadMs === 0 ? "ERROR_STAGE_MANIFEST" : this.firstFragLoadMs === 0 ? "ERROR_STAGE_FIRST_SEGMENT" : "ERROR_STAGE_OTHER")), this.pending.push(this.wrap({ sessionEnd: i })), this.flush(e === "END_REASON_PAGEHIDE");
2087
+ u > 0 && (i.decodedFramesTotal = u), e === "END_REASON_STOPPED" && this.startupTimeMs === 0 && this.lastErrorStage === null && (e = "END_REASON_ABORTED", i.reason = e), this.startupTimeMs === 0 && e !== "END_REASON_ABORTED" && (i.failedStage = this.lastErrorStage ?? (this.manifestLoadMs === 0 ? "ERROR_STAGE_MANIFEST" : this.firstFragLoadMs === 0 ? "ERROR_STAGE_FIRST_SEGMENT" : "ERROR_STAGE_OTHER")), this.pending.push(this.wrap({ sessionEnd: i })), this.flush(e === "END_REASON_PAGEHIDE");
1769
2088
  }
1770
2089
  safeSample() {
1771
2090
  try {
@@ -1783,7 +2102,7 @@ var gt = class {
1783
2102
  };
1784
2103
  }
1785
2104
  enqueue(e) {
1786
- for (this.pending.push(this.wrap(e)); this.pending.length > ct;) {
2105
+ for (this.pending.push(this.wrap(e)); this.pending.length > Et;) {
1787
2106
  let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
1788
2107
  this.pending.splice(e >= 0 ? e : 0, 1);
1789
2108
  }
@@ -1806,7 +2125,7 @@ var gt = class {
1806
2125
  };
1807
2126
  //#endregion
1808
2127
  //#region src/core/environment.ts
1809
- function _t() {
2128
+ function zt() {
1810
2129
  if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
1811
2130
  let e;
1812
2131
  try {
@@ -1831,14 +2150,14 @@ function _t() {
1831
2150
  integration: "INTEGRATION_KIND_IFRAME"
1832
2151
  };
1833
2152
  }
1834
- function vt() {
2153
+ function Bt() {
1835
2154
  if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
1836
2155
  let e = navigator.userAgentData, t = e?.platform ?? "";
1837
2156
  if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
1838
2157
  let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
1839
2158
  return e?.mobile ?? (typeof navigator.maxTouchPoints == "number" && navigator.maxTouchPoints > 1 && n > 0 && n < 900) ? n >= 600 ? "DEVICE_CLASS_TABLET" : "DEVICE_CLASS_PHONE" : e || n >= 900 || /mac|win|linux|chrome os/i.test(t) ? "DEVICE_CLASS_DESKTOP" : "DEVICE_CLASS_OTHER";
1840
2159
  }
1841
- function yt() {
2160
+ function Vt() {
1842
2161
  let e = typeof navigator < "u" ? navigator.connection : void 0;
1843
2162
  if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
1844
2163
  let t = {
@@ -1857,17 +2176,17 @@ function yt() {
1857
2176
  }
1858
2177
  return typeof e.downlink == "number" && e.downlink > 0 && (i.downlinkKbps = Math.round(e.downlink * 1e3)), typeof e.rtt == "number" && e.rtt > 0 && (i.rttMs = Math.round(e.rtt)), e.saveData && (i.saveData = !0), i;
1859
2178
  }
1860
- function bt(e) {
1861
- let t = _t(), n = {
1862
- deviceClass: vt(),
2179
+ function Ht(e) {
2180
+ let t = zt(), n = {
2181
+ deviceClass: Bt(),
1863
2182
  integration: t.integration,
1864
- ...yt()
2183
+ ...Vt()
1865
2184
  };
1866
2185
  t.host && (n.host = t.host), typeof screen < "u" && screen.width > 0 && (n.screenWidth = screen.width, n.screenHeight = screen.height), typeof window < "u" && window.devicePixelRatio > 0 && (n.devicePixelRatio = Math.round(window.devicePixelRatio * 100) / 100);
1867
2186
  let r = e.getBoundingClientRect?.();
1868
2187
  return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
1869
2188
  }
1870
- async function xt() {
2189
+ async function Ut() {
1871
2190
  let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
1872
2191
  if (!e?.getHighEntropyValues) return {};
1873
2192
  try {
@@ -1879,7 +2198,7 @@ async function xt() {
1879
2198
  }
1880
2199
  //#endregion
1881
2200
  //#region src/core/version.ts
1882
- var St = "2.10.0", Ct = {
2201
+ var Wt = "2.11.1", Gt = {
1883
2202
  accent: "#E1B600",
1884
2203
  accentText: "#1B1C23",
1885
2204
  background: "#0e0f13",
@@ -1890,7 +2209,7 @@ var St = "2.10.0", Ct = {
1890
2209
  border: "rgba(255, 255, 255, 0.1)",
1891
2210
  radius: "8px",
1892
2211
  fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
1893
- }, wt = {
2212
+ }, Kt = {
1894
2213
  accent: "--havik-accent",
1895
2214
  accentText: "--havik-accent-text",
1896
2215
  background: "--havik-bg",
@@ -1902,25 +2221,25 @@ var St = "2.10.0", Ct = {
1902
2221
  radius: "--havik-radius",
1903
2222
  fontFamily: "--havik-font"
1904
2223
  };
1905
- function Tt(e, t) {
2224
+ function qt(e, t) {
1906
2225
  let n = {
1907
- ...Ct,
2226
+ ...Gt,
1908
2227
  ...t
1909
2228
  };
1910
- for (let t of Object.keys(wt)) e.style.setProperty(wt[t], n[t]);
2229
+ for (let t of Object.keys(Kt)) e.style.setProperty(Kt[t], n[t]);
1911
2230
  n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
1912
2231
  }
1913
2232
  //#endregion
1914
2233
  //#region src/ui/styles.ts
1915
- var Et = "havik-player-styles", Dt = "\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/* Absolutely centred, NOT a flex sibling of .havik-center: the hidden center\n button (.havik-gone) still occupies its 74px in the row, which pushed an\n in-flow spinner 37px right of centre. */\n.havik-spinner{position:absolute;inset:0;margin:auto;width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n";
1916
- function Ot(e = document) {
1917
- if (e.getElementById(Et)) return;
2234
+ var Jt = "havik-player-styles", Yt = "\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/* Absolutely centred, NOT a flex sibling of .havik-center: the hidden center\n button (.havik-gone) still occupies its 74px in the row, which pushed an\n in-flow spinner 37px right of centre. */\n.havik-spinner{position:absolute;inset:0;margin:auto;width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n";
2235
+ function Xt(e = document) {
2236
+ if (e.getElementById(Jt)) return;
1918
2237
  let t = e.createElement("style");
1919
- t.id = Et, t.textContent = Dt, e.head.appendChild(t);
2238
+ t.id = Jt, t.textContent = Yt, e.head.appendChild(t);
1920
2239
  }
1921
2240
  //#endregion
1922
2241
  //#region src/ui/icons.ts
1923
- var kt = {
2242
+ var Zt = {
1924
2243
  play: "<path d=\"M8 5v14l11-7z\"/>",
1925
2244
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
1926
2245
  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\"/>",
@@ -1933,21 +2252,21 @@ var kt = {
1933
2252
  liveDot: "<circle cx=\"12\" cy=\"12\" r=\"6\"/>",
1934
2253
  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\"/>"
1935
2254
  };
1936
- function Z(e) {
1937
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${kt[e]}</svg>`;
2255
+ function Q(e) {
2256
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${Zt[e]}</svg>`;
1938
2257
  }
1939
2258
  //#endregion
1940
2259
  //#region src/ui/controls.ts
1941
- var At = 3e3, jt = /* @__PURE__ */ new Set([
2260
+ var Qt = 3e3, $t = /* @__PURE__ */ new Set([
1942
2261
  "GONE",
1943
2262
  "NOT_FOUND",
1944
2263
  "INVALID_URN",
1945
2264
  "UNAUTHORIZED"
1946
2265
  ]);
1947
- function Mt(e) {
1948
- return new Nt(e);
2266
+ function en(e) {
2267
+ return new tn(e);
1949
2268
  }
1950
- var Nt = class {
2269
+ var tn = class {
1951
2270
  root;
1952
2271
  video;
1953
2272
  player;
@@ -1989,7 +2308,7 @@ var Nt = class {
1989
2308
  scrubbing = !1;
1990
2309
  offFns = [];
1991
2310
  constructor(e) {
1992
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? At, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, Ot(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");
2311
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Qt, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, Xt(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");
1993
2312
  let t = (e, t, n) => {
1994
2313
  let r = this.doc.createElement(e);
1995
2314
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -2000,12 +2319,12 @@ var Nt = class {
2000
2319
  "aria-label": r,
2001
2320
  title: r
2002
2321
  });
2003
- return i.innerHTML = Z(n), i;
2322
+ return i.innerHTML = Q(n), i;
2004
2323
  };
2005
2324
  this.overlay = t("div", "havik-overlay"), this.center = t("button", "havik-center", {
2006
2325
  type: "button",
2007
2326
  "aria-label": "Play"
2008
- }), this.center.innerHTML = Z("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", {
2327
+ }), this.center.innerHTML = Q("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", {
2009
2328
  role: "slider",
2010
2329
  "aria-label": "Seek",
2011
2330
  "aria-valuemin": "0",
@@ -2023,7 +2342,7 @@ var Nt = class {
2023
2342
  "aria-label": "Volume"
2024
2343
  });
2025
2344
  let a = t("div", "havik-vol");
2026
- a.append(this.muteBtn, this.volSlider), this.live = t("div", "havik-live"), this.live.innerHTML = Z("liveDot");
2345
+ a.append(this.muteBtn, this.volSlider), this.live = t("div", "havik-live"), this.live.innerHTML = Q("liveDot");
2027
2346
  let o = this.doc.createElement("span");
2028
2347
  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";
2029
2348
  let s = t("div", "havik-spacer");
@@ -2077,12 +2396,12 @@ var Nt = class {
2077
2396
  }
2078
2397
  if (e === "preplay") {
2079
2398
  let e = this.doc.createElement("button");
2080
- e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = Z("play"), e.addEventListener("click", () => void this.player.play()), n.append(e);
2399
+ e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = Q("play"), e.addEventListener("click", () => void this.player.play()), n.append(e);
2081
2400
  } else {
2082
2401
  let r = this.doc.createElement("div");
2083
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !jt.has(this.lastError.code)) {
2402
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !$t.has(this.lastError.code)) {
2084
2403
  let e = this.doc.createElement("button");
2085
- e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${Z("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), n.append(e);
2404
+ e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${Q("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), n.append(e);
2086
2405
  }
2087
2406
  }
2088
2407
  this.root.append(n), this.statusBox = n, this.show();
@@ -2121,11 +2440,11 @@ var Nt = class {
2121
2440
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
2122
2441
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
2123
2442
  }
2124
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Pt(this.video.currentTime)} / ${Pt(this.video.duration)}`);
2443
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${nn(this.video.currentTime)} / ${nn(this.video.duration)}`);
2125
2444
  }
2126
2445
  syncLive() {
2127
2446
  let e = this.player.getStats(), t = e.isLive !== !1;
2128
- this.live.style.display = t ? "" : "none", this.time.style.display = t ? "none" : "", this.live.classList.toggle("is-live", e.atLiveEdge !== !1 && t);
2447
+ this.live.style.display = t ? "" : "none", this.time.style.display = t ? "none" : "", this.seek.style.display = t ? "none" : "", this.live.classList.toggle("is-live", e.atLiveEdge !== !1 && t);
2129
2448
  }
2130
2449
  onSeekDown(e) {
2131
2450
  let t = this.seekableRange();
@@ -2171,17 +2490,17 @@ var Nt = class {
2171
2490
  renderQualityMenu() {
2172
2491
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
2173
2492
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
2174
- for (let n of e) this.menu.append(this.menuItem(Ft(n), t === n.index, () => this.player.setQuality(n.index)));
2493
+ for (let n of e) this.menu.append(this.menuItem(rn(n), t === n.index, () => this.player.setQuality(n.index)));
2175
2494
  }
2176
2495
  renderAudioMenu() {
2177
2496
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
2178
2497
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
2179
- for (let t of e) this.menu.append(this.menuItem(It(t), t.default, () => this.player.setAudioTrack(t.id)));
2498
+ for (let t of e) this.menu.append(this.menuItem(an(t), t.default, () => this.player.setAudioTrack(t.id)));
2180
2499
  }
2181
2500
  renderCcMenu() {
2182
2501
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
2183
2502
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
2184
- for (let t of e) this.menu.append(this.menuItem(Lt(t), !1, () => this.player.setTextTrack(t.id)));
2503
+ for (let t of e) this.menu.append(this.menuItem(on(t), !1, () => this.player.setTextTrack(t.id)));
2185
2504
  }
2186
2505
  onTracksChanged() {
2187
2506
  this.openMenu && this.renderMenu(this.openMenu);
@@ -2201,11 +2520,11 @@ var Nt = class {
2201
2520
  }
2202
2521
  async toggleFullscreen() {
2203
2522
  try {
2204
- C(this.video, this.root) ? await T(this.video, this.root) : await w(this.video, this.root);
2523
+ w(this.video, this.root) ? await E(this.video, this.root) : await T(this.video, this.root);
2205
2524
  } catch {}
2206
2525
  }
2207
2526
  syncFullscreen() {
2208
- let e = C(this.video, this.root);
2527
+ let e = w(this.video, this.root);
2209
2528
  this.setIcon(this.fsBtn, e ? "fullscreenExit" : "fullscreen"), this.fsBtn.setAttribute("aria-label", e ? "Exit fullscreen" : "Fullscreen");
2210
2529
  }
2211
2530
  poke() {
@@ -2264,7 +2583,7 @@ var Nt = class {
2264
2583
  }
2265
2584
  }
2266
2585
  setIcon(e, t) {
2267
- e.innerHTML = Z(t);
2586
+ e.innerHTML = Q(t);
2268
2587
  }
2269
2588
  destroy() {
2270
2589
  this.hideTimer && clearTimeout(this.hideTimer);
@@ -2281,41 +2600,41 @@ var Nt = class {
2281
2600
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
2282
2601
  }
2283
2602
  };
2284
- function Pt(e) {
2603
+ function nn(e) {
2285
2604
  (!Number.isFinite(e) || e < 0) && (e = 0);
2286
2605
  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");
2287
2606
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
2288
2607
  }
2289
- function Ft(e) {
2608
+ function rn(e) {
2290
2609
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
2291
2610
  }
2292
- function It(e) {
2611
+ function an(e) {
2293
2612
  return e.name || e.lang || `Track ${e.id}`;
2294
2613
  }
2295
- function Lt(e) {
2614
+ function on(e) {
2296
2615
  return e.name || e.lang || `Track ${e.id}`;
2297
2616
  }
2298
2617
  //#endregion
2299
2618
  //#region src/managed/index.ts
2300
- var Rt = 6e3, Q = 45e3, zt = 2e3, Bt = 3, Vt = 2e3, Ht = 48e4;
2301
- function Ut(e) {
2619
+ var sn = 6e3, cn = 45e3, ln = 2e3, un = 3, dn = 2e3, fn = 48e4;
2620
+ function pn(e) {
2302
2621
  if (e.env !== void 0 && e.baseUrl !== void 0) throw new n("INTERNAL", 0, `createPlayer: pass env OR baseUrl, not both (got env=${JSON.stringify(e.env)} and baseUrl=${JSON.stringify(e.baseUrl)})`);
2303
- if (e.env !== void 0) return I(e.env);
2622
+ if (e.env !== void 0) return U(e.env);
2304
2623
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
2305
2624
  return e.baseUrl;
2306
2625
  }
2307
- async function Wt(e) {
2626
+ async function mn(e) {
2308
2627
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
2309
- let t = Ut(e);
2628
+ let t = pn(e);
2310
2629
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
2311
2630
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
2312
- let r = new Gt({
2631
+ let r = new hn({
2313
2632
  ...e,
2314
2633
  baseUrl: t
2315
2634
  });
2316
2635
  return await r.start(), r;
2317
2636
  }
2318
- var Gt = class {
2637
+ var hn = class {
2319
2638
  opts;
2320
2639
  abort = new AbortController();
2321
2640
  listeners = {};
@@ -2382,7 +2701,7 @@ var Gt = class {
2382
2701
  };
2383
2702
  }
2384
2703
  beginArming(e, t, n = !1) {
2385
- let r = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? D(this.opts.baseUrl);
2704
+ let r = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? O(this.opts.baseUrl);
2386
2705
  r ? this.armWithSse(e, r, t, n) : this.beginArmingByPolling(e);
2387
2706
  }
2388
2707
  beginArmingByPolling(e) {
@@ -2403,7 +2722,7 @@ var Gt = class {
2403
2722
  setTimeout(() => {
2404
2723
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
2405
2724
  phase: i ? "unavailable" : "tooEarly",
2406
- retryInMs: Q,
2725
+ retryInMs: cn,
2407
2726
  attempt: 1,
2408
2727
  elapsedMs: 0,
2409
2728
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -2442,13 +2761,13 @@ var Gt = class {
2442
2761
  !a && !p() && h();
2443
2762
  }, e);
2444
2763
  };
2445
- _(Rt), this.armingSub = E({
2764
+ _(sn), this.armingSub = D({
2446
2765
  eventsBaseUrl: t,
2447
2766
  matchUrn: this.opts.matchUrn,
2448
2767
  credential: this.opts.credential,
2449
- onAlive: () => _(Q),
2768
+ onAlive: () => _(cn),
2450
2769
  onState: (e) => {
2451
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * zt);
2770
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * ln);
2452
2771
  },
2453
2772
  onError: () => h()
2454
2773
  });
@@ -2471,7 +2790,7 @@ var Gt = class {
2471
2790
  let n = await s(this.opts.credential);
2472
2791
  if (this.destroyed || t !== this.loadGen) return;
2473
2792
  this.setState("loading");
2474
- let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = Fe(this.opts.video, (e) => {
2793
+ let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = Be(this.opts.video, (e) => {
2475
2794
  this._state !== "ended" && this._state !== "error" && this.setState(e);
2476
2795
  });
2477
2796
  if (await i.load(), this.destroyed || t !== this.loadGen) {
@@ -2493,23 +2812,23 @@ var Gt = class {
2493
2812
  if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
2494
2813
  let i = e.analytics?.sid;
2495
2814
  if (!i) return;
2496
- let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = ht(this.opts.baseUrl, a);
2815
+ let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = Lt(this.opts.baseUrl, a);
2497
2816
  if (!o) {
2498
2817
  s && setTimeout(() => {
2499
2818
  this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, s));
2500
2819
  }, 0);
2501
2820
  return;
2502
2821
  }
2503
- let c = this.opts.video, l = new gt({
2822
+ let c = this.opts.video, l = new Rt({
2504
2823
  endpoint: o,
2505
2824
  sid: i,
2506
2825
  player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
2507
- sdkVersion: St,
2508
- environment: bt(c),
2826
+ sdkVersion: Wt,
2827
+ environment: Ht(c),
2509
2828
  idleMs: typeof this.opts.analytics == "object" ? this.opts.analytics.idleMs : void 0,
2510
2829
  onIdle: () => this.onAnalyticsIdle(t),
2511
2830
  sample: () => {
2512
- let e = this.engine?.getStats() ?? L, t = c.buffered.length ? Math.max(0, c.buffered.end(c.buffered.length - 1) - c.currentTime) : 0;
2831
+ let e = this.engine?.getStats() ?? W, t = c.buffered.length ? Math.max(0, c.buffered.end(c.buffered.length - 1) - c.currentTime) : 0;
2513
2832
  return {
2514
2833
  ...e,
2515
2834
  bufferedMs: Math.round(t * 1e3),
@@ -2523,7 +2842,7 @@ var Gt = class {
2523
2842
  let u = () => l.noteSeek(), d = () => l.noteVolume(c.muted, c.volume);
2524
2843
  c.addEventListener("seeking", u), c.addEventListener("volumechange", d), this.detachMediaListeners = () => {
2525
2844
  c.removeEventListener("seeking", u), c.removeEventListener("volumechange", d);
2526
- }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), xt().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
2845
+ }, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), Ut().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
2527
2846
  }
2528
2847
  currentPresentation() {
2529
2848
  let e = this.opts.video;
@@ -2538,24 +2857,34 @@ var Gt = class {
2538
2857
  video: this.opts.video,
2539
2858
  descriptor: e,
2540
2859
  credential: t,
2541
- deviceId: A(),
2860
+ deviceId: j(),
2542
2861
  userId: this.opts.userId,
2543
2862
  lowLatency: this.opts.lowLatency === void 0 || this.opts.lowLatency === "auto" || this.opts.lowLatency,
2544
2863
  snapToLiveOnRefocus: this.opts.snapToLiveOnRefocus,
2864
+ mediaDuringKey: this.opts.mediaDuringKey,
2545
2865
  debug: this.opts.debug,
2546
2866
  deferLoad: r,
2547
2867
  hlsConfig: i,
2548
2868
  emit: (e) => this.onEngineEvent(e)
2549
2869
  }, o = this.opts.engine ?? "auto";
2550
- if (o === "native") return new H(a);
2551
- if (o === "hls") return new W(a);
2552
- if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && B(this.opts.video) && V()) return new H(a);
2553
- if (Re()) return new W(a);
2554
- if (B(this.opts.video)) return new H(a);
2870
+ if (o === "native") return new Y(a);
2871
+ if (o === "hls") return new St(a);
2872
+ if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && q(this.opts.video) && J()) return new Y(a);
2873
+ if (Qe()) return new St(a);
2874
+ if (q(this.opts.video)) return new Y(a);
2555
2875
  throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
2556
2876
  }
2557
2877
  onEngineEvent(e) {
2558
2878
  if (!this.destroyed) switch (e.type) {
2879
+ case "firstPlaylist":
2880
+ this.analyticsSession?.noteFirstPlaylist();
2881
+ break;
2882
+ case "firstBytes":
2883
+ this.analyticsSession?.noteFirstBytes();
2884
+ break;
2885
+ case "drmReady":
2886
+ this.analyticsSession?.noteDrmReady();
2887
+ break;
2559
2888
  case "ready":
2560
2889
  this.analyticsSession?.noteReady(), this.emit("ready"), (this.opts.autoplay !== !1 || this.playRequested) && this.play();
2561
2890
  break;
@@ -2579,11 +2908,11 @@ var Gt = class {
2579
2908
  }
2580
2909
  startLiveState(e) {
2581
2910
  if (this.opts.liveStateEvents === !1) return;
2582
- let t = this.opts.eventsBaseUrl ?? D(this.opts.baseUrl);
2911
+ let t = this.opts.eventsBaseUrl ?? O(this.opts.baseUrl);
2583
2912
  if (!t) return;
2584
2913
  this.closeLiveState();
2585
2914
  let r = 0, i = !1;
2586
- this.liveStateSub = E({
2915
+ this.liveStateSub = D({
2587
2916
  eventsBaseUrl: t,
2588
2917
  matchUrn: this.opts.matchUrn,
2589
2918
  credential: this.opts.credential,
@@ -2594,7 +2923,7 @@ var Gt = class {
2594
2923
  r = 0, i = !1;
2595
2924
  },
2596
2925
  onError: () => {
2597
- this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= Bt && (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.`))));
2926
+ this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= un && (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.`))));
2598
2927
  }
2599
2928
  });
2600
2929
  }
@@ -2616,7 +2945,7 @@ var Gt = class {
2616
2945
  scheduleRejoin(e) {
2617
2946
  this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
2618
2947
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
2619
- }, Math.random() * zt));
2948
+ }, Math.random() * ln));
2620
2949
  }
2621
2950
  cancelRejoin() {
2622
2951
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -2640,7 +2969,7 @@ var Gt = class {
2640
2969
  }
2641
2970
  t.controls = !1;
2642
2971
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
2643
- r && r.insertBefore(n, t), n.appendChild(t), Tt(n, this.opts.theme), this.container = n, this.controlBar = Mt({
2972
+ r && r.insertBefore(n, t), n.appendChild(t), qt(n, this.opts.theme), this.container = n, this.controlBar = en({
2644
2973
  root: n,
2645
2974
  video: t,
2646
2975
  player: this,
@@ -2670,7 +2999,7 @@ var Gt = class {
2670
2999
  }
2671
3000
  startStats() {
2672
3001
  this.stopStats();
2673
- let e = Math.max(500, this.opts.statsIntervalMs ?? Vt);
3002
+ let e = Math.max(500, this.opts.statsIntervalMs ?? dn);
2674
3003
  this.statsTimer = setInterval(() => {
2675
3004
  if (!this.engine) return;
2676
3005
  let e = this.engine.getStats();
@@ -2682,7 +3011,7 @@ var Gt = class {
2682
3011
  }
2683
3012
  scheduleLicenseRefresh() {
2684
3013
  this.stopRefresh();
2685
- let e = this.opts.licenseRefreshMs ?? Ht;
3014
+ let e = this.opts.licenseRefreshMs ?? fn;
2686
3015
  e && this._descriptor?.drmEnabled && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
2687
3016
  }
2688
3017
  stopRefresh() {
@@ -2727,7 +3056,7 @@ var Gt = class {
2727
3056
  this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
2728
3057
  }
2729
3058
  getStats() {
2730
- return this.engine?.getStats() ?? L;
3059
+ return this.engine?.getStats() ?? W;
2731
3060
  }
2732
3061
  getQualityLevels() {
2733
3062
  return this.engine?.getQualityLevels() ?? [];
@@ -2766,10 +3095,10 @@ var Gt = class {
2766
3095
  e.pictureInPictureElement === this.opts.video && typeof e.exitPictureInPicture == "function" && await e.exitPictureInPicture();
2767
3096
  }
2768
3097
  async enterFullscreen() {
2769
- return !this.destroyed && w(this.opts.video, this.container ?? void 0);
3098
+ return !this.destroyed && T(this.opts.video, this.container ?? void 0);
2770
3099
  }
2771
3100
  async exitFullscreen() {
2772
- await T(this.opts.video, this.container ?? void 0);
3101
+ await E(this.opts.video, this.container ?? void 0);
2773
3102
  }
2774
3103
  retry() {
2775
3104
  return this.reload();
@@ -2837,7 +3166,7 @@ function $(e) {
2837
3166
  }
2838
3167
  //#endregion
2839
3168
  //#region src/embed/host.ts
2840
- function Kt(e) {
3169
+ function gn(e) {
2841
3170
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
2842
3171
  if (!e.src) throw Error("mountEmbed: opts.src is required");
2843
3172
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -2896,15 +3225,15 @@ function Kt(e) {
2896
3225
  }
2897
3226
  //#endregion
2898
3227
  //#region src/embed/page.ts
2899
- var qt = /^[A-Za-z0-9:_.-]{1,128}$/;
2900
- function Jt(e) {
3228
+ var _n = /^[A-Za-z0-9:_.-]{1,128}$/;
3229
+ function vn(e) {
2901
3230
  if (typeof e == "boolean") return e;
2902
3231
  if (typeof e == "object" && e) {
2903
3232
  let t = e.endpoint;
2904
3233
  return typeof t == "string" ? { endpoint: t } : {};
2905
3234
  }
2906
3235
  }
2907
- function Yt() {
3236
+ function yn() {
2908
3237
  let e = window.HAVIK_EMBED_CONFIG;
2909
3238
  if (e) return {
2910
3239
  baseUrl: e.baseUrl ?? "",
@@ -2914,7 +3243,7 @@ function Yt() {
2914
3243
  autoplay: e.autoplay ?? !0,
2915
3244
  muted: e.muted ?? !0,
2916
3245
  waitForLive: e.waitForLive ?? !0,
2917
- analytics: Jt(e.analytics)
3246
+ analytics: vn(e.analytics)
2918
3247
  };
2919
3248
  let t = new URLSearchParams(location.search);
2920
3249
  return {
@@ -2927,9 +3256,9 @@ function Yt() {
2927
3256
  waitForLive: !0
2928
3257
  };
2929
3258
  }
2930
- async function Xt() {
2931
- let e = Yt();
2932
- Qt();
3259
+ async function bn() {
3260
+ let e = yn();
3261
+ Sn();
2933
3262
  let t = document.createElement("video");
2934
3263
  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);
2935
3264
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -2984,12 +3313,12 @@ async function Xt() {
2984
3313
  code: e.code,
2985
3314
  message: e.message
2986
3315
  })), e.on("autoplayblocked", () => {
2987
- i({ type: "oddin:autoplayblocked" }), Zt(t, e);
3316
+ i({ type: "oddin:autoplayblocked" }), xn(t, e);
2988
3317
  });
2989
3318
  }, s = async (r) => {
2990
3319
  a?.destroy(), a = null;
2991
3320
  try {
2992
- let n = await Wt({
3321
+ let n = await mn({
2993
3322
  video: t,
2994
3323
  baseUrl: e.baseUrl,
2995
3324
  matchUrn: r,
@@ -3051,7 +3380,7 @@ async function Xt() {
3051
3380
  a?.retry();
3052
3381
  break;
3053
3382
  case "oddin:load":
3054
- typeof e.matchUrn == "string" && qt.test(e.matchUrn) && s(e.matchUrn);
3383
+ typeof e.matchUrn == "string" && _n.test(e.matchUrn) && s(e.matchUrn);
3055
3384
  break;
3056
3385
  case "oddin:destroy": c();
3057
3386
  }
@@ -3063,18 +3392,18 @@ async function Xt() {
3063
3392
  protocol: 1
3064
3393
  });
3065
3394
  }
3066
- function Zt(e, t) {
3395
+ function xn(e, t) {
3067
3396
  if (document.getElementById("havik-tap")) return;
3068
3397
  let n = document.createElement("button");
3069
3398
  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", () => {
3070
3399
  n.remove(), t.play();
3071
3400
  }), document.body.appendChild(n);
3072
3401
  }
3073
- function Qt() {
3402
+ function Sn() {
3074
3403
  let e = document.createElement("style");
3075
3404
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
3076
3405
  }
3077
3406
  //#endregion
3078
- export { Ct as ODDIN_THEME, n as PlaybackError, Tt as applyTheme, I as baseUrlForEnv, Xt as bootEmbed, ve as clearDeviceId, Wt as createPlayer, D as deriveEventsBaseUrl, Ae as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, de as fetchCatalog, pe as fetchTournament, he as flattenMatches, A as getDeviceId, Me as isLowLatencyManifest, $ as isOddinMessage, be as licenseRequestHeaders, Kt as mountEmbed, v as pollToLive, b as resolveStream, E as subscribeLiveState, C as videoFullscreenActive, ge as watchStatus };
3407
+ export { Gt as ODDIN_THEME, n as PlaybackError, qt as applyTheme, U as baseUrlForEnv, bn as bootEmbed, ye as clearDeviceId, mn as createPlayer, O as deriveEventsBaseUrl, Fe as detectDrmSupport, T as enterVideoFullscreen, E as exitVideoFullscreen, fe as fetchCatalog, me as fetchTournament, ge as flattenMatches, j as getDeviceId, Le as isLowLatencyManifest, $ as isOddinMessage, xe as licenseRequestHeaders, gn as mountEmbed, v as pollToLive, b as resolveStream, D as subscribeLiveState, w as videoFullscreenActive, _e as watchStatus };
3079
3408
 
3080
3409
  //# sourceMappingURL=index.js.map