@oddin-gg/havik-player 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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 +225 -216
- 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
|
}
|
|
@@ -1301,14 +1307,16 @@ var Re = class {
|
|
|
1301
1307
|
if (e && e(this.url, t)) return;
|
|
1302
1308
|
}
|
|
1303
1309
|
let n = this.opts.fetchFn ?? (typeof fetch < "u" ? fetch : void 0);
|
|
1304
|
-
|
|
1310
|
+
if (!n) return;
|
|
1311
|
+
let r = { "content-type": "application/json" };
|
|
1312
|
+
this.opts.apiKey && (r["x-api-key"] = this.opts.apiKey), n(this.url, {
|
|
1305
1313
|
method: "POST",
|
|
1306
|
-
headers:
|
|
1314
|
+
headers: r,
|
|
1307
1315
|
body: t,
|
|
1308
1316
|
keepalive: !0
|
|
1309
1317
|
}).catch(() => {});
|
|
1310
1318
|
}
|
|
1311
|
-
},
|
|
1319
|
+
}, Be = "1.14.0", W = {
|
|
1312
1320
|
accent: "#E1B600",
|
|
1313
1321
|
accentText: "#1B1C23",
|
|
1314
1322
|
background: "#0e0f13",
|
|
@@ -1341,15 +1349,15 @@ function K(e, t) {
|
|
|
1341
1349
|
}
|
|
1342
1350
|
//#endregion
|
|
1343
1351
|
//#region src/ui/styles.ts
|
|
1344
|
-
var q = "havik-player-styles",
|
|
1345
|
-
function
|
|
1352
|
+
var q = "havik-player-styles", Ve = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n.havik-spinner{width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n";
|
|
1353
|
+
function He(e = document) {
|
|
1346
1354
|
if (e.getElementById(q)) return;
|
|
1347
1355
|
let t = e.createElement("style");
|
|
1348
|
-
t.id = q, t.textContent =
|
|
1356
|
+
t.id = q, t.textContent = Ve, e.head.appendChild(t);
|
|
1349
1357
|
}
|
|
1350
1358
|
//#endregion
|
|
1351
1359
|
//#region src/ui/icons.ts
|
|
1352
|
-
var
|
|
1360
|
+
var Ue = {
|
|
1353
1361
|
play: "<path d=\"M8 5v14l11-7z\"/>",
|
|
1354
1362
|
pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
|
|
1355
1363
|
volumeHigh: "<path d=\"M3 10v4h4l5 5V5L7 10H3zm13.5 2a4.5 4.5 0 0 0-2.5-4v8a4.5 4.5 0 0 0 2.5-4zm-2.5-9v2.06A7 7 0 0 1 14 19v2a9 9 0 0 0 0-18z\"/>",
|
|
@@ -1363,20 +1371,20 @@ var He = {
|
|
|
1363
1371
|
retry: "<path d=\"M17.65 6.35A8 8 0 1 0 19.73 13h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z\"/>"
|
|
1364
1372
|
};
|
|
1365
1373
|
function J(e) {
|
|
1366
|
-
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${
|
|
1374
|
+
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${Ue[e]}</svg>`;
|
|
1367
1375
|
}
|
|
1368
1376
|
//#endregion
|
|
1369
1377
|
//#region src/ui/controls.ts
|
|
1370
|
-
var
|
|
1378
|
+
var We = 3e3, Ge = /* @__PURE__ */ new Set([
|
|
1371
1379
|
"GONE",
|
|
1372
1380
|
"NOT_FOUND",
|
|
1373
1381
|
"INVALID_URN",
|
|
1374
1382
|
"UNAUTHORIZED"
|
|
1375
1383
|
]);
|
|
1376
|
-
function
|
|
1377
|
-
return new
|
|
1384
|
+
function Ke(e) {
|
|
1385
|
+
return new qe(e);
|
|
1378
1386
|
}
|
|
1379
|
-
var
|
|
1387
|
+
var qe = class {
|
|
1380
1388
|
root;
|
|
1381
1389
|
video;
|
|
1382
1390
|
player;
|
|
@@ -1416,7 +1424,7 @@ var Ke = class {
|
|
|
1416
1424
|
scrubbing = !1;
|
|
1417
1425
|
offFns = [];
|
|
1418
1426
|
constructor(e) {
|
|
1419
|
-
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ??
|
|
1427
|
+
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? We, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.errorMessage = e.errorMessage, He(this.doc), this.root.classList.add("havik-player"), this.root.hasAttribute("tabindex") || (this.root.tabIndex = 0), this.root.setAttribute("role", "region"), this.root.setAttribute("aria-label", "Video player");
|
|
1420
1428
|
let t = (e, t, n) => {
|
|
1421
1429
|
let r = this.doc.createElement(e);
|
|
1422
1430
|
if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
|
|
@@ -1503,7 +1511,7 @@ var Ke = class {
|
|
|
1503
1511
|
e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = J("play"), e.addEventListener("click", () => void this.player.play()), t.append(e);
|
|
1504
1512
|
} else {
|
|
1505
1513
|
let n = this.doc.createElement("div");
|
|
1506
|
-
if (n.className = "havik-status__msg", n.textContent = e === "ended" ? this.endedMessage : e === "waiting" ? "Starting soon…" : this.errorMessage ?? this.lastError?.message ?? "Playback unavailable", t.append(n), e === "error" && this.lastError && !
|
|
1514
|
+
if (n.className = "havik-status__msg", n.textContent = e === "ended" ? this.endedMessage : e === "waiting" ? "Starting soon…" : this.errorMessage ?? this.lastError?.message ?? "Playback unavailable", t.append(n), e === "error" && this.lastError && !Ge.has(this.lastError.code)) {
|
|
1507
1515
|
let e = this.doc.createElement("button");
|
|
1508
1516
|
e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${J("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), t.append(e);
|
|
1509
1517
|
}
|
|
@@ -1594,17 +1602,17 @@ var Ke = class {
|
|
|
1594
1602
|
renderQualityMenu() {
|
|
1595
1603
|
let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
|
|
1596
1604
|
n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
|
|
1597
|
-
for (let n of e) this.menu.append(this.menuItem(
|
|
1605
|
+
for (let n of e) this.menu.append(this.menuItem(Je(n), t === n.index, () => this.player.setQuality(n.index)));
|
|
1598
1606
|
}
|
|
1599
1607
|
renderAudioMenu() {
|
|
1600
1608
|
let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
|
|
1601
1609
|
t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
|
|
1602
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
1610
|
+
for (let t of e) this.menu.append(this.menuItem(Ye(t), t.default, () => this.player.setAudioTrack(t.id)));
|
|
1603
1611
|
}
|
|
1604
1612
|
renderCcMenu() {
|
|
1605
1613
|
let e = this.player.getTextTracks(), t = this.doc.createElement("div");
|
|
1606
1614
|
t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
|
|
1607
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
1615
|
+
for (let t of e) this.menu.append(this.menuItem(Xe(t), !1, () => this.player.setTextTrack(t.id)));
|
|
1608
1616
|
}
|
|
1609
1617
|
onTracksChanged() {
|
|
1610
1618
|
this.openMenu && this.renderMenu(this.openMenu);
|
|
@@ -1710,27 +1718,27 @@ function Y(e) {
|
|
|
1710
1718
|
let t = Math.floor(e % 60), n = Math.floor(e / 60 % 60), r = Math.floor(e / 3600), i = r > 0 ? String(n).padStart(2, "0") : String(n), a = String(t).padStart(2, "0");
|
|
1711
1719
|
return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
|
|
1712
1720
|
}
|
|
1713
|
-
function
|
|
1721
|
+
function Je(e) {
|
|
1714
1722
|
return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
|
|
1715
1723
|
}
|
|
1716
|
-
function
|
|
1724
|
+
function Ye(e) {
|
|
1717
1725
|
return e.name || e.lang || `Track ${e.id}`;
|
|
1718
1726
|
}
|
|
1719
|
-
function
|
|
1727
|
+
function Xe(e) {
|
|
1720
1728
|
return e.name || e.lang || `Track ${e.id}`;
|
|
1721
1729
|
}
|
|
1722
1730
|
//#endregion
|
|
1723
1731
|
//#region src/managed/index.ts
|
|
1724
|
-
var
|
|
1732
|
+
var Ze = 6e3, X = 45e3, Qe = 2e3, $e = 3, Z = 2e3, et = 480 * 1e3;
|
|
1725
1733
|
async function Q(e) {
|
|
1726
1734
|
if (!e.video || typeof e.video.play != "function") throw new t("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
|
|
1727
1735
|
if (!e.baseUrl) throw new t("INTERNAL", 0, "createPlayer: baseUrl is required");
|
|
1728
1736
|
if (!e.matchUrn) throw new t("INVALID_URN", 0, "createPlayer: matchUrn is required");
|
|
1729
1737
|
if (!e.credential) throw new t("INTERNAL", 0, "createPlayer: credential is required");
|
|
1730
|
-
let n = new
|
|
1738
|
+
let n = new tt(e);
|
|
1731
1739
|
return await n.start(), n;
|
|
1732
1740
|
}
|
|
1733
|
-
var
|
|
1741
|
+
var tt = class {
|
|
1734
1742
|
opts;
|
|
1735
1743
|
abort = new AbortController();
|
|
1736
1744
|
listeners = {};
|
|
@@ -1766,13 +1774,13 @@ var et = class {
|
|
|
1766
1774
|
this.opts.muted && (this.opts.video.muted = !0), this.opts.poster && (this.opts.video.poster = this.opts.poster), this.mountControls(), this.attachPipListeners(), this.setState("loading");
|
|
1767
1775
|
let e = ++this.loadGen, n = this.resolveOptions();
|
|
1768
1776
|
try {
|
|
1769
|
-
let t = await
|
|
1777
|
+
let t = await y(n);
|
|
1770
1778
|
if (await this.onResolved(t, e), e === this.loadGen && this._state === "error" && this._lastError) {
|
|
1771
1779
|
let e = this._lastError;
|
|
1772
1780
|
throw this.destroy(), e;
|
|
1773
1781
|
}
|
|
1774
1782
|
} catch (n) {
|
|
1775
|
-
if (this.opts.waitForLive && n instanceof t &&
|
|
1783
|
+
if (this.opts.waitForLive && n instanceof t && a(n.code)) {
|
|
1776
1784
|
this.setState("waiting"), this.beginArming(e, n.liveStartsAtMs);
|
|
1777
1785
|
return;
|
|
1778
1786
|
}
|
|
@@ -1788,11 +1796,11 @@ var et = class {
|
|
|
1788
1796
|
};
|
|
1789
1797
|
}
|
|
1790
1798
|
beginArming(e, t) {
|
|
1791
|
-
let n = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ??
|
|
1799
|
+
let n = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? T(this.opts.baseUrl);
|
|
1792
1800
|
n ? this.armWithSse(e, n, t) : this.beginArmingByPolling(e);
|
|
1793
1801
|
}
|
|
1794
1802
|
beginArmingByPolling(e) {
|
|
1795
|
-
|
|
1803
|
+
b({
|
|
1796
1804
|
...this.resolveOptions(),
|
|
1797
1805
|
signal: this.abort.signal,
|
|
1798
1806
|
waitForLive: this.buildWaitConfig(e)
|
|
@@ -1803,11 +1811,11 @@ var et = class {
|
|
|
1803
1811
|
});
|
|
1804
1812
|
}
|
|
1805
1813
|
armWithSse(e, n, r) {
|
|
1806
|
-
let
|
|
1814
|
+
let i = !1, o = !1, s = new AbortController();
|
|
1807
1815
|
this.armFallbackCtrl = s, this.setState("waiting");
|
|
1808
1816
|
let c = this.opts.waitForLive, l = c && c !== !0 ? c.kickoffAt : void 0, u = typeof l == "number" ? l : typeof l == "string" ? Date.parse(l) : NaN, d = r ?? (Number.isFinite(u) ? u : void 0);
|
|
1809
1817
|
setTimeout(() => {
|
|
1810
|
-
|
|
1818
|
+
i || this.destroyed || e !== this.loadGen || this.emit("waiting", {
|
|
1811
1819
|
phase: "tooEarly",
|
|
1812
1820
|
retryInMs: X,
|
|
1813
1821
|
attempt: 1,
|
|
@@ -1816,8 +1824,8 @@ var et = class {
|
|
|
1816
1824
|
});
|
|
1817
1825
|
}, 0);
|
|
1818
1826
|
let f = () => this.destroyed || this.abort.signal.aborted || e !== this.loadGen, p = async (n) => {
|
|
1819
|
-
if (!(
|
|
1820
|
-
|
|
1827
|
+
if (!(i || f())) {
|
|
1828
|
+
i = !0, this.closeArming(), s.abort();
|
|
1821
1829
|
try {
|
|
1822
1830
|
await this.onResolved(n, e);
|
|
1823
1831
|
} catch (e) {
|
|
@@ -1826,29 +1834,29 @@ var et = class {
|
|
|
1826
1834
|
}
|
|
1827
1835
|
}
|
|
1828
1836
|
}, m = () => {
|
|
1829
|
-
|
|
1837
|
+
i || o || f() || (o = !0, b({
|
|
1830
1838
|
...this.resolveOptions(),
|
|
1831
1839
|
signal: s.signal,
|
|
1832
1840
|
waitForLive: this.buildWaitConfig(e)
|
|
1833
1841
|
}).then((e) => p(e)).catch((e) => {
|
|
1834
|
-
|
|
1842
|
+
i || f() || s.signal.aborted || (i = !0, this.closeArming(), this.fail(e instanceof t ? e : new t("INTERNAL", 0, String(e))));
|
|
1835
1843
|
}));
|
|
1836
1844
|
}, h = () => {
|
|
1837
|
-
|
|
1838
|
-
|
|
1845
|
+
i || f() || y(this.resolveOptions()).then((e) => p(e)).catch((e) => {
|
|
1846
|
+
i || f() || e instanceof t && a(e.code) || (i = !0, this.closeArming(), s.abort(), this.fail(e instanceof t ? e : new t("INTERNAL", 0, String(e))));
|
|
1839
1847
|
});
|
|
1840
1848
|
}, g = (e) => {
|
|
1841
1849
|
this.armSseTimer && clearTimeout(this.armSseTimer), this.armSseTimer = setTimeout(() => {
|
|
1842
|
-
!
|
|
1850
|
+
!i && !f() && m();
|
|
1843
1851
|
}, e);
|
|
1844
1852
|
};
|
|
1845
|
-
g(
|
|
1853
|
+
g(Ze), this.armingSub = w({
|
|
1846
1854
|
eventsBaseUrl: n,
|
|
1847
1855
|
matchUrn: this.opts.matchUrn,
|
|
1848
1856
|
credential: this.opts.credential,
|
|
1849
1857
|
onAlive: () => g(X),
|
|
1850
1858
|
onState: (e) => {
|
|
1851
|
-
|
|
1859
|
+
i || f() || (e.state === "live" || e.state === "gone") && setTimeout(h, Math.random() * Qe);
|
|
1852
1860
|
},
|
|
1853
1861
|
onError: () => m()
|
|
1854
1862
|
});
|
|
@@ -1868,10 +1876,10 @@ var et = class {
|
|
|
1868
1876
|
async onResolved(e, t) {
|
|
1869
1877
|
if (this.destroyed || t !== this.loadGen) return;
|
|
1870
1878
|
this._descriptor = e, this.endedHandled = !1;
|
|
1871
|
-
let n = await
|
|
1879
|
+
let n = await o(this.opts.credential);
|
|
1872
1880
|
if (this.destroyed || t !== this.loadGen) return;
|
|
1873
1881
|
this.setState("loading");
|
|
1874
|
-
let r = this.createEngine(e, n), i =
|
|
1882
|
+
let r = this.createEngine(e, n), i = ve(this.opts.video, (e) => {
|
|
1875
1883
|
this._state === "ended" || this._state === "error" || this.setState(e);
|
|
1876
1884
|
});
|
|
1877
1885
|
if (await r.load(), this.destroyed || t !== this.loadGen) {
|
|
@@ -1882,32 +1890,33 @@ var et = class {
|
|
|
1882
1890
|
i(), r.destroy();
|
|
1883
1891
|
return;
|
|
1884
1892
|
}
|
|
1885
|
-
this.engine = r, this.detachMedia = i, this.opts.maxBitrate != null && r.setMaxBitrate(this.opts.maxBitrate), this.startStats(), this.scheduleLicenseRefresh(), this.startLiveState(t), this.startAnalytics(e, r);
|
|
1893
|
+
this.engine = r, this.detachMedia = i, this.opts.maxBitrate != null && r.setMaxBitrate(this.opts.maxBitrate), this.startStats(), this.scheduleLicenseRefresh(), this.startLiveState(t), this.startAnalytics(e, r, n.apiKey);
|
|
1886
1894
|
}
|
|
1887
|
-
startAnalytics(e, t) {
|
|
1895
|
+
startAnalytics(e, t, n) {
|
|
1888
1896
|
if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachSeekListener?.(), this.opts.analytics === !1) return;
|
|
1889
|
-
let
|
|
1890
|
-
if (!n) return;
|
|
1891
|
-
let r = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? Le(this.opts.baseUrl);
|
|
1897
|
+
let r = e.analytics?.sid;
|
|
1892
1898
|
if (!r) return;
|
|
1893
|
-
let i = this.opts.
|
|
1894
|
-
|
|
1895
|
-
|
|
1899
|
+
let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? Re(this.opts.baseUrl);
|
|
1900
|
+
if (!i) return;
|
|
1901
|
+
let a = this.opts.video, o = new ze({
|
|
1902
|
+
endpoint: i,
|
|
1903
|
+
sid: r,
|
|
1904
|
+
apiKey: n,
|
|
1896
1905
|
player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
|
|
1897
|
-
sdkVersion:
|
|
1906
|
+
sdkVersion: Be,
|
|
1898
1907
|
sample: () => {
|
|
1899
|
-
let e = this.engine?.getStats() ??
|
|
1908
|
+
let e = this.engine?.getStats() ?? F, t = a.buffered.length ? Math.max(0, a.buffered.end(a.buffered.length - 1) - a.currentTime) : 0;
|
|
1900
1909
|
return {
|
|
1901
1910
|
...e,
|
|
1902
1911
|
bufferedMs: Math.round(t * 1e3),
|
|
1903
1912
|
state: this._state,
|
|
1904
|
-
positionMs: Math.round(
|
|
1913
|
+
positionMs: Math.round(a.currentTime * 1e3)
|
|
1905
1914
|
};
|
|
1906
1915
|
}
|
|
1907
1916
|
});
|
|
1908
|
-
this.analyticsSession =
|
|
1909
|
-
let
|
|
1910
|
-
|
|
1917
|
+
this.analyticsSession = o;
|
|
1918
|
+
let s = () => o.noteSeek();
|
|
1919
|
+
a.addEventListener("seeking", s), this.detachSeekListener = () => a.removeEventListener("seeking", s), o.start();
|
|
1911
1920
|
}
|
|
1912
1921
|
createEngine(e, n) {
|
|
1913
1922
|
let r = { ...this.opts.hlsConfig };
|
|
@@ -1916,7 +1925,7 @@ var et = class {
|
|
|
1916
1925
|
video: this.opts.video,
|
|
1917
1926
|
descriptor: e,
|
|
1918
1927
|
credential: n,
|
|
1919
|
-
deviceId:
|
|
1928
|
+
deviceId: O(),
|
|
1920
1929
|
userId: this.opts.userId,
|
|
1921
1930
|
lowLatency: this.opts.lowLatency === void 0 || this.opts.lowLatency === "auto" || this.opts.lowLatency,
|
|
1922
1931
|
snapToLiveOnRefocus: this.opts.snapToLiveOnRefocus,
|
|
@@ -1927,7 +1936,7 @@ var et = class {
|
|
|
1927
1936
|
if (a === "native") return new z(i);
|
|
1928
1937
|
if (a === "hls") return new V(i);
|
|
1929
1938
|
if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && L(this.opts.video) && R()) return new z(i);
|
|
1930
|
-
if (
|
|
1939
|
+
if (xe()) return new V(i);
|
|
1931
1940
|
if (L(this.opts.video)) return new z(i);
|
|
1932
1941
|
throw new t("INTERNAL", 0, "no supported HLS playback engine in this browser");
|
|
1933
1942
|
}
|
|
@@ -1958,11 +1967,11 @@ var et = class {
|
|
|
1958
1967
|
}
|
|
1959
1968
|
startLiveState(e) {
|
|
1960
1969
|
if (this.opts.liveStateEvents === !1) return;
|
|
1961
|
-
let n = this.opts.eventsBaseUrl ??
|
|
1970
|
+
let n = this.opts.eventsBaseUrl ?? T(this.opts.baseUrl);
|
|
1962
1971
|
if (!n) return;
|
|
1963
1972
|
this.closeLiveState();
|
|
1964
1973
|
let r = 0, i = !1;
|
|
1965
|
-
this.liveStateSub =
|
|
1974
|
+
this.liveStateSub = w({
|
|
1966
1975
|
eventsBaseUrl: n,
|
|
1967
1976
|
matchUrn: this.opts.matchUrn,
|
|
1968
1977
|
credential: this.opts.credential,
|
|
@@ -1973,7 +1982,7 @@ var et = class {
|
|
|
1973
1982
|
r = 0, i = !1;
|
|
1974
1983
|
},
|
|
1975
1984
|
onError: () => {
|
|
1976
|
-
this.destroyed || e !== this.loadGen || i || (r += 1, r >=
|
|
1985
|
+
this.destroyed || e !== this.loadGen || i || (r += 1, r >= $e && (i = !0, this.emit("warning", new t("NETWORK", 0, `live-state events unreachable at ${n} after ${r} attempts — end-of-stream detection degraded to the HLS staleness watchdog. Check the events host, the api-key's allowed origins, and CSP connect-src.`))));
|
|
1977
1986
|
}
|
|
1978
1987
|
});
|
|
1979
1988
|
}
|
|
@@ -1995,7 +2004,7 @@ var et = class {
|
|
|
1995
2004
|
}
|
|
1996
2005
|
t.controls = !1;
|
|
1997
2006
|
let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
|
|
1998
|
-
r && r.insertBefore(n, t), n.appendChild(t), K(n, this.opts.theme), this.container = n, this.controlBar =
|
|
2007
|
+
r && r.insertBefore(n, t), n.appendChild(t), K(n, this.opts.theme), this.container = n, this.controlBar = Ke({
|
|
1999
2008
|
root: n,
|
|
2000
2009
|
video: t,
|
|
2001
2010
|
player: this,
|
|
@@ -2018,7 +2027,7 @@ var et = class {
|
|
|
2018
2027
|
}
|
|
2019
2028
|
startStats() {
|
|
2020
2029
|
this.stopStats();
|
|
2021
|
-
let e = Math.max(500, this.opts.statsIntervalMs ??
|
|
2030
|
+
let e = Math.max(500, this.opts.statsIntervalMs ?? Z);
|
|
2022
2031
|
this.statsTimer = setInterval(() => {
|
|
2023
2032
|
if (!this.engine) return;
|
|
2024
2033
|
let e = this.engine.getStats();
|
|
@@ -2030,7 +2039,7 @@ var et = class {
|
|
|
2030
2039
|
}
|
|
2031
2040
|
scheduleLicenseRefresh() {
|
|
2032
2041
|
this.stopRefresh();
|
|
2033
|
-
let e = this.opts.licenseRefreshMs ??
|
|
2042
|
+
let e = this.opts.licenseRefreshMs ?? et;
|
|
2034
2043
|
!e || !this._descriptor?.drmEnabled || (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
|
|
2035
2044
|
}
|
|
2036
2045
|
stopRefresh() {
|
|
@@ -2039,7 +2048,7 @@ var et = class {
|
|
|
2039
2048
|
async refreshLicense() {
|
|
2040
2049
|
let e = this.engine;
|
|
2041
2050
|
if (!(this.destroyed || !e)) try {
|
|
2042
|
-
let t = await
|
|
2051
|
+
let t = await y(this.resolveOptions());
|
|
2043
2052
|
if (this.destroyed || this.engine !== e) return;
|
|
2044
2053
|
let n = t.drm?.widevine?.licenseUrl, r = t.drm?.fairplay?.licenseUrl;
|
|
2045
2054
|
(n || r) && (e.setLicenseUrls({
|
|
@@ -2047,7 +2056,7 @@ var et = class {
|
|
|
2047
2056
|
fairplay: r
|
|
2048
2057
|
}), this._descriptor = t);
|
|
2049
2058
|
} catch (e) {
|
|
2050
|
-
e instanceof t && !
|
|
2059
|
+
e instanceof t && !a(e.code) && (this.stopRefresh(), this.destroyed || this.emit("warning", e));
|
|
2051
2060
|
}
|
|
2052
2061
|
}
|
|
2053
2062
|
async play() {
|
|
@@ -2067,7 +2076,7 @@ var et = class {
|
|
|
2067
2076
|
this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
|
|
2068
2077
|
}
|
|
2069
2078
|
getStats() {
|
|
2070
|
-
return this.engine?.getStats() ??
|
|
2079
|
+
return this.engine?.getStats() ?? F;
|
|
2071
2080
|
}
|
|
2072
2081
|
getQualityLevels() {
|
|
2073
2082
|
return this.engine?.getQualityLevels() ?? [];
|
|
@@ -2113,7 +2122,7 @@ var et = class {
|
|
|
2113
2122
|
let e = ++this.loadGen;
|
|
2114
2123
|
this.stopStats(), this.stopRefresh(), this.closeArming(), this.closeLiveState(), this.detachMedia?.(), this.engine?.destroy(), this.engine = null;
|
|
2115
2124
|
try {
|
|
2116
|
-
let t = await
|
|
2125
|
+
let t = await b({
|
|
2117
2126
|
...this.resolveOptions(),
|
|
2118
2127
|
waitForLive: this.opts.waitForLive ? this.buildWaitConfig(e) : void 0
|
|
2119
2128
|
});
|
|
@@ -2174,7 +2183,7 @@ function $(e) {
|
|
|
2174
2183
|
}
|
|
2175
2184
|
//#endregion
|
|
2176
2185
|
//#region src/embed/host.ts
|
|
2177
|
-
function
|
|
2186
|
+
function nt(e) {
|
|
2178
2187
|
if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
|
|
2179
2188
|
if (!e.src) throw Error("mountEmbed: opts.src is required");
|
|
2180
2189
|
if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
|
|
@@ -2233,8 +2242,8 @@ function tt(e) {
|
|
|
2233
2242
|
}
|
|
2234
2243
|
//#endregion
|
|
2235
2244
|
//#region src/embed/page.ts
|
|
2236
|
-
var
|
|
2237
|
-
function
|
|
2245
|
+
var rt = /^[A-Za-z0-9:_.-]{1,128}$/;
|
|
2246
|
+
function it() {
|
|
2238
2247
|
let e = window.HAVIK_EMBED_CONFIG;
|
|
2239
2248
|
if (e) return {
|
|
2240
2249
|
baseUrl: e.baseUrl ?? "",
|
|
@@ -2256,9 +2265,9 @@ function rt() {
|
|
|
2256
2265
|
waitForLive: !0
|
|
2257
2266
|
};
|
|
2258
2267
|
}
|
|
2259
|
-
async function
|
|
2260
|
-
let e =
|
|
2261
|
-
|
|
2268
|
+
async function at() {
|
|
2269
|
+
let e = it();
|
|
2270
|
+
st();
|
|
2262
2271
|
let n = document.createElement("video");
|
|
2263
2272
|
n.setAttribute("playsinline", ""), n.muted = e.muted ?? !0, n.style.cssText = "width:100%;height:100%;object-fit:contain;background:#000;aspect-ratio:auto", document.body.appendChild(n);
|
|
2264
2273
|
let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
|
|
@@ -2313,7 +2322,7 @@ async function it() {
|
|
|
2313
2322
|
code: e.code,
|
|
2314
2323
|
message: e.message
|
|
2315
2324
|
})), e.on("autoplayblocked", () => {
|
|
2316
|
-
i({ type: "oddin:autoplayblocked" }),
|
|
2325
|
+
i({ type: "oddin:autoplayblocked" }), ot(n, e);
|
|
2317
2326
|
});
|
|
2318
2327
|
}, s = async (r) => {
|
|
2319
2328
|
a?.destroy(), a = null;
|
|
@@ -2379,7 +2388,7 @@ async function it() {
|
|
|
2379
2388
|
a?.retry();
|
|
2380
2389
|
break;
|
|
2381
2390
|
case "oddin:load":
|
|
2382
|
-
typeof e.matchUrn == "string" &&
|
|
2391
|
+
typeof e.matchUrn == "string" && rt.test(e.matchUrn) && s(e.matchUrn);
|
|
2383
2392
|
break;
|
|
2384
2393
|
case "oddin:destroy":
|
|
2385
2394
|
c();
|
|
@@ -2393,18 +2402,18 @@ async function it() {
|
|
|
2393
2402
|
protocol: 1
|
|
2394
2403
|
});
|
|
2395
2404
|
}
|
|
2396
|
-
function
|
|
2405
|
+
function ot(e, t) {
|
|
2397
2406
|
if (document.getElementById("havik-tap")) return;
|
|
2398
2407
|
let n = document.createElement("button");
|
|
2399
2408
|
n.id = "havik-tap", n.textContent = "▶ Tap to play", n.style.cssText = "position:fixed;inset:0;width:100%;height:100%;border:0;background:rgba(0,0,0,.55);color:#fff;font:600 18px system-ui,sans-serif;cursor:pointer;z-index:9", n.addEventListener("click", () => {
|
|
2400
2409
|
n.remove(), t.play();
|
|
2401
2410
|
}), document.body.appendChild(n);
|
|
2402
2411
|
}
|
|
2403
|
-
function
|
|
2412
|
+
function st() {
|
|
2404
2413
|
let e = document.createElement("style");
|
|
2405
2414
|
e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
|
|
2406
2415
|
}
|
|
2407
2416
|
//#endregion
|
|
2408
|
-
export { W as ODDIN_THEME, t as PlaybackError, K as applyTheme,
|
|
2417
|
+
export { W as ODDIN_THEME, t as PlaybackError, K as applyTheme, at as bootEmbed, fe as clearDeviceId, Q as createPlayer, T as deriveEventsBaseUrl, M as detectDrmSupport, C as fetchCatalog, ce as fetchTournament, ue as flattenMatches, O as getDeviceId, P as isLowLatencyManifest, $ as isOddinMessage, me as licenseRequestHeaders, nt as mountEmbed, v as pollToLive, b as resolveStream, w as subscribeLiveState, de as watchStatus };
|
|
2409
2418
|
|
|
2410
2419
|
//# sourceMappingURL=index.js.map
|