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