@oddin-gg/havik-player 1.14.0 → 1.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/havik-player.global.js +14 -14
- package/dist/havik-player.global.js.map +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +377 -315
- package/dist/index.js.map +1 -1
- package/embed/index.html +1 -1
- package/examples/vanilla.html +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import e from "hls.js";
|
|
1
|
+
import e, { CapLevelController as t } from "hls.js";
|
|
2
2
|
//#region src/core/errors.ts
|
|
3
|
-
var
|
|
3
|
+
var n = class extends Error {
|
|
4
4
|
code;
|
|
5
5
|
httpStatus;
|
|
6
6
|
retryAfterMs;
|
|
@@ -11,7 +11,7 @@ var t = class extends Error {
|
|
|
11
11
|
super(n), this.name = "PlaybackError", this.code = e, this.httpStatus = t, this.retryAfterMs = r?.retryAfterMs, this.requestId = r?.requestId, this.serverCode = r?.serverCode, this.liveStartsAtMs = r?.liveStartsAtMs, r?.cause !== void 0 && (this.cause = r.cause);
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
|
-
function
|
|
14
|
+
function r(e) {
|
|
15
15
|
switch (e) {
|
|
16
16
|
case 400: return "INVALID_URN";
|
|
17
17
|
case 401: return "UNAUTHORIZED";
|
|
@@ -24,101 +24,102 @@ function n(e) {
|
|
|
24
24
|
default: return "INTERNAL";
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
-
function
|
|
27
|
+
function i(e) {
|
|
28
28
|
return e === 401 ? "UNAUTHORIZED" : e === 403 ? "FORBIDDEN" : "DRM_CLIENT";
|
|
29
29
|
}
|
|
30
|
-
var
|
|
30
|
+
var a = /* @__PURE__ */ new Set([
|
|
31
31
|
"TOO_EARLY",
|
|
32
32
|
"UNAVAILABLE",
|
|
33
33
|
"RATE_LIMITED",
|
|
34
34
|
"NETWORK"
|
|
35
35
|
]);
|
|
36
|
-
function
|
|
37
|
-
return
|
|
36
|
+
function o(e) {
|
|
37
|
+
return a.has(e);
|
|
38
38
|
}
|
|
39
39
|
//#endregion
|
|
40
40
|
//#region src/core/types.ts
|
|
41
|
-
async function
|
|
41
|
+
async function s(e) {
|
|
42
42
|
let t = typeof e == "function" ? await e() : e;
|
|
43
43
|
if (!t || !t.apiKey) throw Error("havik-player: credential is missing an apiKey");
|
|
44
44
|
return t;
|
|
45
45
|
}
|
|
46
46
|
//#endregion
|
|
47
47
|
//#region src/core/http.ts
|
|
48
|
-
function
|
|
48
|
+
function c(e, t) {
|
|
49
49
|
let r = e.replace(/\/+$/, ""), i;
|
|
50
50
|
try {
|
|
51
|
-
i = new URL(r +
|
|
51
|
+
i = new URL(r + t);
|
|
52
52
|
} catch {
|
|
53
|
-
throw new
|
|
53
|
+
throw new n("INTERNAL", 0, `invalid baseUrl: ${JSON.stringify(e)}`);
|
|
54
54
|
}
|
|
55
|
-
if (i.protocol !== "https:" && i.protocol !== "http:") throw new
|
|
55
|
+
if (i.protocol !== "https:" && i.protocol !== "http:") throw new n("INTERNAL", 0, `baseUrl must be http(s), got: ${JSON.stringify(e)}`);
|
|
56
56
|
return i.toString();
|
|
57
57
|
}
|
|
58
|
-
function
|
|
58
|
+
function l(e) {
|
|
59
59
|
if (!e) return;
|
|
60
60
|
let t = Number(e);
|
|
61
61
|
if (Number.isFinite(t)) return Math.max(0, Math.round(t * 1e3));
|
|
62
62
|
let n = Date.parse(e);
|
|
63
63
|
if (!Number.isNaN(n)) return Math.max(0, n - Date.now());
|
|
64
64
|
}
|
|
65
|
-
async function
|
|
66
|
-
let r = await
|
|
65
|
+
async function u(e, t) {
|
|
66
|
+
let r = await s(t.credential), i = t.headers ?? new Headers();
|
|
67
|
+
if (t.apiHeaders) for (let [e, n] of Object.entries(t.apiHeaders)) i.has(e) || i.set(e, n);
|
|
67
68
|
i.set("x-api-key", r.apiKey);
|
|
68
69
|
try {
|
|
69
70
|
return await fetch(e, {
|
|
70
|
-
method:
|
|
71
|
+
method: t.method ?? "GET",
|
|
71
72
|
headers: i,
|
|
72
|
-
body:
|
|
73
|
-
signal:
|
|
73
|
+
body: t.body,
|
|
74
|
+
signal: t.signal,
|
|
74
75
|
cache: "no-store",
|
|
75
76
|
credentials: "omit",
|
|
76
77
|
mode: "cors"
|
|
77
78
|
});
|
|
78
79
|
} catch (e) {
|
|
79
|
-
throw
|
|
80
|
+
throw t.signal?.aborted ? new n("ABORTED", 0, "request aborted", { cause: e }) : new n("NETWORK", 0, `network request failed: ${d(e)}`, { cause: e });
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
|
-
function
|
|
83
|
+
function d(e) {
|
|
83
84
|
return e instanceof Error ? e.message : String(e);
|
|
84
85
|
}
|
|
85
86
|
//#endregion
|
|
86
87
|
//#region src/core/waitForLive.ts
|
|
87
|
-
var
|
|
88
|
+
var f = 1e3, p = 3e4, m = 6e5, h = 3e5, g = 1e3, ee = {
|
|
88
89
|
TOO_EARLY: "tooEarly",
|
|
89
90
|
UNAVAILABLE: "unavailable",
|
|
90
91
|
RATE_LIMITED: "rateLimited",
|
|
91
92
|
NETWORK: "network"
|
|
92
93
|
};
|
|
93
|
-
function
|
|
94
|
-
let t = e === !0 || e === !1 ? {} : e, n = Math.max(
|
|
94
|
+
function te(e) {
|
|
95
|
+
let t = e === !0 || e === !1 ? {} : e, n = Math.max(g, t.floorMs ?? f), r = Math.max(n, t.ceilMs ?? p), i = Math.max(0, t.imminentMs ?? m), a = Math.max(r, t.sanityFloorMs ?? h);
|
|
95
96
|
return {
|
|
96
97
|
timeoutMs: t.timeoutMs,
|
|
97
98
|
floorMs: n,
|
|
98
99
|
ceilMs: r,
|
|
99
100
|
imminentMs: i,
|
|
100
101
|
sanityFloorMs: a,
|
|
101
|
-
kickoffMs:
|
|
102
|
+
kickoffMs: ne(t.kickoffAt),
|
|
102
103
|
jitter: t.jitter ?? !0,
|
|
103
104
|
onState: t.onState
|
|
104
105
|
};
|
|
105
106
|
}
|
|
106
|
-
function
|
|
107
|
+
function ne(e) {
|
|
107
108
|
if (e == null) return;
|
|
108
109
|
if (typeof e == "number") return Number.isFinite(e) ? e : void 0;
|
|
109
110
|
let t = Date.parse(e);
|
|
110
111
|
return Number.isFinite(t) ? t : void 0;
|
|
111
112
|
}
|
|
112
|
-
function
|
|
113
|
+
function _(e, t, n) {
|
|
113
114
|
return Math.min(n, Math.max(t, e));
|
|
114
115
|
}
|
|
115
|
-
function
|
|
116
|
+
function re(e) {
|
|
116
117
|
return Math.round(e * (1 + .15 * Math.random()));
|
|
117
118
|
}
|
|
118
|
-
function
|
|
119
|
+
function ie(e, t, n, r) {
|
|
119
120
|
let i = r ?? n.kickoffMs, a = i == null ? void 0 : Math.max(0, i - t), o = Math.max(n.floorMs, e ?? 0);
|
|
120
121
|
if (a == null || a <= n.imminentMs) return {
|
|
121
|
-
delay:
|
|
122
|
+
delay: _(e ?? n.floorMs, o, n.ceilMs),
|
|
122
123
|
ttkMs: a
|
|
123
124
|
};
|
|
124
125
|
let s = Math.min(Math.floor(a / 10), n.sanityFloorMs, a);
|
|
@@ -127,61 +128,62 @@ function re(e, t, n, r) {
|
|
|
127
128
|
ttkMs: a
|
|
128
129
|
};
|
|
129
130
|
}
|
|
130
|
-
function
|
|
131
|
+
function ae(e, t) {
|
|
131
132
|
return new Promise((r, i) => {
|
|
132
|
-
if (
|
|
133
|
-
i(new
|
|
133
|
+
if (t?.aborted) {
|
|
134
|
+
i(new n("ABORTED", 0, "wait-for-live aborted"));
|
|
134
135
|
return;
|
|
135
136
|
}
|
|
136
137
|
let a = () => {
|
|
137
|
-
clearTimeout(o), i(new
|
|
138
|
+
clearTimeout(o), i(new n("ABORTED", 0, "wait-for-live aborted"));
|
|
138
139
|
}, o = setTimeout(() => {
|
|
139
|
-
|
|
140
|
+
t?.removeEventListener("abort", a), r();
|
|
140
141
|
}, e);
|
|
141
|
-
|
|
142
|
+
t?.addEventListener("abort", a, { once: !0 });
|
|
142
143
|
});
|
|
143
144
|
}
|
|
144
|
-
async function v(e,
|
|
145
|
-
let i =
|
|
145
|
+
async function v(e, t, r) {
|
|
146
|
+
let i = te(t), a = Date.now(), s = i.timeoutMs == null ? Infinity : a + i.timeoutMs, c = 0, l;
|
|
146
147
|
for (;;) {
|
|
147
|
-
if (r?.aborted) throw new
|
|
148
|
+
if (r?.aborted) throw new n("ABORTED", 0, "wait-for-live aborted");
|
|
148
149
|
try {
|
|
149
150
|
return await e();
|
|
150
151
|
} catch (e) {
|
|
151
|
-
if (!(e instanceof
|
|
152
|
+
if (!(e instanceof n) || !o(e.code)) throw e;
|
|
152
153
|
c += 1, e.liveStartsAtMs != null && Number.isFinite(e.liveStartsAtMs) && (l = e.liveStartsAtMs);
|
|
153
|
-
let
|
|
154
|
-
if (i.jitter && (f =
|
|
154
|
+
let t = Date.now(), u = ie(e.retryAfterMs, t, i, l), d = u.ttkMs, f = u.delay;
|
|
155
|
+
if (i.jitter && (f = re(f)), t + f > s) throw new n("TIMEOUT", e.httpStatus, `timed out after ${i.timeoutMs}ms waiting for the stream to go live`, {
|
|
155
156
|
retryAfterMs: e.retryAfterMs,
|
|
156
157
|
cause: e
|
|
157
158
|
});
|
|
158
159
|
i.onState?.({
|
|
159
|
-
phase:
|
|
160
|
+
phase: ee[e.code] ?? "unavailable",
|
|
160
161
|
retryInMs: f,
|
|
161
162
|
attempt: c,
|
|
162
|
-
elapsedMs:
|
|
163
|
+
elapsedMs: t - a,
|
|
163
164
|
ttkMs: d
|
|
164
|
-
}), await
|
|
165
|
+
}), await ae(f, r);
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
168
|
}
|
|
168
169
|
//#endregion
|
|
169
170
|
//#region src/core/resolve.ts
|
|
170
171
|
async function y(e) {
|
|
171
|
-
if (!e.matchUrn || typeof e.matchUrn != "string") throw new
|
|
172
|
-
let
|
|
172
|
+
if (!e.matchUrn || typeof e.matchUrn != "string") throw new n("INVALID_URN", 0, "resolveStream: a non-empty matchUrn is required");
|
|
173
|
+
let t = await u(c(e.baseUrl, `/v1/playback/${encodeURIComponent(e.matchUrn)}`), {
|
|
173
174
|
credential: e.credential,
|
|
174
|
-
signal: e.signal
|
|
175
|
+
signal: e.signal,
|
|
176
|
+
apiHeaders: e.apiHeaders
|
|
175
177
|
});
|
|
176
|
-
if (!
|
|
177
|
-
return
|
|
178
|
+
if (!t.ok) throw await oe(t);
|
|
179
|
+
return se(await t.json(), e.matchUrn);
|
|
178
180
|
}
|
|
179
181
|
function b(e) {
|
|
180
182
|
let t = () => y(e);
|
|
181
183
|
return e.waitForLive ? v(t, e.waitForLive, e.signal) : t();
|
|
182
184
|
}
|
|
183
|
-
async function
|
|
184
|
-
let
|
|
185
|
+
async function oe(e) {
|
|
186
|
+
let t = l(e.headers.get("retry-after")), i = e.headers.get("x-request-id") ?? e.headers.get("x-amzn-requestid") ?? void 0, a = e.statusText || `HTTP ${e.status}`, o, s;
|
|
185
187
|
try {
|
|
186
188
|
let t = await e.json();
|
|
187
189
|
if (t?.error?.message && (a = t.error.message), t?.error?.code && (o = t.error.code), typeof t?.liveStartsAt == "string") {
|
|
@@ -189,14 +191,14 @@ async function ae(e) {
|
|
|
189
191
|
Number.isFinite(e) && (s = e);
|
|
190
192
|
}
|
|
191
193
|
} catch {}
|
|
192
|
-
return new
|
|
193
|
-
retryAfterMs:
|
|
194
|
+
return new n(r(e.status), e.status, a, {
|
|
195
|
+
retryAfterMs: t,
|
|
194
196
|
requestId: i,
|
|
195
197
|
serverCode: o,
|
|
196
198
|
liveStartsAtMs: s
|
|
197
199
|
});
|
|
198
200
|
}
|
|
199
|
-
var
|
|
201
|
+
var x = /* @__PURE__ */ new Set([
|
|
200
202
|
"matchUrn",
|
|
201
203
|
"protocol",
|
|
202
204
|
"drmEnabled",
|
|
@@ -207,61 +209,62 @@ var oe = /* @__PURE__ */ new Set([
|
|
|
207
209
|
"expiresAt",
|
|
208
210
|
"analytics"
|
|
209
211
|
]);
|
|
210
|
-
function
|
|
212
|
+
function S(e, t) {
|
|
211
213
|
let r;
|
|
212
214
|
try {
|
|
213
|
-
r = new URL(
|
|
215
|
+
r = new URL(t);
|
|
214
216
|
} catch {
|
|
215
|
-
throw new
|
|
217
|
+
throw new n("INTERNAL", 0, `playback ${e} is not a valid URL`);
|
|
216
218
|
}
|
|
217
|
-
if (r.protocol !== "https:" && r.protocol !== "http:") throw new
|
|
218
|
-
return
|
|
219
|
+
if (r.protocol !== "https:" && r.protocol !== "http:") throw new n("INTERNAL", 0, `playback ${e} has a disallowed scheme: ${r.protocol}`);
|
|
220
|
+
return t;
|
|
219
221
|
}
|
|
220
|
-
function
|
|
221
|
-
if (!e.manifestUrl) throw new
|
|
222
|
+
function se(e, t) {
|
|
223
|
+
if (!e.manifestUrl) throw new n("INTERNAL", 0, "playback response is missing manifestUrl");
|
|
222
224
|
let r = e.drmEnabled ?? !!e.drm, i = {
|
|
223
|
-
matchUrn: e.matchUrn ??
|
|
225
|
+
matchUrn: e.matchUrn ?? t,
|
|
224
226
|
protocol: "hls",
|
|
225
227
|
drmEnabled: r,
|
|
226
|
-
manifestUrl:
|
|
228
|
+
manifestUrl: S("manifestUrl", e.manifestUrl),
|
|
227
229
|
serverTime: e.serverTime ?? ""
|
|
228
230
|
};
|
|
229
231
|
if (e.liveStartsAt && (i.liveStartsAt = e.liveStartsAt), typeof e.analytics?.sid == "string" && e.analytics.sid && (i.analytics = { sid: e.analytics.sid }), r && e.drm) {
|
|
230
232
|
let t = {};
|
|
231
|
-
e.drm.widevine?.licenseUrl && (t.widevine = { licenseUrl:
|
|
232
|
-
licenseUrl:
|
|
233
|
-
certificateUrl:
|
|
233
|
+
e.drm.widevine?.licenseUrl && (t.widevine = { licenseUrl: S("widevine licenseUrl", e.drm.widevine.licenseUrl) }), e.drm.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && (t.fairplay = {
|
|
234
|
+
licenseUrl: S("fairplay licenseUrl", e.drm.fairplay.licenseUrl),
|
|
235
|
+
certificateUrl: S("fairplay certificateUrl", e.drm.fairplay.certificateUrl)
|
|
234
236
|
}), (t.widevine || t.fairplay) && (i.drm = t);
|
|
235
237
|
}
|
|
236
238
|
let a = {};
|
|
237
|
-
for (let [t, n] of Object.entries(e))
|
|
239
|
+
for (let [t, n] of Object.entries(e)) x.has(t) || (a[t] = n);
|
|
238
240
|
return Object.keys(a).length > 0 && (i.extensions = a), i;
|
|
239
241
|
}
|
|
240
242
|
//#endregion
|
|
241
243
|
//#region src/core/catalog.ts
|
|
242
|
-
async function
|
|
243
|
-
let
|
|
244
|
-
e.status &&
|
|
245
|
-
let i =
|
|
244
|
+
async function ce(e) {
|
|
245
|
+
let t = new URLSearchParams();
|
|
246
|
+
e.status && t.set("status", Array.isArray(e.status) ? e.status.join(",") : e.status), e.sport && t.set("sport", e.sport);
|
|
247
|
+
let i = t.toString(), a = c(e.baseUrl, "/v1/catalog" + (i ? `?${i}` : "")), o = new Headers();
|
|
246
248
|
e.etag && o.set("If-None-Match", e.etag);
|
|
247
|
-
let
|
|
249
|
+
let s = await u(a, {
|
|
248
250
|
credential: e.credential,
|
|
249
251
|
signal: e.signal,
|
|
250
|
-
headers: o
|
|
252
|
+
headers: o,
|
|
253
|
+
apiHeaders: e.apiHeaders
|
|
251
254
|
});
|
|
252
|
-
if (
|
|
255
|
+
if (s.status === 304) return {
|
|
253
256
|
tournaments: [],
|
|
254
257
|
etag: e.etag,
|
|
255
258
|
notModified: !0
|
|
256
259
|
};
|
|
257
|
-
if (!
|
|
260
|
+
if (!s.ok) throw new n(r(s.status), s.status, `catalog request failed (HTTP ${s.status})`);
|
|
258
261
|
return {
|
|
259
|
-
tournaments:
|
|
260
|
-
etag:
|
|
262
|
+
tournaments: le(await s.json()),
|
|
263
|
+
etag: s.headers.get("etag") ?? void 0,
|
|
261
264
|
notModified: !1
|
|
262
265
|
};
|
|
263
266
|
}
|
|
264
|
-
function
|
|
267
|
+
function le(e) {
|
|
265
268
|
return (e.tournaments ?? []).map((e) => {
|
|
266
269
|
let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
|
|
267
270
|
return {
|
|
@@ -281,13 +284,14 @@ function se(e) {
|
|
|
281
284
|
};
|
|
282
285
|
});
|
|
283
286
|
}
|
|
284
|
-
async function
|
|
285
|
-
let
|
|
287
|
+
async function ue(e) {
|
|
288
|
+
let t = c(e.baseUrl, `/v1/tournaments/${encodeURIComponent(e.urn)}`), i = new Headers();
|
|
286
289
|
e.etag && i.set("If-None-Match", e.etag);
|
|
287
|
-
let a = await
|
|
290
|
+
let a = await u(t, {
|
|
288
291
|
credential: e.credential,
|
|
289
292
|
signal: e.signal,
|
|
290
|
-
headers: i
|
|
293
|
+
headers: i,
|
|
294
|
+
apiHeaders: e.apiHeaders
|
|
291
295
|
});
|
|
292
296
|
if (a.status === 304) return {
|
|
293
297
|
etag: e.etag,
|
|
@@ -298,15 +302,15 @@ async function ce(e) {
|
|
|
298
302
|
notModified: !1,
|
|
299
303
|
notFound: !0
|
|
300
304
|
};
|
|
301
|
-
if (!a.ok) throw new
|
|
305
|
+
if (!a.ok) throw new n(r(a.status), a.status, `tournament request failed (HTTP ${a.status})`);
|
|
302
306
|
return {
|
|
303
|
-
tournament:
|
|
307
|
+
tournament: de(await a.json()),
|
|
304
308
|
etag: a.headers.get("etag") ?? void 0,
|
|
305
309
|
notModified: !1,
|
|
306
310
|
notFound: !1
|
|
307
311
|
};
|
|
308
312
|
}
|
|
309
|
-
function
|
|
313
|
+
function de(e) {
|
|
310
314
|
let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
|
|
311
315
|
return {
|
|
312
316
|
urn: t,
|
|
@@ -324,54 +328,55 @@ function le(e) {
|
|
|
324
328
|
}))
|
|
325
329
|
};
|
|
326
330
|
}
|
|
327
|
-
function
|
|
331
|
+
function fe(e) {
|
|
328
332
|
return e.tournaments.flatMap((e) => e.matches);
|
|
329
333
|
}
|
|
330
334
|
//#endregion
|
|
331
335
|
//#region src/core/poll.ts
|
|
332
|
-
function
|
|
333
|
-
let
|
|
334
|
-
|
|
336
|
+
function pe(e) {
|
|
337
|
+
let t = Math.max(2e3, e.intervalMs ?? 1e4), i = new AbortController(), a, o, s, l = !1, d = () => {
|
|
338
|
+
l || (a = setTimeout(f, t));
|
|
335
339
|
}, f = async () => {
|
|
336
340
|
try {
|
|
337
|
-
let
|
|
341
|
+
let t = c(e.baseUrl, `/v1/matches/${encodeURIComponent(e.matchUrn)}`), a = new Headers();
|
|
338
342
|
o && a.set("If-None-Match", o);
|
|
339
|
-
let
|
|
343
|
+
let l = await u(t, {
|
|
340
344
|
credential: e.credential,
|
|
341
345
|
signal: i.signal,
|
|
342
|
-
headers: a
|
|
346
|
+
headers: a,
|
|
347
|
+
apiHeaders: e.apiHeaders
|
|
343
348
|
});
|
|
344
|
-
if (
|
|
345
|
-
if (!
|
|
346
|
-
o =
|
|
347
|
-
let
|
|
348
|
-
i !==
|
|
349
|
-
matchUrn:
|
|
350
|
-
matchName:
|
|
349
|
+
if (l.status !== 304) {
|
|
350
|
+
if (!l.ok) throw new n(r(l.status), l.status, `match status request failed (HTTP ${l.status})`);
|
|
351
|
+
o = l.headers.get("etag") ?? o;
|
|
352
|
+
let t = await l.json(), i = t.status ?? "upcoming";
|
|
353
|
+
i !== s && (s = i, e.onChange({
|
|
354
|
+
matchUrn: t.matchUrn ?? e.matchUrn,
|
|
355
|
+
matchName: t.matchName,
|
|
351
356
|
status: i,
|
|
352
|
-
datePlannedStart:
|
|
357
|
+
datePlannedStart: t.datePlannedStart
|
|
353
358
|
}));
|
|
354
359
|
}
|
|
355
|
-
} catch (
|
|
356
|
-
i.signal.aborted || e.onError?.(
|
|
360
|
+
} catch (t) {
|
|
361
|
+
i.signal.aborted || e.onError?.(t instanceof n ? t : new n("NETWORK", 0, String(t)));
|
|
357
362
|
} finally {
|
|
358
363
|
d();
|
|
359
364
|
}
|
|
360
365
|
};
|
|
361
366
|
return f(), { stop() {
|
|
362
|
-
|
|
367
|
+
l = !0, i.abort(), a && clearTimeout(a);
|
|
363
368
|
} };
|
|
364
369
|
}
|
|
365
370
|
//#endregion
|
|
366
371
|
//#region src/core/events.ts
|
|
367
|
-
function
|
|
372
|
+
function C(e) {
|
|
368
373
|
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) => {
|
|
369
374
|
if (r.signal.aborted) return t();
|
|
370
375
|
let n = setTimeout(t, e);
|
|
371
376
|
r.signal.addEventListener("abort", () => {
|
|
372
377
|
clearTimeout(n), t();
|
|
373
378
|
}, { once: !0 });
|
|
374
|
-
}),
|
|
379
|
+
}), o = (t) => {
|
|
375
380
|
e.onAlive?.();
|
|
376
381
|
let n = "message", r = [];
|
|
377
382
|
for (let e of t.split("\n")) {
|
|
@@ -380,19 +385,19 @@ function w(e) {
|
|
|
380
385
|
let i = t.indexOf(":"), a = i < 0 ? t : t.slice(0, i), o = i < 0 ? "" : t.slice(i + 1);
|
|
381
386
|
o.startsWith(" ") && (o = o.slice(1)), a === "event" ? n = o : a === "data" && r.push(o);
|
|
382
387
|
}
|
|
383
|
-
if (
|
|
388
|
+
if (n === "state" && r.length !== 0) try {
|
|
384
389
|
let t = JSON.parse(r.join("\n"));
|
|
385
390
|
t && typeof t.state == "string" && e.onState(t);
|
|
386
391
|
} catch {}
|
|
387
392
|
}, l = /\r\n\r\n|\n\n|\r\r/, u = async (e, t) => {
|
|
388
|
-
let n = e.getReader(), r = new TextDecoder(), a = "",
|
|
393
|
+
let n = e.getReader(), r = new TextDecoder(), a = "", s = !1;
|
|
389
394
|
try {
|
|
390
395
|
for (;;) {
|
|
391
|
-
let { value: e, done:
|
|
392
|
-
if (
|
|
393
|
-
!
|
|
396
|
+
let { value: e, done: c } = await n.read();
|
|
397
|
+
if (c) return;
|
|
398
|
+
!s && e && e.length > 0 && (s = !0, t()), a += r.decode(e, { stream: !0 });
|
|
394
399
|
let u;
|
|
395
|
-
for (; (u = l.exec(a)) !== null;)
|
|
400
|
+
for (; (u = l.exec(a)) !== null;) o(a.slice(0, u.index)), a = a.slice(u.index + u[0].length);
|
|
396
401
|
if (i) return;
|
|
397
402
|
}
|
|
398
403
|
} finally {
|
|
@@ -400,12 +405,12 @@ function w(e) {
|
|
|
400
405
|
}
|
|
401
406
|
};
|
|
402
407
|
return (async () => {
|
|
403
|
-
let
|
|
408
|
+
let o = t;
|
|
404
409
|
for (; !i;) {
|
|
405
410
|
try {
|
|
406
|
-
let n = await
|
|
411
|
+
let n = await s(e.credential);
|
|
407
412
|
if (i) return;
|
|
408
|
-
let a =
|
|
413
|
+
let a = c(e.eventsBaseUrl, `/v1/events/${encodeURIComponent(e.matchUrn)}`), l = await fetch(a, {
|
|
409
414
|
method: "GET",
|
|
410
415
|
headers: {
|
|
411
416
|
"x-api-key": n.apiKey,
|
|
@@ -418,20 +423,20 @@ function w(e) {
|
|
|
418
423
|
});
|
|
419
424
|
if (i) return;
|
|
420
425
|
!l.ok || !l.body ? e.onError?.(/* @__PURE__ */ Error(`live-state subscribe failed: HTTP ${l.status}`)) : await u(l.body, () => {
|
|
421
|
-
|
|
426
|
+
o = t;
|
|
422
427
|
});
|
|
423
428
|
} catch (t) {
|
|
424
429
|
if (i || r.signal.aborted) return;
|
|
425
430
|
e.onError?.(t);
|
|
426
431
|
}
|
|
427
432
|
if (i) return;
|
|
428
|
-
await a(
|
|
433
|
+
await a(o), o = Math.min(n, o * 2);
|
|
429
434
|
}
|
|
430
435
|
})(), { close() {
|
|
431
436
|
i || (i = !0, r.abort());
|
|
432
437
|
} };
|
|
433
438
|
}
|
|
434
|
-
function
|
|
439
|
+
function w(e) {
|
|
435
440
|
try {
|
|
436
441
|
let t = new URL(e);
|
|
437
442
|
return t.hostname = `events.${t.hostname}`, t.pathname = "", t.search = "", t.origin;
|
|
@@ -441,27 +446,27 @@ function T(e) {
|
|
|
441
446
|
}
|
|
442
447
|
//#endregion
|
|
443
448
|
//#region src/core/drm/deviceId.ts
|
|
444
|
-
var
|
|
445
|
-
function
|
|
446
|
-
if (
|
|
449
|
+
var T = "havik.player.deviceId", E;
|
|
450
|
+
function D() {
|
|
451
|
+
if (E) return E;
|
|
447
452
|
try {
|
|
448
|
-
let e = globalThis.localStorage?.getItem(
|
|
449
|
-
if (e) return
|
|
453
|
+
let e = globalThis.localStorage?.getItem(T);
|
|
454
|
+
if (e) return E = e, e;
|
|
450
455
|
} catch {}
|
|
451
|
-
let e =
|
|
452
|
-
|
|
456
|
+
let e = he();
|
|
457
|
+
E = e;
|
|
453
458
|
try {
|
|
454
|
-
globalThis.localStorage?.setItem(
|
|
459
|
+
globalThis.localStorage?.setItem(T, e);
|
|
455
460
|
} catch {}
|
|
456
461
|
return e;
|
|
457
462
|
}
|
|
458
|
-
function
|
|
459
|
-
|
|
463
|
+
function me() {
|
|
464
|
+
E = void 0;
|
|
460
465
|
try {
|
|
461
|
-
globalThis.localStorage?.removeItem(
|
|
466
|
+
globalThis.localStorage?.removeItem(T);
|
|
462
467
|
} catch {}
|
|
463
468
|
}
|
|
464
|
-
function
|
|
469
|
+
function he() {
|
|
465
470
|
let e = globalThis.crypto;
|
|
466
471
|
if (e && typeof e.randomUUID == "function") return e.randomUUID();
|
|
467
472
|
let t = /* @__PURE__ */ new Uint8Array(16);
|
|
@@ -473,22 +478,22 @@ function pe() {
|
|
|
473
478
|
}
|
|
474
479
|
//#endregion
|
|
475
480
|
//#region src/core/drm/headers.ts
|
|
476
|
-
function
|
|
481
|
+
function ge(e, t, n = {}) {
|
|
477
482
|
let r = {
|
|
478
483
|
"x-api-key": t.apiKey,
|
|
479
484
|
"X-Match-Urn": e.matchUrn,
|
|
480
|
-
"X-Device-Id": n.deviceId ??
|
|
485
|
+
"X-Device-Id": n.deviceId ?? D(),
|
|
481
486
|
"Content-Type": "application/octet-stream"
|
|
482
487
|
};
|
|
483
488
|
return n.userId && (r["X-User-Id"] = n.userId), r;
|
|
484
489
|
}
|
|
485
490
|
//#endregion
|
|
486
491
|
//#region src/core/drm/support.ts
|
|
487
|
-
var
|
|
492
|
+
var _e = 5e3, O = "video/mp4; codecs=\"avc1.42E01E\"", k = "audio/mp4; codecs=\"mp4a.40.2\"", ve = {
|
|
488
493
|
widevine: ["com.widevine.alpha"],
|
|
489
494
|
fairplay: ["com.apple.fps"]
|
|
490
495
|
};
|
|
491
|
-
function
|
|
496
|
+
function A(e) {
|
|
492
497
|
return e === "fairplay" ? [{
|
|
493
498
|
initDataTypes: [
|
|
494
499
|
"sinf",
|
|
@@ -496,24 +501,24 @@ function _e(e) {
|
|
|
496
501
|
"cenc"
|
|
497
502
|
],
|
|
498
503
|
videoCapabilities: [{
|
|
499
|
-
contentType:
|
|
504
|
+
contentType: O,
|
|
500
505
|
robustness: "",
|
|
501
506
|
encryptionScheme: "cbcs"
|
|
502
507
|
}],
|
|
503
508
|
audioCapabilities: [{
|
|
504
|
-
contentType:
|
|
509
|
+
contentType: k,
|
|
505
510
|
robustness: "",
|
|
506
511
|
encryptionScheme: "cbcs"
|
|
507
512
|
}]
|
|
508
513
|
}] : [{
|
|
509
514
|
initDataTypes: ["cenc"],
|
|
510
515
|
videoCapabilities: [{
|
|
511
|
-
contentType:
|
|
516
|
+
contentType: O,
|
|
512
517
|
robustness: "SW_SECURE_DECODE",
|
|
513
518
|
encryptionScheme: "cbcs"
|
|
514
519
|
}],
|
|
515
520
|
audioCapabilities: [{
|
|
516
|
-
contentType:
|
|
521
|
+
contentType: k,
|
|
517
522
|
robustness: "SW_SECURE_CRYPTO",
|
|
518
523
|
encryptionScheme: "cbcs"
|
|
519
524
|
}]
|
|
@@ -530,7 +535,7 @@ function j(e, t) {
|
|
|
530
535
|
});
|
|
531
536
|
}
|
|
532
537
|
async function M(e = {}) {
|
|
533
|
-
let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ??
|
|
538
|
+
let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? _e, r = {};
|
|
534
539
|
if (typeof window < "u" && window.isSecureContext === !1) return {
|
|
535
540
|
verdict: "insecure-context",
|
|
536
541
|
detail: r
|
|
@@ -542,7 +547,7 @@ async function M(e = {}) {
|
|
|
542
547
|
};
|
|
543
548
|
let a = !1, o = !1, s = Date.now() + n, c = () => Math.max(1, s - Date.now());
|
|
544
549
|
for (let e of t) {
|
|
545
|
-
let t =
|
|
550
|
+
let t = ve[e];
|
|
546
551
|
if (!t) {
|
|
547
552
|
r[e] = "unknown system";
|
|
548
553
|
continue;
|
|
@@ -553,7 +558,7 @@ async function M(e = {}) {
|
|
|
553
558
|
break;
|
|
554
559
|
}
|
|
555
560
|
try {
|
|
556
|
-
return await j((await j(i(n,
|
|
561
|
+
return await j((await j(i(n, A(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
|
|
557
562
|
verdict: "supported",
|
|
558
563
|
system: e,
|
|
559
564
|
detail: r
|
|
@@ -587,7 +592,7 @@ var F = { droppedFrames: 0 };
|
|
|
587
592
|
function I(e) {
|
|
588
593
|
return e.getVideoPlaybackQuality?.()?.droppedVideoFrames ?? 0;
|
|
589
594
|
}
|
|
590
|
-
function
|
|
595
|
+
function ye(e, t) {
|
|
591
596
|
let n = [
|
|
592
597
|
["playing", () => t("playing")],
|
|
593
598
|
["waiting", () => t("buffering")],
|
|
@@ -604,7 +609,7 @@ function ve(e, t) {
|
|
|
604
609
|
}
|
|
605
610
|
//#endregion
|
|
606
611
|
//#region src/core/engine/native.ts
|
|
607
|
-
function
|
|
612
|
+
function be(e) {
|
|
608
613
|
try {
|
|
609
614
|
let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
|
|
610
615
|
return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
|
|
@@ -634,12 +639,12 @@ var z = class {
|
|
|
634
639
|
this.deps = e;
|
|
635
640
|
}
|
|
636
641
|
async load() {
|
|
637
|
-
let { video: e, descriptor:
|
|
638
|
-
if (
|
|
639
|
-
if (!
|
|
642
|
+
let { video: e, descriptor: t, emit: r } = this.deps;
|
|
643
|
+
if (t.drmEnabled) {
|
|
644
|
+
if (!t.drm?.fairplay) {
|
|
640
645
|
r({
|
|
641
646
|
type: "error",
|
|
642
|
-
error: new
|
|
647
|
+
error: new n("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"),
|
|
643
648
|
fatal: !0
|
|
644
649
|
});
|
|
645
650
|
return;
|
|
@@ -648,25 +653,25 @@ var z = class {
|
|
|
648
653
|
}
|
|
649
654
|
this.onLoaded = () => r({ type: "ready" }), this.onError = () => r({
|
|
650
655
|
type: "error",
|
|
651
|
-
error: new
|
|
656
|
+
error: new n("INTERNAL", 0, "native HLS playback error"),
|
|
652
657
|
fatal: !0
|
|
653
|
-
}), e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src =
|
|
658
|
+
}), e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = xe(t.manifestUrl, t.analytics?.sid), e.load();
|
|
654
659
|
}
|
|
655
660
|
armFairPlay() {
|
|
656
|
-
let { video: e, descriptor:
|
|
661
|
+
let { video: e, descriptor: t, credential: r, deviceId: a, userId: o, emit: s } = this.deps, c = t.drm?.fairplay;
|
|
657
662
|
if (!c) return;
|
|
658
663
|
this.fairplayLicenseUrl = c.licenseUrl;
|
|
659
|
-
let l = (e,
|
|
664
|
+
let l = (e, t, r) => s({
|
|
660
665
|
type: "error",
|
|
661
|
-
error: new
|
|
662
|
-
fatal:
|
|
666
|
+
error: new n(i(t), t, e),
|
|
667
|
+
fatal: r
|
|
663
668
|
});
|
|
664
|
-
this.onEncrypted = (
|
|
665
|
-
let
|
|
666
|
-
!
|
|
669
|
+
this.onEncrypted = (n) => {
|
|
670
|
+
let i = n;
|
|
671
|
+
!i.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV = be(i.initData), (async () => {
|
|
667
672
|
try {
|
|
668
|
-
let
|
|
669
|
-
initDataTypes: [
|
|
673
|
+
let n = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
|
|
674
|
+
initDataTypes: [i.initDataType],
|
|
670
675
|
videoCapabilities: [{
|
|
671
676
|
contentType: "application/vnd.apple.mpegurl",
|
|
672
677
|
robustness: ""
|
|
@@ -674,37 +679,37 @@ var z = class {
|
|
|
674
679
|
distinctiveIdentifier: "not-allowed",
|
|
675
680
|
persistentState: "not-allowed",
|
|
676
681
|
sessionTypes: ["temporary"]
|
|
677
|
-
}])).createMediaKeys(), s = await fetch(c.certificateUrl, { headers: { "x-api-key":
|
|
682
|
+
}])).createMediaKeys(), s = await fetch(c.certificateUrl, { headers: { "x-api-key": r.apiKey } });
|
|
678
683
|
if (!s.ok) {
|
|
679
684
|
this.fairplayArmed = !1, l(`FairPlay certificate request failed: ${s.status}`, s.status, !0);
|
|
680
685
|
return;
|
|
681
686
|
}
|
|
682
|
-
await
|
|
683
|
-
let u =
|
|
687
|
+
await n.setServerCertificate(await s.arrayBuffer()), await e.setMediaKeys(n);
|
|
688
|
+
let u = n.createSession();
|
|
684
689
|
this.keySession = u, u.addEventListener("message", (e) => {
|
|
685
690
|
(async () => {
|
|
686
691
|
try {
|
|
687
|
-
let
|
|
692
|
+
let n = await fetch(this.fairplayLicenseUrl ?? c.licenseUrl, {
|
|
688
693
|
method: "POST",
|
|
689
694
|
body: e.message,
|
|
690
695
|
headers: {
|
|
691
|
-
"x-api-key":
|
|
692
|
-
"X-Match-Urn":
|
|
696
|
+
"x-api-key": r.apiKey,
|
|
697
|
+
"X-Match-Urn": t.matchUrn,
|
|
693
698
|
"X-Device-Id": a,
|
|
694
699
|
...o ? { "X-User-Id": o } : {},
|
|
695
700
|
...this.fairplayKeyIV ? { "X-Key-Iv": this.fairplayKeyIV } : {}
|
|
696
701
|
}
|
|
697
702
|
});
|
|
698
|
-
if (!
|
|
699
|
-
l(`FairPlay license request failed: ${
|
|
703
|
+
if (!n.ok) {
|
|
704
|
+
l(`FairPlay license request failed: ${n.status}`, n.status, !0);
|
|
700
705
|
return;
|
|
701
706
|
}
|
|
702
|
-
await u.update(new Uint8Array(await
|
|
707
|
+
await u.update(new Uint8Array(await n.arrayBuffer()));
|
|
703
708
|
} catch (e) {
|
|
704
709
|
l(`FairPlay license request failed: ${String(e)}`, 0, !0);
|
|
705
710
|
}
|
|
706
711
|
})();
|
|
707
|
-
}), await u.generateRequest(
|
|
712
|
+
}), await u.generateRequest(i.initDataType, i.initData);
|
|
708
713
|
} catch (e) {
|
|
709
714
|
this.fairplayArmed = !1, l(`FairPlay key session failed: ${String(e)}`, 0, !0);
|
|
710
715
|
}
|
|
@@ -778,25 +783,25 @@ var z = class {
|
|
|
778
783
|
} catch {}
|
|
779
784
|
}
|
|
780
785
|
};
|
|
781
|
-
function
|
|
786
|
+
function xe(e, t) {
|
|
782
787
|
return t ? `${e}${e.includes("?") ? "&" : "?"}CMCD=${encodeURIComponent(`sid="${t}"`)}` : e;
|
|
783
788
|
}
|
|
784
789
|
//#endregion
|
|
785
790
|
//#region src/core/engine/hls.ts
|
|
786
|
-
function
|
|
791
|
+
function Se() {
|
|
787
792
|
return e.isSupported();
|
|
788
793
|
}
|
|
789
|
-
var
|
|
790
|
-
function
|
|
794
|
+
var Ce = 3, we = 5, Te = 3, Ee = 3e4, De = 12e3, Oe = 3e3, B = 6e3, ke = 4e3, Ae = 2, je = 5, Me = 2e3, Ne = 3e4, Pe = 2, Fe = 9, Ie = .5;
|
|
795
|
+
function Le(e) {
|
|
791
796
|
if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
|
|
792
797
|
delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
|
|
793
798
|
return;
|
|
794
799
|
}
|
|
795
|
-
e.liveSyncDuration ??=
|
|
796
|
-
let t = Math.max(12, e.liveSyncDuration +
|
|
797
|
-
(e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t),
|
|
800
|
+
e.liveSyncDuration ??= Pe;
|
|
801
|
+
let t = Math.max(12, e.liveSyncDuration + Fe);
|
|
802
|
+
(e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Re(e, e.liveSyncDuration);
|
|
798
803
|
}
|
|
799
|
-
function
|
|
804
|
+
function Re(e, t) {
|
|
800
805
|
let n = (e, t, n) => Math.min(n, Math.max(t, e));
|
|
801
806
|
e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
|
|
802
807
|
}
|
|
@@ -829,27 +834,47 @@ var V = class {
|
|
|
829
834
|
chaseSuspended = !1;
|
|
830
835
|
savedLiveMaxLatency;
|
|
831
836
|
savedMaxSyncRate;
|
|
837
|
+
capObserver = null;
|
|
838
|
+
capTimer = null;
|
|
839
|
+
sizeCapLevel = null;
|
|
840
|
+
bitrateCapLevel = -1;
|
|
841
|
+
sizeCapManaged = !0;
|
|
842
|
+
dprMedia = null;
|
|
843
|
+
onDprChange = null;
|
|
832
844
|
constructor(e) {
|
|
833
845
|
this.deps = e, this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl;
|
|
834
846
|
}
|
|
835
847
|
async load() {
|
|
836
|
-
let { descriptor: e, snapToLiveOnRefocus:
|
|
848
|
+
let { descriptor: e, snapToLiveOnRefocus: t, video: r, emit: i } = this.deps, a = L(r) && R(), o = !!e.drm?.widevine?.licenseUrl && !a;
|
|
837
849
|
if (e.drmEnabled && !o) {
|
|
838
850
|
i({
|
|
839
851
|
type: "error",
|
|
840
|
-
error: new
|
|
852
|
+
error: new n("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)"),
|
|
841
853
|
fatal: !0
|
|
842
854
|
});
|
|
843
855
|
return;
|
|
844
856
|
}
|
|
845
|
-
this.startHls(),
|
|
857
|
+
this.startHls(), t !== !1 && typeof document < "u" && (this.onVisibility = () => {
|
|
846
858
|
let e = this.hls;
|
|
847
859
|
if (document.visibilityState !== "visible" || !e) return;
|
|
848
860
|
let t = e.liveSyncPosition;
|
|
849
861
|
if (typeof t != "number") return;
|
|
850
862
|
let n = typeof e.targetLatency == "number" && e.targetLatency > 0 ? e.targetLatency : 3;
|
|
851
863
|
t - r.currentTime > Math.max(n * 3, 8) && (r.currentTime = t);
|
|
852
|
-
}, document.addEventListener("visibilitychange", this.onVisibility)), this.onSeeking = () => this.clampOverSeek(), r.addEventListener("seeking", this.onSeeking);
|
|
864
|
+
}, document.addEventListener("visibilitychange", this.onVisibility)), this.onSeeking = () => this.clampOverSeek(), r.addEventListener("seeking", this.onSeeking), this.sizeCapManaged && typeof ResizeObserver < "u" && (this.capObserver = new ResizeObserver(() => this.onMediaResize()), this.capObserver.observe(r), this.watchDprChanges());
|
|
865
|
+
}
|
|
866
|
+
watchDprChanges() {
|
|
867
|
+
if (typeof matchMedia != "function") return;
|
|
868
|
+
this.unwatchDprChanges();
|
|
869
|
+
let e = typeof self < "u" && self.devicePixelRatio || 1, t = matchMedia(`(resolution: ${e}dppx)`);
|
|
870
|
+
if (typeof t.addEventListener != "function") return;
|
|
871
|
+
let n = () => {
|
|
872
|
+
this.onMediaResize(), this.watchDprChanges();
|
|
873
|
+
};
|
|
874
|
+
t.addEventListener("change", n), this.dprMedia = t, this.onDprChange = n;
|
|
875
|
+
}
|
|
876
|
+
unwatchDprChanges() {
|
|
877
|
+
this.dprMedia && this.onDprChange && this.dprMedia.removeEventListener("change", this.onDprChange), this.dprMedia = null, this.onDprChange = null;
|
|
853
878
|
}
|
|
854
879
|
clampOverSeek() {
|
|
855
880
|
let e = this.hls;
|
|
@@ -857,7 +882,7 @@ var V = class {
|
|
|
857
882
|
let t = e.liveSyncPosition;
|
|
858
883
|
if (typeof t != "number" || !Number.isFinite(t)) return;
|
|
859
884
|
let { video: n } = this.deps;
|
|
860
|
-
n.currentTime <= t +
|
|
885
|
+
n.currentTime <= t + Ie || (n.currentTime = t);
|
|
861
886
|
}
|
|
862
887
|
startHls() {
|
|
863
888
|
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 = {
|
|
@@ -868,7 +893,7 @@ var V = class {
|
|
|
868
893
|
maxBufferHole: .5,
|
|
869
894
|
maxLiveSyncPlaybackRate: 1.5,
|
|
870
895
|
abrEwmaDefaultEstimate: 1e6,
|
|
871
|
-
capLevelToPlayerSize: !
|
|
896
|
+
capLevelToPlayerSize: !1,
|
|
872
897
|
...u ? {
|
|
873
898
|
emeEnabled: !0,
|
|
874
899
|
drmSystems: { ...this.licenseUrl ? { "com.widevine.alpha": { licenseUrl: this.licenseUrl } } : {} },
|
|
@@ -893,14 +918,14 @@ var V = class {
|
|
|
893
918
|
} } : {},
|
|
894
919
|
...c
|
|
895
920
|
};
|
|
896
|
-
|
|
921
|
+
Le(d), this.sizeCapManaged = d.capLevelToPlayerSize !== !0;
|
|
897
922
|
let f = new e(d);
|
|
898
923
|
this.hls = f, f.on(e.Events.MANIFEST_PARSED, () => {
|
|
899
|
-
this.mediaRecoveries = 0, this.networkRecoveries = 0, l({ type: "ready" });
|
|
924
|
+
this.mediaRecoveries = 0, this.networkRecoveries = 0, this.applySizeCap(), l({ type: "ready" });
|
|
900
925
|
}), f.on(e.Events.FRAG_BUFFERED, (e, t) => {
|
|
901
926
|
t?.frag?.type === "main" && (this.mediaRecoveries > 0 || this.networkRecoveries > 0 || this.licenseRecoveries > 0 || this.starvationKicks > 0) && !this.recoveryResetTimer && (this.recoveryResetTimer = setTimeout(() => {
|
|
902
927
|
this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.recoveryResetTimer = null;
|
|
903
|
-
},
|
|
928
|
+
}, Ee));
|
|
904
929
|
}), f.on(e.Events.FRAG_LOADING, (e, t) => {
|
|
905
930
|
t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
|
|
906
931
|
}), f.on(e.Events.FRAG_LOADED, (e, t) => {
|
|
@@ -916,7 +941,7 @@ var V = class {
|
|
|
916
941
|
}));
|
|
917
942
|
}), f.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.endWatchdog = setInterval(() => {
|
|
918
943
|
this.checkEndStale(), this.checkStarvation();
|
|
919
|
-
},
|
|
944
|
+
}, Oe), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
|
|
920
945
|
this.renditionSwitches += 1, l({
|
|
921
946
|
type: "qualitychange",
|
|
922
947
|
index: t.level
|
|
@@ -946,15 +971,54 @@ var V = class {
|
|
|
946
971
|
}
|
|
947
972
|
setMaxBitrate(e) {
|
|
948
973
|
let t = this.hls;
|
|
949
|
-
if (
|
|
950
|
-
|
|
951
|
-
|
|
974
|
+
if (t) {
|
|
975
|
+
if (e == null) this.bitrateCapLevel = -1;
|
|
976
|
+
else {
|
|
977
|
+
let n = -1;
|
|
978
|
+
t.levels.forEach((t, r) => {
|
|
979
|
+
t.bitrate <= e && (n = r);
|
|
980
|
+
}), this.bitrateCapLevel = n;
|
|
981
|
+
}
|
|
982
|
+
this.pushCap();
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
computeSizeCapTarget() {
|
|
986
|
+
let e = this.hls;
|
|
987
|
+
if (!e || e.levels.length === 0) return null;
|
|
988
|
+
let { video: n } = this.deps, r = n.getBoundingClientRect(), i = r.width, a = r.height;
|
|
989
|
+
if (!i && !a && (i = n.width || 0, a = n.height || 0), !(i > 0) || !(a > 0)) return null;
|
|
990
|
+
let o = e.config ?? {}, s = 1;
|
|
991
|
+
return !o.ignoreDevicePixelRatio && typeof self < "u" && self.devicePixelRatio > 0 && (s = self.devicePixelRatio), s = Math.min(s, o.maxDevicePixelRatio ?? Infinity), t.getMaxLevelByMediaSize(e.levels, i * s, a * s);
|
|
992
|
+
}
|
|
993
|
+
onMediaResize() {
|
|
994
|
+
if (!this.sizeCapManaged) return;
|
|
995
|
+
let e = this.computeSizeCapTarget();
|
|
996
|
+
if (e == null) return;
|
|
997
|
+
if (this.sizeCapLevel == null) {
|
|
998
|
+
this.applySizeCap();
|
|
952
999
|
return;
|
|
953
1000
|
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
}
|
|
1001
|
+
if (e === this.sizeCapLevel) {
|
|
1002
|
+
this.capTimer &&= (clearTimeout(this.capTimer), null);
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
this.capTimer && clearTimeout(this.capTimer);
|
|
1006
|
+
let t = e > this.sizeCapLevel ? Me : Ne;
|
|
1007
|
+
this.capTimer = setTimeout(() => {
|
|
1008
|
+
this.capTimer = null, this.computeSizeCapTarget() === e ? this.applySizeCap() : this.onMediaResize();
|
|
1009
|
+
}, t);
|
|
1010
|
+
}
|
|
1011
|
+
applySizeCap() {
|
|
1012
|
+
if (!this.sizeCapManaged) return;
|
|
1013
|
+
this.capTimer &&= (clearTimeout(this.capTimer), null);
|
|
1014
|
+
let e = this.computeSizeCapTarget();
|
|
1015
|
+
e != null && (this.sizeCapLevel = e, this.pushCap());
|
|
1016
|
+
}
|
|
1017
|
+
pushCap() {
|
|
1018
|
+
let e = this.hls;
|
|
1019
|
+
if (!e) return;
|
|
1020
|
+
let t = this.sizeCapLevel ?? -1, n = this.bitrateCapLevel;
|
|
1021
|
+
e.autoLevelCapping = t < 0 ? n : n < 0 ? t : Math.min(t, n);
|
|
958
1022
|
}
|
|
959
1023
|
getAudioTracks() {
|
|
960
1024
|
return this.hls ? this.hls.audioTracks.map((e) => ({
|
|
@@ -1020,7 +1084,7 @@ var V = class {
|
|
|
1020
1084
|
return;
|
|
1021
1085
|
}
|
|
1022
1086
|
let e = Date.now() - this.lastAdvanceMs;
|
|
1023
|
-
!this.chaseSuspended && e > B && this.suspendChasing(), e >
|
|
1087
|
+
!this.chaseSuspended && e > B && this.suspendChasing(), e > De && this.emitEnded("stalled");
|
|
1024
1088
|
}
|
|
1025
1089
|
checkStarvation() {
|
|
1026
1090
|
let e = this.hls, { video: t } = this.deps;
|
|
@@ -1029,12 +1093,12 @@ var V = class {
|
|
|
1029
1093
|
this.starvedTicks = 0;
|
|
1030
1094
|
return;
|
|
1031
1095
|
}
|
|
1032
|
-
let n = this.lastAdvanceMs !== 0 && Date.now() - this.lastAdvanceMs < B, r = this.lastFragLoadingMs !== 0 && Date.now() - this.lastFragLoadingMs >
|
|
1096
|
+
let n = this.lastAdvanceMs !== 0 && Date.now() - this.lastAdvanceMs < B, r = this.lastFragLoadingMs !== 0 && Date.now() - this.lastFragLoadingMs > ke;
|
|
1033
1097
|
if (!(t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused && n && r)) {
|
|
1034
1098
|
this.starvedTicks = 0;
|
|
1035
1099
|
return;
|
|
1036
1100
|
}
|
|
1037
|
-
if (!(++this.starvedTicks <
|
|
1101
|
+
if (!(++this.starvedTicks < Ae) && (this.starvedTicks = 0, !(this.starvationKicks >= je))) {
|
|
1038
1102
|
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");
|
|
1039
1103
|
try {
|
|
1040
1104
|
e.stopLoad(), e.startLoad(-1);
|
|
@@ -1068,7 +1132,7 @@ var V = class {
|
|
|
1068
1132
|
} catch {}
|
|
1069
1133
|
}
|
|
1070
1134
|
destroy() {
|
|
1071
|
-
if (this.clearRecoveryReset(), this.endWatchdog &&= (clearInterval(this.endWatchdog), null), this.onVisibility && typeof document < "u" && (document.removeEventListener("visibilitychange", this.onVisibility), this.onVisibility = null), this.onSeeking &&= (this.deps.video.removeEventListener("seeking", this.onSeeking), null), this.networkRetryTimer &&= (clearTimeout(this.networkRetryTimer), null), this.licenseRetryTimer &&= (clearTimeout(this.licenseRetryTimer), null), this.hls) {
|
|
1135
|
+
if (this.clearRecoveryReset(), this.capTimer &&= (clearTimeout(this.capTimer), null), this.capObserver &&= (this.capObserver.disconnect(), null), this.unwatchDprChanges(), this.endWatchdog &&= (clearInterval(this.endWatchdog), null), this.onVisibility && typeof document < "u" && (document.removeEventListener("visibilitychange", this.onVisibility), this.onVisibility = null), this.onSeeking &&= (this.deps.video.removeEventListener("seeking", this.onSeeking), null), this.networkRetryTimer &&= (clearTimeout(this.networkRetryTimer), null), this.licenseRetryTimer &&= (clearTimeout(this.licenseRetryTimer), null), this.hls) {
|
|
1072
1136
|
try {
|
|
1073
1137
|
this.hls.destroy();
|
|
1074
1138
|
} catch (e) {
|
|
@@ -1081,9 +1145,9 @@ var V = class {
|
|
|
1081
1145
|
this.recoveryResetTimer &&= (clearTimeout(this.recoveryResetTimer), null);
|
|
1082
1146
|
}
|
|
1083
1147
|
scheduleLicenseRetry(t) {
|
|
1084
|
-
if (
|
|
1148
|
+
if (t.details !== e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED && t.details !== e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED) return !1;
|
|
1085
1149
|
let n = t.response?.code ?? 0;
|
|
1086
|
-
if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >=
|
|
1150
|
+
if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Te) return !1;
|
|
1087
1151
|
this.licenseRecoveries += 1;
|
|
1088
1152
|
let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
|
|
1089
1153
|
return this.licenseRetryTimer = setTimeout(() => {
|
|
@@ -1111,7 +1175,7 @@ var V = class {
|
|
|
1111
1175
|
return;
|
|
1112
1176
|
}
|
|
1113
1177
|
if (t.type === e.ErrorTypes.NETWORK_ERROR) {
|
|
1114
|
-
if (this.networkRecoveries <
|
|
1178
|
+
if (this.networkRecoveries < we) {
|
|
1115
1179
|
this.networkRecoveries += 1;
|
|
1116
1180
|
let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
|
|
1117
1181
|
this.networkRetryTimer = setTimeout(() => {
|
|
@@ -1127,7 +1191,7 @@ var V = class {
|
|
|
1127
1191
|
return;
|
|
1128
1192
|
}
|
|
1129
1193
|
if (t.type === e.ErrorTypes.MEDIA_ERROR) {
|
|
1130
|
-
if (this.mediaRecoveries <
|
|
1194
|
+
if (this.mediaRecoveries < Ce) {
|
|
1131
1195
|
this.mediaRecoveries += 1, n.recoverMediaError();
|
|
1132
1196
|
return;
|
|
1133
1197
|
}
|
|
@@ -1145,11 +1209,11 @@ var V = class {
|
|
|
1145
1209
|
});
|
|
1146
1210
|
}
|
|
1147
1211
|
}
|
|
1148
|
-
toError(e,
|
|
1149
|
-
let
|
|
1150
|
-
return new t
|
|
1212
|
+
toError(e, t = "PLAYBACK") {
|
|
1213
|
+
let r = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
|
|
1214
|
+
return new n(t === "DRM" ? i(r) : r === 401 ? "UNAUTHORIZED" : r === 403 ? "FORBIDDEN" : "INTERNAL", r, `${t} error: ${a}${o ? ` (${o})` : ""}`);
|
|
1151
1215
|
}
|
|
1152
|
-
},
|
|
1216
|
+
}, ze = 15e3, Be = 100, H = 1, U = {
|
|
1153
1217
|
idle: "PLAYER_STATE_IDLE",
|
|
1154
1218
|
waiting: "PLAYER_STATE_WAITING",
|
|
1155
1219
|
loading: "PLAYER_STATE_LOADING",
|
|
@@ -1159,7 +1223,7 @@ var V = class {
|
|
|
1159
1223
|
ended: "PLAYER_STATE_ENDED",
|
|
1160
1224
|
error: "PLAYER_STATE_ERROR"
|
|
1161
1225
|
};
|
|
1162
|
-
function
|
|
1226
|
+
function Ve(e) {
|
|
1163
1227
|
try {
|
|
1164
1228
|
let t = new URL(e), [n, ...r] = t.hostname.split(".");
|
|
1165
1229
|
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);
|
|
@@ -1167,7 +1231,7 @@ function Re(e) {
|
|
|
1167
1231
|
return;
|
|
1168
1232
|
}
|
|
1169
1233
|
}
|
|
1170
|
-
var
|
|
1234
|
+
var He = class {
|
|
1171
1235
|
opts;
|
|
1172
1236
|
url;
|
|
1173
1237
|
now;
|
|
@@ -1202,7 +1266,7 @@ var ze = class {
|
|
|
1202
1266
|
constructor(e) {
|
|
1203
1267
|
this.opts = {
|
|
1204
1268
|
...e,
|
|
1205
|
-
heartbeatMs: e.heartbeatMs ??
|
|
1269
|
+
heartbeatMs: e.heartbeatMs ?? ze
|
|
1206
1270
|
}, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`, this.now = e.now ?? (() => Date.now()), this.bornMs = this.now();
|
|
1207
1271
|
}
|
|
1208
1272
|
start() {
|
|
@@ -1294,7 +1358,7 @@ var ze = class {
|
|
|
1294
1358
|
};
|
|
1295
1359
|
}
|
|
1296
1360
|
enqueue(e) {
|
|
1297
|
-
for (this.pending.push(this.wrap(e)); this.pending.length >
|
|
1361
|
+
for (this.pending.push(this.wrap(e)); this.pending.length > Be;) {
|
|
1298
1362
|
let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
|
|
1299
1363
|
this.pending.splice(e >= 0 ? e : 0, 1);
|
|
1300
1364
|
}
|
|
@@ -1316,7 +1380,7 @@ var ze = class {
|
|
|
1316
1380
|
keepalive: !0
|
|
1317
1381
|
}).catch(() => {});
|
|
1318
1382
|
}
|
|
1319
|
-
},
|
|
1383
|
+
}, Ue = "1.15.1", W = {
|
|
1320
1384
|
accent: "#E1B600",
|
|
1321
1385
|
accentText: "#1B1C23",
|
|
1322
1386
|
background: "#0e0f13",
|
|
@@ -1349,15 +1413,15 @@ function K(e, t) {
|
|
|
1349
1413
|
}
|
|
1350
1414
|
//#endregion
|
|
1351
1415
|
//#region src/ui/styles.ts
|
|
1352
|
-
var q = "havik-player-styles",
|
|
1353
|
-
function
|
|
1416
|
+
var q = "havik-player-styles", We = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n.havik-spinner{width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n";
|
|
1417
|
+
function Ge(e = document) {
|
|
1354
1418
|
if (e.getElementById(q)) return;
|
|
1355
1419
|
let t = e.createElement("style");
|
|
1356
|
-
t.id = q, t.textContent =
|
|
1420
|
+
t.id = q, t.textContent = We, e.head.appendChild(t);
|
|
1357
1421
|
}
|
|
1358
1422
|
//#endregion
|
|
1359
1423
|
//#region src/ui/icons.ts
|
|
1360
|
-
var
|
|
1424
|
+
var Ke = {
|
|
1361
1425
|
play: "<path d=\"M8 5v14l11-7z\"/>",
|
|
1362
1426
|
pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
|
|
1363
1427
|
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\"/>",
|
|
@@ -1371,20 +1435,20 @@ var Ue = {
|
|
|
1371
1435
|
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\"/>"
|
|
1372
1436
|
};
|
|
1373
1437
|
function J(e) {
|
|
1374
|
-
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${
|
|
1438
|
+
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${Ke[e]}</svg>`;
|
|
1375
1439
|
}
|
|
1376
1440
|
//#endregion
|
|
1377
1441
|
//#region src/ui/controls.ts
|
|
1378
|
-
var
|
|
1442
|
+
var qe = 3e3, Je = /* @__PURE__ */ new Set([
|
|
1379
1443
|
"GONE",
|
|
1380
1444
|
"NOT_FOUND",
|
|
1381
1445
|
"INVALID_URN",
|
|
1382
1446
|
"UNAUTHORIZED"
|
|
1383
1447
|
]);
|
|
1384
|
-
function
|
|
1385
|
-
return new
|
|
1448
|
+
function Ye(e) {
|
|
1449
|
+
return new Xe(e);
|
|
1386
1450
|
}
|
|
1387
|
-
var
|
|
1451
|
+
var Xe = class {
|
|
1388
1452
|
root;
|
|
1389
1453
|
video;
|
|
1390
1454
|
player;
|
|
@@ -1424,7 +1488,7 @@ var qe = class {
|
|
|
1424
1488
|
scrubbing = !1;
|
|
1425
1489
|
offFns = [];
|
|
1426
1490
|
constructor(e) {
|
|
1427
|
-
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ??
|
|
1491
|
+
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? qe, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.errorMessage = e.errorMessage, Ge(this.doc), this.root.classList.add("havik-player"), this.root.hasAttribute("tabindex") || (this.root.tabIndex = 0), this.root.setAttribute("role", "region"), this.root.setAttribute("aria-label", "Video player");
|
|
1428
1492
|
let t = (e, t, n) => {
|
|
1429
1493
|
let r = this.doc.createElement(e);
|
|
1430
1494
|
if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
|
|
@@ -1480,7 +1544,7 @@ var qe = class {
|
|
|
1480
1544
|
this.setVolumeFill(t), this.player.setVolume(t), t > 0 && e.muted && this.player.setMuted(!1);
|
|
1481
1545
|
}), this.on(this.liveGo, "click", () => this.player.seekToLive()), this.on(this.pipBtn, "click", () => void this.togglePip()), this.on(this.fsBtn, "click", () => void this.toggleFullscreen()), this.on(this.ccBtn, "click", () => this.toggleMenu("cc")), this.on(this.audioBtn, "click", () => this.toggleMenu("audio")), this.on(this.qualityBtn, "click", () => this.toggleMenu("quality")), this.on(this.seek, "pointerdown", (e) => this.onSeekDown(e)), this.on(this.seek, "keydown", (e) => this.onSeekKey(e)), this.on(e, "play", () => this.syncPlayPause()), this.on(e, "pause", () => this.syncPlayPause()), this.on(e, "volumechange", () => this.syncVolume()), this.on(e, "timeupdate", () => this.syncProgress()), this.on(e, "progress", () => this.syncProgress()), this.on(e, "durationchange", () => this.syncProgress()), this.offFns.push(this.player.on("statechange", () => this.syncState())), this.offFns.push(this.player.on("ready", () => this.refreshTracks())), this.offFns.push(this.player.on("qualitychange", () => this.onTracksChanged())), this.offFns.push(this.player.on("audiotrackchange", () => this.onTracksChanged())), this.offFns.push(this.player.on("texttrackchange", () => this.onTracksChanged())), this.offFns.push(this.player.on("pipchange", (e) => this.syncPip(e))), this.offFns.push(this.player.on("stats", () => this.syncLive())), this.offFns.push(this.player.on("error", (e) => {
|
|
1482
1546
|
this.lastError = e, this.statusKind = null, this.syncState();
|
|
1483
|
-
})), this.on(this.root, "pointermove", () => this.poke()), this.on(this.root, "pointerleave", () => this.scheduleHide(0)), this.on(this.root, "focusin", () => this.poke()), this.on(this.root, "keydown", (e) => this.onKey(e)), this.on(this.doc, "pointerdown", (e) => this.onDocPointer(e), { capture: !0 }), this.on(this.doc, "fullscreenchange", () => this.syncFullscreen()), this.syncState();
|
|
1547
|
+
})), this.on(this.root, "pointermove", () => this.poke()), this.on(this.root, "pointerleave", () => this.scheduleHide(0)), this.on(this.root, "focusin", () => this.poke()), this.on(this.root, "keydown", (e) => this.onKey(e)), this.on(this.doc, "pointerdown", (e) => this.onDocPointer(e), { capture: !0 }), this.on(this.doc, "fullscreenchange", () => this.syncFullscreen()), this.on(this.doc, "webkitfullscreenchange", () => this.syncFullscreen()), this.on(e, "webkitbeginfullscreen", () => this.syncFullscreen()), this.on(e, "webkitendfullscreen", () => this.syncFullscreen()), this.syncState();
|
|
1484
1548
|
}
|
|
1485
1549
|
togglePlay() {
|
|
1486
1550
|
this.video.paused || this.video.ended ? this.player.play() : this.player.pause();
|
|
@@ -1511,7 +1575,7 @@ var qe = class {
|
|
|
1511
1575
|
e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = J("play"), e.addEventListener("click", () => void this.player.play()), t.append(e);
|
|
1512
1576
|
} else {
|
|
1513
1577
|
let n = this.doc.createElement("div");
|
|
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 && !
|
|
1578
|
+
if (n.className = "havik-status__msg", n.textContent = e === "ended" ? this.endedMessage : e === "waiting" ? "Starting soon…" : this.errorMessage ?? this.lastError?.message ?? "Playback unavailable", t.append(n), e === "error" && this.lastError && !Je.has(this.lastError.code)) {
|
|
1515
1579
|
let e = this.doc.createElement("button");
|
|
1516
1580
|
e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${J("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), t.append(e);
|
|
1517
1581
|
}
|
|
@@ -1602,17 +1666,17 @@ var qe = class {
|
|
|
1602
1666
|
renderQualityMenu() {
|
|
1603
1667
|
let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
|
|
1604
1668
|
n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
|
|
1605
|
-
for (let n of e) this.menu.append(this.menuItem(
|
|
1669
|
+
for (let n of e) this.menu.append(this.menuItem(Ze(n), t === n.index, () => this.player.setQuality(n.index)));
|
|
1606
1670
|
}
|
|
1607
1671
|
renderAudioMenu() {
|
|
1608
1672
|
let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
|
|
1609
1673
|
t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
|
|
1610
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
1674
|
+
for (let t of e) this.menu.append(this.menuItem(X(t), t.default, () => this.player.setAudioTrack(t.id)));
|
|
1611
1675
|
}
|
|
1612
1676
|
renderCcMenu() {
|
|
1613
1677
|
let e = this.player.getTextTracks(), t = this.doc.createElement("div");
|
|
1614
1678
|
t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
|
|
1615
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
1679
|
+
for (let t of e) this.menu.append(this.menuItem(Qe(t), !1, () => this.player.setTextTrack(t.id)));
|
|
1616
1680
|
}
|
|
1617
1681
|
onTracksChanged() {
|
|
1618
1682
|
this.openMenu && this.renderMenu(this.openMenu);
|
|
@@ -1631,14 +1695,18 @@ var qe = class {
|
|
|
1631
1695
|
this.pipBtn.setAttribute("aria-pressed", e ? "true" : "false");
|
|
1632
1696
|
}
|
|
1633
1697
|
async toggleFullscreen() {
|
|
1634
|
-
let e = this.root, t = this.doc;
|
|
1698
|
+
let e = this.root, t = this.doc, n = this.video;
|
|
1635
1699
|
try {
|
|
1636
|
-
|
|
1700
|
+
if (typeof e.requestFullscreen == "function" || typeof e.webkitRequestFullscreen == "function") {
|
|
1701
|
+
t.fullscreenElement ?? t.webkitFullscreenElement ? typeof t.exitFullscreen == "function" ? await t.exitFullscreen() : t.webkitExitFullscreen?.() : typeof e.requestFullscreen == "function" ? await e.requestFullscreen() : e.webkitRequestFullscreen?.();
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
n.webkitSupportsFullscreen && typeof n.webkitEnterFullscreen == "function" && (n.webkitDisplayingFullscreen ? n.webkitExitFullscreen?.() : n.webkitEnterFullscreen());
|
|
1637
1705
|
} catch {}
|
|
1638
1706
|
}
|
|
1639
1707
|
syncFullscreen() {
|
|
1640
|
-
let e = this.doc.fullscreenElement === this.root;
|
|
1641
|
-
this.setIcon(this.fsBtn,
|
|
1708
|
+
let e = this.doc, t = this.video, n = (e.fullscreenElement ?? e.webkitFullscreenElement) === this.root || t.webkitDisplayingFullscreen === !0;
|
|
1709
|
+
this.setIcon(this.fsBtn, n ? "fullscreenExit" : "fullscreen"), this.fsBtn.setAttribute("aria-label", n ? "Exit fullscreen" : "Fullscreen");
|
|
1642
1710
|
}
|
|
1643
1711
|
poke() {
|
|
1644
1712
|
this.show(), this.scheduleHide();
|
|
@@ -1718,27 +1786,27 @@ function Y(e) {
|
|
|
1718
1786
|
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");
|
|
1719
1787
|
return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
|
|
1720
1788
|
}
|
|
1721
|
-
function
|
|
1789
|
+
function Ze(e) {
|
|
1722
1790
|
return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
|
|
1723
1791
|
}
|
|
1724
|
-
function
|
|
1792
|
+
function X(e) {
|
|
1725
1793
|
return e.name || e.lang || `Track ${e.id}`;
|
|
1726
1794
|
}
|
|
1727
|
-
function
|
|
1795
|
+
function Qe(e) {
|
|
1728
1796
|
return e.name || e.lang || `Track ${e.id}`;
|
|
1729
1797
|
}
|
|
1730
1798
|
//#endregion
|
|
1731
1799
|
//#region src/managed/index.ts
|
|
1732
|
-
var
|
|
1800
|
+
var $e = 6e3, Z = 45e3, et = 2e3, tt = 3, nt = 2e3, rt = 48e4;
|
|
1733
1801
|
async function Q(e) {
|
|
1734
|
-
if (!e.video || typeof e.video.play != "function") throw new
|
|
1735
|
-
if (!e.baseUrl) throw new
|
|
1736
|
-
if (!e.matchUrn) throw new
|
|
1737
|
-
if (!e.credential) throw new
|
|
1738
|
-
let
|
|
1739
|
-
return await
|
|
1802
|
+
if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
|
|
1803
|
+
if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: baseUrl is required");
|
|
1804
|
+
if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
|
|
1805
|
+
if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
|
|
1806
|
+
let t = new it(e);
|
|
1807
|
+
return await t.start(), t;
|
|
1740
1808
|
}
|
|
1741
|
-
var
|
|
1809
|
+
var it = class {
|
|
1742
1810
|
opts;
|
|
1743
1811
|
abort = new AbortController();
|
|
1744
1812
|
listeners = {};
|
|
@@ -1772,19 +1840,19 @@ var tt = class {
|
|
|
1772
1840
|
}
|
|
1773
1841
|
async start() {
|
|
1774
1842
|
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");
|
|
1775
|
-
let e = ++this.loadGen,
|
|
1843
|
+
let e = ++this.loadGen, t = this.resolveOptions();
|
|
1776
1844
|
try {
|
|
1777
|
-
let
|
|
1778
|
-
if (await this.onResolved(
|
|
1845
|
+
let n = await y(t);
|
|
1846
|
+
if (await this.onResolved(n, e), e === this.loadGen && this._state === "error" && this._lastError) {
|
|
1779
1847
|
let e = this._lastError;
|
|
1780
1848
|
throw this.destroy(), e;
|
|
1781
1849
|
}
|
|
1782
|
-
} catch (
|
|
1783
|
-
if (this.opts.waitForLive &&
|
|
1784
|
-
this.setState("waiting"), this.beginArming(e,
|
|
1850
|
+
} catch (t) {
|
|
1851
|
+
if (this.opts.waitForLive && t instanceof n && o(t.code)) {
|
|
1852
|
+
this.setState("waiting"), this.beginArming(e, t.liveStartsAtMs);
|
|
1785
1853
|
return;
|
|
1786
1854
|
}
|
|
1787
|
-
throw
|
|
1855
|
+
throw t;
|
|
1788
1856
|
}
|
|
1789
1857
|
}
|
|
1790
1858
|
resolveOptions() {
|
|
@@ -1792,11 +1860,12 @@ var tt = class {
|
|
|
1792
1860
|
baseUrl: this.opts.baseUrl,
|
|
1793
1861
|
matchUrn: this.opts.matchUrn,
|
|
1794
1862
|
credential: this.opts.credential,
|
|
1795
|
-
signal: this.abort.signal
|
|
1863
|
+
signal: this.abort.signal,
|
|
1864
|
+
apiHeaders: this.opts.apiHeaders
|
|
1796
1865
|
};
|
|
1797
1866
|
}
|
|
1798
1867
|
beginArming(e, t) {
|
|
1799
|
-
let n = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ??
|
|
1868
|
+
let n = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? w(this.opts.baseUrl);
|
|
1800
1869
|
n ? this.armWithSse(e, n, t) : this.beginArmingByPolling(e);
|
|
1801
1870
|
}
|
|
1802
1871
|
beginArmingByPolling(e) {
|
|
@@ -1806,57 +1875,57 @@ var tt = class {
|
|
|
1806
1875
|
waitForLive: this.buildWaitConfig(e)
|
|
1807
1876
|
}).then((t) => {
|
|
1808
1877
|
if (!(this.destroyed || this.abort.signal.aborted || e !== this.loadGen)) return this.onResolved(t, e);
|
|
1809
|
-
}).catch((
|
|
1810
|
-
this.destroyed || this.abort.signal.aborted || e !== this.loadGen || this.fail(
|
|
1878
|
+
}).catch((t) => {
|
|
1879
|
+
this.destroyed || this.abort.signal.aborted || e !== this.loadGen || this.fail(t instanceof n ? t : new n("INTERNAL", 0, String(t)));
|
|
1811
1880
|
});
|
|
1812
1881
|
}
|
|
1813
|
-
armWithSse(e,
|
|
1814
|
-
let i = !1,
|
|
1882
|
+
armWithSse(e, t, r) {
|
|
1883
|
+
let i = !1, a = !1, s = new AbortController();
|
|
1815
1884
|
this.armFallbackCtrl = s, this.setState("waiting");
|
|
1816
1885
|
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);
|
|
1817
1886
|
setTimeout(() => {
|
|
1818
1887
|
i || this.destroyed || e !== this.loadGen || this.emit("waiting", {
|
|
1819
1888
|
phase: "tooEarly",
|
|
1820
|
-
retryInMs:
|
|
1889
|
+
retryInMs: Z,
|
|
1821
1890
|
attempt: 1,
|
|
1822
1891
|
elapsedMs: 0,
|
|
1823
1892
|
...d !== void 0 && d > Date.now() ? { ttkMs: d - Date.now() } : {}
|
|
1824
1893
|
});
|
|
1825
1894
|
}, 0);
|
|
1826
|
-
let f = () => this.destroyed || this.abort.signal.aborted || e !== this.loadGen, p = async (
|
|
1895
|
+
let f = () => this.destroyed || this.abort.signal.aborted || e !== this.loadGen, p = async (t) => {
|
|
1827
1896
|
if (!(i || f())) {
|
|
1828
1897
|
i = !0, this.closeArming(), s.abort();
|
|
1829
1898
|
try {
|
|
1830
|
-
await this.onResolved(
|
|
1899
|
+
await this.onResolved(t, e);
|
|
1831
1900
|
} catch (e) {
|
|
1832
1901
|
if (f()) return;
|
|
1833
|
-
this.fail(e instanceof
|
|
1902
|
+
this.fail(e instanceof n ? e : new n("INTERNAL", 0, String(e)));
|
|
1834
1903
|
}
|
|
1835
1904
|
}
|
|
1836
1905
|
}, m = () => {
|
|
1837
|
-
i ||
|
|
1906
|
+
i || a || f() || (a = !0, b({
|
|
1838
1907
|
...this.resolveOptions(),
|
|
1839
1908
|
signal: s.signal,
|
|
1840
1909
|
waitForLive: this.buildWaitConfig(e)
|
|
1841
1910
|
}).then((e) => p(e)).catch((e) => {
|
|
1842
|
-
i || f() || s.signal.aborted || (i = !0, this.closeArming(), this.fail(e instanceof
|
|
1911
|
+
i || f() || s.signal.aborted || (i = !0, this.closeArming(), this.fail(e instanceof n ? e : new n("INTERNAL", 0, String(e))));
|
|
1843
1912
|
}));
|
|
1844
1913
|
}, h = () => {
|
|
1845
1914
|
i || f() || y(this.resolveOptions()).then((e) => p(e)).catch((e) => {
|
|
1846
|
-
i || f() || e instanceof
|
|
1915
|
+
i || f() || e instanceof n && o(e.code) || (i = !0, this.closeArming(), s.abort(), this.fail(e instanceof n ? e : new n("INTERNAL", 0, String(e))));
|
|
1847
1916
|
});
|
|
1848
1917
|
}, g = (e) => {
|
|
1849
1918
|
this.armSseTimer && clearTimeout(this.armSseTimer), this.armSseTimer = setTimeout(() => {
|
|
1850
1919
|
!i && !f() && m();
|
|
1851
1920
|
}, e);
|
|
1852
1921
|
};
|
|
1853
|
-
g(
|
|
1854
|
-
eventsBaseUrl:
|
|
1922
|
+
g($e), this.armingSub = C({
|
|
1923
|
+
eventsBaseUrl: t,
|
|
1855
1924
|
matchUrn: this.opts.matchUrn,
|
|
1856
1925
|
credential: this.opts.credential,
|
|
1857
|
-
onAlive: () => g(
|
|
1926
|
+
onAlive: () => g(Z),
|
|
1858
1927
|
onState: (e) => {
|
|
1859
|
-
i || f() || (e.state === "live" || e.state === "gone") && setTimeout(h, Math.random() *
|
|
1928
|
+
i || f() || (e.state === "live" || e.state === "gone") && setTimeout(h, Math.random() * et);
|
|
1860
1929
|
},
|
|
1861
1930
|
onError: () => m()
|
|
1862
1931
|
});
|
|
@@ -1876,11 +1945,11 @@ var tt = class {
|
|
|
1876
1945
|
async onResolved(e, t) {
|
|
1877
1946
|
if (this.destroyed || t !== this.loadGen) return;
|
|
1878
1947
|
this._descriptor = e, this.endedHandled = !1;
|
|
1879
|
-
let n = await
|
|
1948
|
+
let n = await s(this.opts.credential);
|
|
1880
1949
|
if (this.destroyed || t !== this.loadGen) return;
|
|
1881
1950
|
this.setState("loading");
|
|
1882
|
-
let r = this.createEngine(e, n), i =
|
|
1883
|
-
this._state
|
|
1951
|
+
let r = this.createEngine(e, n), i = ye(this.opts.video, (e) => {
|
|
1952
|
+
this._state !== "ended" && this._state !== "error" && this.setState(e);
|
|
1884
1953
|
});
|
|
1885
1954
|
if (await r.load(), this.destroyed || t !== this.loadGen) {
|
|
1886
1955
|
i(), r.destroy();
|
|
@@ -1896,14 +1965,14 @@ var tt = class {
|
|
|
1896
1965
|
if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachSeekListener?.(), this.opts.analytics === !1) return;
|
|
1897
1966
|
let r = e.analytics?.sid;
|
|
1898
1967
|
if (!r) return;
|
|
1899
|
-
let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ??
|
|
1968
|
+
let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? Ve(this.opts.baseUrl);
|
|
1900
1969
|
if (!i) return;
|
|
1901
|
-
let a = this.opts.video, o = new
|
|
1970
|
+
let a = this.opts.video, o = new He({
|
|
1902
1971
|
endpoint: i,
|
|
1903
1972
|
sid: r,
|
|
1904
1973
|
apiKey: n,
|
|
1905
1974
|
player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
|
|
1906
|
-
sdkVersion:
|
|
1975
|
+
sdkVersion: Ue,
|
|
1907
1976
|
sample: () => {
|
|
1908
1977
|
let e = this.engine?.getStats() ?? F, t = a.buffered.length ? Math.max(0, a.buffered.end(a.buffered.length - 1) - a.currentTime) : 0;
|
|
1909
1978
|
return {
|
|
@@ -1918,14 +1987,14 @@ var tt = class {
|
|
|
1918
1987
|
let s = () => o.noteSeek();
|
|
1919
1988
|
a.addEventListener("seeking", s), this.detachSeekListener = () => a.removeEventListener("seeking", s), o.start();
|
|
1920
1989
|
}
|
|
1921
|
-
createEngine(e,
|
|
1990
|
+
createEngine(e, t) {
|
|
1922
1991
|
let r = { ...this.opts.hlsConfig };
|
|
1923
1992
|
this.opts.startLevel != null && (r.startLevel = this.opts.startLevel), this.opts.liveLatencyTarget != null && (r.liveSyncDuration = this.opts.liveLatencyTarget);
|
|
1924
1993
|
let i = {
|
|
1925
1994
|
video: this.opts.video,
|
|
1926
1995
|
descriptor: e,
|
|
1927
|
-
credential:
|
|
1928
|
-
deviceId:
|
|
1996
|
+
credential: t,
|
|
1997
|
+
deviceId: D(),
|
|
1929
1998
|
userId: this.opts.userId,
|
|
1930
1999
|
lowLatency: this.opts.lowLatency === void 0 || this.opts.lowLatency === "auto" || this.opts.lowLatency,
|
|
1931
2000
|
snapToLiveOnRefocus: this.opts.snapToLiveOnRefocus,
|
|
@@ -1936,9 +2005,9 @@ var tt = class {
|
|
|
1936
2005
|
if (a === "native") return new z(i);
|
|
1937
2006
|
if (a === "hls") return new V(i);
|
|
1938
2007
|
if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && L(this.opts.video) && R()) return new z(i);
|
|
1939
|
-
if (
|
|
2008
|
+
if (Se()) return new V(i);
|
|
1940
2009
|
if (L(this.opts.video)) return new z(i);
|
|
1941
|
-
throw new
|
|
2010
|
+
throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
|
|
1942
2011
|
}
|
|
1943
2012
|
onEngineEvent(e) {
|
|
1944
2013
|
if (!this.destroyed) switch (e.type) {
|
|
@@ -1960,19 +2029,17 @@ var tt = class {
|
|
|
1960
2029
|
case "ended":
|
|
1961
2030
|
this.onStreamEnded();
|
|
1962
2031
|
break;
|
|
1963
|
-
case "error":
|
|
1964
|
-
this.analyticsSession?.noteError(e.error.code, e.error.message, e.fatal, e.error.httpStatus ?? 0, Math.round(this.opts.video.currentTime * 1e3)), e.fatal ? this.fail(e.error) : this.emit("warning", e.error);
|
|
1965
|
-
break;
|
|
2032
|
+
case "error": this.analyticsSession?.noteError(e.error.code, e.error.message, e.fatal, e.error.httpStatus ?? 0, Math.round(this.opts.video.currentTime * 1e3)), e.fatal ? this.fail(e.error) : this.emit("warning", e.error);
|
|
1966
2033
|
}
|
|
1967
2034
|
}
|
|
1968
2035
|
startLiveState(e) {
|
|
1969
2036
|
if (this.opts.liveStateEvents === !1) return;
|
|
1970
|
-
let
|
|
1971
|
-
if (!
|
|
2037
|
+
let t = this.opts.eventsBaseUrl ?? w(this.opts.baseUrl);
|
|
2038
|
+
if (!t) return;
|
|
1972
2039
|
this.closeLiveState();
|
|
1973
2040
|
let r = 0, i = !1;
|
|
1974
|
-
this.liveStateSub =
|
|
1975
|
-
eventsBaseUrl:
|
|
2041
|
+
this.liveStateSub = C({
|
|
2042
|
+
eventsBaseUrl: t,
|
|
1976
2043
|
matchUrn: this.opts.matchUrn,
|
|
1977
2044
|
credential: this.opts.credential,
|
|
1978
2045
|
onState: (t) => {
|
|
@@ -1982,7 +2049,7 @@ var tt = class {
|
|
|
1982
2049
|
r = 0, i = !1;
|
|
1983
2050
|
},
|
|
1984
2051
|
onError: () => {
|
|
1985
|
-
this.destroyed || e !== this.loadGen || i || (r += 1, r >=
|
|
2052
|
+
this.destroyed || e !== this.loadGen || i || (r += 1, r >= tt && (i = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} after ${r} attempts — end-of-stream detection degraded to the HLS staleness watchdog. Check the events host, the api-key's allowed origins, and CSP connect-src.`))));
|
|
1986
2053
|
}
|
|
1987
2054
|
});
|
|
1988
2055
|
}
|
|
@@ -2004,7 +2071,7 @@ var tt = class {
|
|
|
2004
2071
|
}
|
|
2005
2072
|
t.controls = !1;
|
|
2006
2073
|
let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
|
|
2007
|
-
r && r.insertBefore(n, t), n.appendChild(t), K(n, this.opts.theme), this.container = n, this.controlBar =
|
|
2074
|
+
r && r.insertBefore(n, t), n.appendChild(t), K(n, this.opts.theme), this.container = n, this.controlBar = Ye({
|
|
2008
2075
|
root: n,
|
|
2009
2076
|
video: t,
|
|
2010
2077
|
player: this,
|
|
@@ -2027,7 +2094,7 @@ var tt = class {
|
|
|
2027
2094
|
}
|
|
2028
2095
|
startStats() {
|
|
2029
2096
|
this.stopStats();
|
|
2030
|
-
let e = Math.max(500, this.opts.statsIntervalMs ??
|
|
2097
|
+
let e = Math.max(500, this.opts.statsIntervalMs ?? nt);
|
|
2031
2098
|
this.statsTimer = setInterval(() => {
|
|
2032
2099
|
if (!this.engine) return;
|
|
2033
2100
|
let e = this.engine.getStats();
|
|
@@ -2039,7 +2106,7 @@ var tt = class {
|
|
|
2039
2106
|
}
|
|
2040
2107
|
scheduleLicenseRefresh() {
|
|
2041
2108
|
this.stopRefresh();
|
|
2042
|
-
let e = this.opts.licenseRefreshMs ??
|
|
2109
|
+
let e = this.opts.licenseRefreshMs ?? rt;
|
|
2043
2110
|
!e || !this._descriptor?.drmEnabled || (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
|
|
2044
2111
|
}
|
|
2045
2112
|
stopRefresh() {
|
|
@@ -2056,7 +2123,7 @@ var tt = class {
|
|
|
2056
2123
|
fairplay: r
|
|
2057
2124
|
}), this._descriptor = t);
|
|
2058
2125
|
} catch (e) {
|
|
2059
|
-
e instanceof
|
|
2126
|
+
e instanceof n && !o(e.code) && (this.stopRefresh(), this.destroyed || this.emit("warning", e));
|
|
2060
2127
|
}
|
|
2061
2128
|
}
|
|
2062
2129
|
async play() {
|
|
@@ -2127,9 +2194,9 @@ var tt = class {
|
|
|
2127
2194
|
waitForLive: this.opts.waitForLive ? this.buildWaitConfig(e) : void 0
|
|
2128
2195
|
});
|
|
2129
2196
|
if (await this.onResolved(t, e), e === this.loadGen && this._state === "error" && this._lastError) throw this._lastError;
|
|
2130
|
-
} catch (
|
|
2197
|
+
} catch (t) {
|
|
2131
2198
|
if (this.destroyed || this.abort.signal.aborted || e !== this.loadGen) return;
|
|
2132
|
-
throw
|
|
2199
|
+
throw t !== this._lastError && this.fail(t instanceof n ? t : new n("INTERNAL", 0, String(t))), t;
|
|
2133
2200
|
}
|
|
2134
2201
|
}
|
|
2135
2202
|
on(e, t) {
|
|
@@ -2162,10 +2229,7 @@ var tt = class {
|
|
|
2162
2229
|
case "paused":
|
|
2163
2230
|
this.emit("paused");
|
|
2164
2231
|
break;
|
|
2165
|
-
case "ended":
|
|
2166
|
-
this.emit("ended");
|
|
2167
|
-
break;
|
|
2168
|
-
default: break;
|
|
2232
|
+
case "ended": this.emit("ended");
|
|
2169
2233
|
}
|
|
2170
2234
|
}
|
|
2171
2235
|
emit(e, t) {
|
|
@@ -2183,7 +2247,7 @@ function $(e) {
|
|
|
2183
2247
|
}
|
|
2184
2248
|
//#endregion
|
|
2185
2249
|
//#region src/embed/host.ts
|
|
2186
|
-
function
|
|
2250
|
+
function at(e) {
|
|
2187
2251
|
if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
|
|
2188
2252
|
if (!e.src) throw Error("mountEmbed: opts.src is required");
|
|
2189
2253
|
if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
|
|
@@ -2242,8 +2306,8 @@ function nt(e) {
|
|
|
2242
2306
|
}
|
|
2243
2307
|
//#endregion
|
|
2244
2308
|
//#region src/embed/page.ts
|
|
2245
|
-
var
|
|
2246
|
-
function
|
|
2309
|
+
var ot = /^[A-Za-z0-9:_.-]{1,128}$/;
|
|
2310
|
+
function st() {
|
|
2247
2311
|
let e = window.HAVIK_EMBED_CONFIG;
|
|
2248
2312
|
if (e) return {
|
|
2249
2313
|
baseUrl: e.baseUrl ?? "",
|
|
@@ -2265,11 +2329,11 @@ function it() {
|
|
|
2265
2329
|
waitForLive: !0
|
|
2266
2330
|
};
|
|
2267
2331
|
}
|
|
2268
|
-
async function
|
|
2269
|
-
let e =
|
|
2270
|
-
|
|
2271
|
-
let
|
|
2272
|
-
|
|
2332
|
+
async function ct() {
|
|
2333
|
+
let e = st();
|
|
2334
|
+
ut();
|
|
2335
|
+
let t = document.createElement("video");
|
|
2336
|
+
t.setAttribute("playsinline", ""), t.muted = e.muted ?? !0, t.style.cssText = "width:100%;height:100%;object-fit:contain;background:#000;aspect-ratio:auto", document.body.appendChild(t);
|
|
2273
2337
|
let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
|
|
2274
2338
|
r || console.warn("havik-player: embed has no parentOrigin — the postMessage bridge is disabled (no events posted, no commands accepted). mountEmbed sets parentOrigin automatically.");
|
|
2275
2339
|
let i = (e) => {
|
|
@@ -2322,13 +2386,13 @@ async function at() {
|
|
|
2322
2386
|
code: e.code,
|
|
2323
2387
|
message: e.message
|
|
2324
2388
|
})), e.on("autoplayblocked", () => {
|
|
2325
|
-
i({ type: "oddin:autoplayblocked" }),
|
|
2389
|
+
i({ type: "oddin:autoplayblocked" }), lt(t, e);
|
|
2326
2390
|
});
|
|
2327
2391
|
}, s = async (r) => {
|
|
2328
2392
|
a?.destroy(), a = null;
|
|
2329
2393
|
try {
|
|
2330
|
-
let
|
|
2331
|
-
video:
|
|
2394
|
+
let n = await Q({
|
|
2395
|
+
video: t,
|
|
2332
2396
|
baseUrl: e.baseUrl,
|
|
2333
2397
|
matchUrn: r,
|
|
2334
2398
|
credential: { apiKey: e.apiKey },
|
|
@@ -2336,14 +2400,14 @@ async function at() {
|
|
|
2336
2400
|
muted: e.muted,
|
|
2337
2401
|
waitForLive: e.waitForLive
|
|
2338
2402
|
});
|
|
2339
|
-
a =
|
|
2340
|
-
let i =
|
|
2341
|
-
i?.classList.contains("havik-player") && (i.style.cssText = "position:absolute;inset:0;width:100%;height:100%;aspect-ratio:auto"), o(
|
|
2403
|
+
a = n;
|
|
2404
|
+
let i = t.parentElement;
|
|
2405
|
+
i?.classList.contains("havik-player") && (i.style.cssText = "position:absolute;inset:0;width:100%;height:100%;aspect-ratio:auto"), o(n);
|
|
2342
2406
|
} catch (e) {
|
|
2343
|
-
let
|
|
2407
|
+
let t = e instanceof n ? e.code : "INTERNAL", r = e instanceof Error ? e.message : String(e);
|
|
2344
2408
|
i({
|
|
2345
2409
|
type: "oddin:error",
|
|
2346
|
-
code:
|
|
2410
|
+
code: t,
|
|
2347
2411
|
message: r
|
|
2348
2412
|
});
|
|
2349
2413
|
}
|
|
@@ -2388,11 +2452,9 @@ async function at() {
|
|
|
2388
2452
|
a?.retry();
|
|
2389
2453
|
break;
|
|
2390
2454
|
case "oddin:load":
|
|
2391
|
-
typeof e.matchUrn == "string" &&
|
|
2392
|
-
break;
|
|
2393
|
-
case "oddin:destroy":
|
|
2394
|
-
c();
|
|
2455
|
+
typeof e.matchUrn == "string" && ot.test(e.matchUrn) && s(e.matchUrn);
|
|
2395
2456
|
break;
|
|
2457
|
+
case "oddin:destroy": c();
|
|
2396
2458
|
}
|
|
2397
2459
|
}, u = (e) => {
|
|
2398
2460
|
!r || e.origin !== r || $(e.data) && l(e.data);
|
|
@@ -2402,18 +2464,18 @@ async function at() {
|
|
|
2402
2464
|
protocol: 1
|
|
2403
2465
|
});
|
|
2404
2466
|
}
|
|
2405
|
-
function
|
|
2467
|
+
function lt(e, t) {
|
|
2406
2468
|
if (document.getElementById("havik-tap")) return;
|
|
2407
2469
|
let n = document.createElement("button");
|
|
2408
2470
|
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", () => {
|
|
2409
2471
|
n.remove(), t.play();
|
|
2410
2472
|
}), document.body.appendChild(n);
|
|
2411
2473
|
}
|
|
2412
|
-
function
|
|
2474
|
+
function ut() {
|
|
2413
2475
|
let e = document.createElement("style");
|
|
2414
2476
|
e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
|
|
2415
2477
|
}
|
|
2416
2478
|
//#endregion
|
|
2417
|
-
export { W as ODDIN_THEME,
|
|
2479
|
+
export { W as ODDIN_THEME, n as PlaybackError, K as applyTheme, ct as bootEmbed, me as clearDeviceId, Q as createPlayer, w as deriveEventsBaseUrl, M as detectDrmSupport, ce as fetchCatalog, ue as fetchTournament, fe as flattenMatches, D as getDeviceId, P as isLowLatencyManifest, $ as isOddinMessage, ge as licenseRequestHeaders, at as mountEmbed, v as pollToLive, b as resolveStream, C as subscribeLiveState, pe as watchStatus };
|
|
2418
2480
|
|
|
2419
2481
|
//# sourceMappingURL=index.js.map
|