@oddin-gg/havik-player 1.15.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.js +367 -311
- 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,102 +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
|
|
67
|
-
if (
|
|
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);
|
|
68
68
|
i.set("x-api-key", r.apiKey);
|
|
69
69
|
try {
|
|
70
70
|
return await fetch(e, {
|
|
71
|
-
method:
|
|
71
|
+
method: t.method ?? "GET",
|
|
72
72
|
headers: i,
|
|
73
|
-
body:
|
|
74
|
-
signal:
|
|
73
|
+
body: t.body,
|
|
74
|
+
signal: t.signal,
|
|
75
75
|
cache: "no-store",
|
|
76
76
|
credentials: "omit",
|
|
77
77
|
mode: "cors"
|
|
78
78
|
});
|
|
79
79
|
} catch (e) {
|
|
80
|
-
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 });
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
function
|
|
83
|
+
function d(e) {
|
|
84
84
|
return e instanceof Error ? e.message : String(e);
|
|
85
85
|
}
|
|
86
86
|
//#endregion
|
|
87
87
|
//#region src/core/waitForLive.ts
|
|
88
|
-
var
|
|
88
|
+
var f = 1e3, p = 3e4, m = 6e5, h = 3e5, g = 1e3, ee = {
|
|
89
89
|
TOO_EARLY: "tooEarly",
|
|
90
90
|
UNAVAILABLE: "unavailable",
|
|
91
91
|
RATE_LIMITED: "rateLimited",
|
|
92
92
|
NETWORK: "network"
|
|
93
93
|
};
|
|
94
|
-
function
|
|
95
|
-
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);
|
|
96
96
|
return {
|
|
97
97
|
timeoutMs: t.timeoutMs,
|
|
98
98
|
floorMs: n,
|
|
99
99
|
ceilMs: r,
|
|
100
100
|
imminentMs: i,
|
|
101
101
|
sanityFloorMs: a,
|
|
102
|
-
kickoffMs:
|
|
102
|
+
kickoffMs: ne(t.kickoffAt),
|
|
103
103
|
jitter: t.jitter ?? !0,
|
|
104
104
|
onState: t.onState
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
|
-
function
|
|
107
|
+
function ne(e) {
|
|
108
108
|
if (e == null) return;
|
|
109
109
|
if (typeof e == "number") return Number.isFinite(e) ? e : void 0;
|
|
110
110
|
let t = Date.parse(e);
|
|
111
111
|
return Number.isFinite(t) ? t : void 0;
|
|
112
112
|
}
|
|
113
|
-
function
|
|
113
|
+
function _(e, t, n) {
|
|
114
114
|
return Math.min(n, Math.max(t, e));
|
|
115
115
|
}
|
|
116
|
-
function
|
|
116
|
+
function re(e) {
|
|
117
117
|
return Math.round(e * (1 + .15 * Math.random()));
|
|
118
118
|
}
|
|
119
|
-
function
|
|
119
|
+
function ie(e, t, n, r) {
|
|
120
120
|
let i = r ?? n.kickoffMs, a = i == null ? void 0 : Math.max(0, i - t), o = Math.max(n.floorMs, e ?? 0);
|
|
121
121
|
if (a == null || a <= n.imminentMs) return {
|
|
122
|
-
delay:
|
|
122
|
+
delay: _(e ?? n.floorMs, o, n.ceilMs),
|
|
123
123
|
ttkMs: a
|
|
124
124
|
};
|
|
125
125
|
let s = Math.min(Math.floor(a / 10), n.sanityFloorMs, a);
|
|
@@ -128,62 +128,62 @@ function re(e, t, n, r) {
|
|
|
128
128
|
ttkMs: a
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
|
-
function
|
|
131
|
+
function ae(e, t) {
|
|
132
132
|
return new Promise((r, i) => {
|
|
133
|
-
if (
|
|
134
|
-
i(new
|
|
133
|
+
if (t?.aborted) {
|
|
134
|
+
i(new n("ABORTED", 0, "wait-for-live aborted"));
|
|
135
135
|
return;
|
|
136
136
|
}
|
|
137
137
|
let a = () => {
|
|
138
|
-
clearTimeout(o), i(new
|
|
138
|
+
clearTimeout(o), i(new n("ABORTED", 0, "wait-for-live aborted"));
|
|
139
139
|
}, o = setTimeout(() => {
|
|
140
|
-
|
|
140
|
+
t?.removeEventListener("abort", a), r();
|
|
141
141
|
}, e);
|
|
142
|
-
|
|
142
|
+
t?.addEventListener("abort", a, { once: !0 });
|
|
143
143
|
});
|
|
144
144
|
}
|
|
145
|
-
async function v(e,
|
|
146
|
-
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;
|
|
147
147
|
for (;;) {
|
|
148
|
-
if (r?.aborted) throw new
|
|
148
|
+
if (r?.aborted) throw new n("ABORTED", 0, "wait-for-live aborted");
|
|
149
149
|
try {
|
|
150
150
|
return await e();
|
|
151
151
|
} catch (e) {
|
|
152
|
-
if (!(e instanceof
|
|
152
|
+
if (!(e instanceof n) || !o(e.code)) throw e;
|
|
153
153
|
c += 1, e.liveStartsAtMs != null && Number.isFinite(e.liveStartsAtMs) && (l = e.liveStartsAtMs);
|
|
154
|
-
let
|
|
155
|
-
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`, {
|
|
156
156
|
retryAfterMs: e.retryAfterMs,
|
|
157
157
|
cause: e
|
|
158
158
|
});
|
|
159
159
|
i.onState?.({
|
|
160
|
-
phase:
|
|
160
|
+
phase: ee[e.code] ?? "unavailable",
|
|
161
161
|
retryInMs: f,
|
|
162
162
|
attempt: c,
|
|
163
|
-
elapsedMs:
|
|
163
|
+
elapsedMs: t - a,
|
|
164
164
|
ttkMs: d
|
|
165
|
-
}), await
|
|
165
|
+
}), await ae(f, r);
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
//#endregion
|
|
170
170
|
//#region src/core/resolve.ts
|
|
171
171
|
async function y(e) {
|
|
172
|
-
if (!e.matchUrn || typeof e.matchUrn != "string") throw new
|
|
173
|
-
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)}`), {
|
|
174
174
|
credential: e.credential,
|
|
175
175
|
signal: e.signal,
|
|
176
176
|
apiHeaders: e.apiHeaders
|
|
177
177
|
});
|
|
178
|
-
if (!
|
|
179
|
-
return
|
|
178
|
+
if (!t.ok) throw await oe(t);
|
|
179
|
+
return se(await t.json(), e.matchUrn);
|
|
180
180
|
}
|
|
181
181
|
function b(e) {
|
|
182
182
|
let t = () => y(e);
|
|
183
183
|
return e.waitForLive ? v(t, e.waitForLive, e.signal) : t();
|
|
184
184
|
}
|
|
185
|
-
async function
|
|
186
|
-
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;
|
|
187
187
|
try {
|
|
188
188
|
let t = await e.json();
|
|
189
189
|
if (t?.error?.message && (a = t.error.message), t?.error?.code && (o = t.error.code), typeof t?.liveStartsAt == "string") {
|
|
@@ -191,14 +191,14 @@ async function ae(e) {
|
|
|
191
191
|
Number.isFinite(e) && (s = e);
|
|
192
192
|
}
|
|
193
193
|
} catch {}
|
|
194
|
-
return new
|
|
195
|
-
retryAfterMs:
|
|
194
|
+
return new n(r(e.status), e.status, a, {
|
|
195
|
+
retryAfterMs: t,
|
|
196
196
|
requestId: i,
|
|
197
197
|
serverCode: o,
|
|
198
198
|
liveStartsAtMs: s
|
|
199
199
|
});
|
|
200
200
|
}
|
|
201
|
-
var
|
|
201
|
+
var x = /* @__PURE__ */ new Set([
|
|
202
202
|
"matchUrn",
|
|
203
203
|
"protocol",
|
|
204
204
|
"drmEnabled",
|
|
@@ -209,62 +209,62 @@ var oe = /* @__PURE__ */ new Set([
|
|
|
209
209
|
"expiresAt",
|
|
210
210
|
"analytics"
|
|
211
211
|
]);
|
|
212
|
-
function
|
|
212
|
+
function S(e, t) {
|
|
213
213
|
let r;
|
|
214
214
|
try {
|
|
215
|
-
r = new URL(
|
|
215
|
+
r = new URL(t);
|
|
216
216
|
} catch {
|
|
217
|
-
throw new
|
|
217
|
+
throw new n("INTERNAL", 0, `playback ${e} is not a valid URL`);
|
|
218
218
|
}
|
|
219
|
-
if (r.protocol !== "https:" && r.protocol !== "http:") throw new
|
|
220
|
-
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;
|
|
221
221
|
}
|
|
222
|
-
function
|
|
223
|
-
if (!e.manifestUrl) throw new
|
|
222
|
+
function se(e, t) {
|
|
223
|
+
if (!e.manifestUrl) throw new n("INTERNAL", 0, "playback response is missing manifestUrl");
|
|
224
224
|
let r = e.drmEnabled ?? !!e.drm, i = {
|
|
225
|
-
matchUrn: e.matchUrn ??
|
|
225
|
+
matchUrn: e.matchUrn ?? t,
|
|
226
226
|
protocol: "hls",
|
|
227
227
|
drmEnabled: r,
|
|
228
|
-
manifestUrl:
|
|
228
|
+
manifestUrl: S("manifestUrl", e.manifestUrl),
|
|
229
229
|
serverTime: e.serverTime ?? ""
|
|
230
230
|
};
|
|
231
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) {
|
|
232
232
|
let t = {};
|
|
233
|
-
e.drm.widevine?.licenseUrl && (t.widevine = { licenseUrl:
|
|
234
|
-
licenseUrl:
|
|
235
|
-
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)
|
|
236
236
|
}), (t.widevine || t.fairplay) && (i.drm = t);
|
|
237
237
|
}
|
|
238
238
|
let a = {};
|
|
239
|
-
for (let [t, n] of Object.entries(e))
|
|
239
|
+
for (let [t, n] of Object.entries(e)) x.has(t) || (a[t] = n);
|
|
240
240
|
return Object.keys(a).length > 0 && (i.extensions = a), i;
|
|
241
241
|
}
|
|
242
242
|
//#endregion
|
|
243
243
|
//#region src/core/catalog.ts
|
|
244
|
-
async function
|
|
245
|
-
let
|
|
246
|
-
e.status &&
|
|
247
|
-
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();
|
|
248
248
|
e.etag && o.set("If-None-Match", e.etag);
|
|
249
|
-
let
|
|
249
|
+
let s = await u(a, {
|
|
250
250
|
credential: e.credential,
|
|
251
251
|
signal: e.signal,
|
|
252
252
|
headers: o,
|
|
253
253
|
apiHeaders: e.apiHeaders
|
|
254
254
|
});
|
|
255
|
-
if (
|
|
255
|
+
if (s.status === 304) return {
|
|
256
256
|
tournaments: [],
|
|
257
257
|
etag: e.etag,
|
|
258
258
|
notModified: !0
|
|
259
259
|
};
|
|
260
|
-
if (!
|
|
260
|
+
if (!s.ok) throw new n(r(s.status), s.status, `catalog request failed (HTTP ${s.status})`);
|
|
261
261
|
return {
|
|
262
|
-
tournaments:
|
|
263
|
-
etag:
|
|
262
|
+
tournaments: le(await s.json()),
|
|
263
|
+
etag: s.headers.get("etag") ?? void 0,
|
|
264
264
|
notModified: !1
|
|
265
265
|
};
|
|
266
266
|
}
|
|
267
|
-
function
|
|
267
|
+
function le(e) {
|
|
268
268
|
return (e.tournaments ?? []).map((e) => {
|
|
269
269
|
let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
|
|
270
270
|
return {
|
|
@@ -284,10 +284,10 @@ function se(e) {
|
|
|
284
284
|
};
|
|
285
285
|
});
|
|
286
286
|
}
|
|
287
|
-
async function
|
|
288
|
-
let
|
|
287
|
+
async function ue(e) {
|
|
288
|
+
let t = c(e.baseUrl, `/v1/tournaments/${encodeURIComponent(e.urn)}`), i = new Headers();
|
|
289
289
|
e.etag && i.set("If-None-Match", e.etag);
|
|
290
|
-
let a = await
|
|
290
|
+
let a = await u(t, {
|
|
291
291
|
credential: e.credential,
|
|
292
292
|
signal: e.signal,
|
|
293
293
|
headers: i,
|
|
@@ -302,15 +302,15 @@ async function ce(e) {
|
|
|
302
302
|
notModified: !1,
|
|
303
303
|
notFound: !0
|
|
304
304
|
};
|
|
305
|
-
if (!a.ok) throw new
|
|
305
|
+
if (!a.ok) throw new n(r(a.status), a.status, `tournament request failed (HTTP ${a.status})`);
|
|
306
306
|
return {
|
|
307
|
-
tournament:
|
|
307
|
+
tournament: de(await a.json()),
|
|
308
308
|
etag: a.headers.get("etag") ?? void 0,
|
|
309
309
|
notModified: !1,
|
|
310
310
|
notFound: !1
|
|
311
311
|
};
|
|
312
312
|
}
|
|
313
|
-
function
|
|
313
|
+
function de(e) {
|
|
314
314
|
let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
|
|
315
315
|
return {
|
|
316
316
|
urn: t,
|
|
@@ -328,55 +328,55 @@ function le(e) {
|
|
|
328
328
|
}))
|
|
329
329
|
};
|
|
330
330
|
}
|
|
331
|
-
function
|
|
331
|
+
function fe(e) {
|
|
332
332
|
return e.tournaments.flatMap((e) => e.matches);
|
|
333
333
|
}
|
|
334
334
|
//#endregion
|
|
335
335
|
//#region src/core/poll.ts
|
|
336
|
-
function
|
|
337
|
-
let
|
|
338
|
-
|
|
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));
|
|
339
339
|
}, f = async () => {
|
|
340
340
|
try {
|
|
341
|
-
let
|
|
341
|
+
let t = c(e.baseUrl, `/v1/matches/${encodeURIComponent(e.matchUrn)}`), a = new Headers();
|
|
342
342
|
o && a.set("If-None-Match", o);
|
|
343
|
-
let
|
|
343
|
+
let l = await u(t, {
|
|
344
344
|
credential: e.credential,
|
|
345
345
|
signal: i.signal,
|
|
346
346
|
headers: a,
|
|
347
347
|
apiHeaders: e.apiHeaders
|
|
348
348
|
});
|
|
349
|
-
if (
|
|
350
|
-
if (!
|
|
351
|
-
o =
|
|
352
|
-
let
|
|
353
|
-
i !==
|
|
354
|
-
matchUrn:
|
|
355
|
-
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,
|
|
356
356
|
status: i,
|
|
357
|
-
datePlannedStart:
|
|
357
|
+
datePlannedStart: t.datePlannedStart
|
|
358
358
|
}));
|
|
359
359
|
}
|
|
360
|
-
} catch (
|
|
361
|
-
i.signal.aborted || e.onError?.(
|
|
360
|
+
} catch (t) {
|
|
361
|
+
i.signal.aborted || e.onError?.(t instanceof n ? t : new n("NETWORK", 0, String(t)));
|
|
362
362
|
} finally {
|
|
363
363
|
d();
|
|
364
364
|
}
|
|
365
365
|
};
|
|
366
366
|
return f(), { stop() {
|
|
367
|
-
|
|
367
|
+
l = !0, i.abort(), a && clearTimeout(a);
|
|
368
368
|
} };
|
|
369
369
|
}
|
|
370
370
|
//#endregion
|
|
371
371
|
//#region src/core/events.ts
|
|
372
|
-
function
|
|
372
|
+
function C(e) {
|
|
373
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) => {
|
|
374
374
|
if (r.signal.aborted) return t();
|
|
375
375
|
let n = setTimeout(t, e);
|
|
376
376
|
r.signal.addEventListener("abort", () => {
|
|
377
377
|
clearTimeout(n), t();
|
|
378
378
|
}, { once: !0 });
|
|
379
|
-
}),
|
|
379
|
+
}), o = (t) => {
|
|
380
380
|
e.onAlive?.();
|
|
381
381
|
let n = "message", r = [];
|
|
382
382
|
for (let e of t.split("\n")) {
|
|
@@ -385,19 +385,19 @@ function w(e) {
|
|
|
385
385
|
let i = t.indexOf(":"), a = i < 0 ? t : t.slice(0, i), o = i < 0 ? "" : t.slice(i + 1);
|
|
386
386
|
o.startsWith(" ") && (o = o.slice(1)), a === "event" ? n = o : a === "data" && r.push(o);
|
|
387
387
|
}
|
|
388
|
-
if (
|
|
388
|
+
if (n === "state" && r.length !== 0) try {
|
|
389
389
|
let t = JSON.parse(r.join("\n"));
|
|
390
390
|
t && typeof t.state == "string" && e.onState(t);
|
|
391
391
|
} catch {}
|
|
392
392
|
}, l = /\r\n\r\n|\n\n|\r\r/, u = async (e, t) => {
|
|
393
|
-
let n = e.getReader(), r = new TextDecoder(), a = "",
|
|
393
|
+
let n = e.getReader(), r = new TextDecoder(), a = "", s = !1;
|
|
394
394
|
try {
|
|
395
395
|
for (;;) {
|
|
396
|
-
let { value: e, done:
|
|
397
|
-
if (
|
|
398
|
-
!
|
|
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 });
|
|
399
399
|
let u;
|
|
400
|
-
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);
|
|
401
401
|
if (i) return;
|
|
402
402
|
}
|
|
403
403
|
} finally {
|
|
@@ -405,12 +405,12 @@ function w(e) {
|
|
|
405
405
|
}
|
|
406
406
|
};
|
|
407
407
|
return (async () => {
|
|
408
|
-
let
|
|
408
|
+
let o = t;
|
|
409
409
|
for (; !i;) {
|
|
410
410
|
try {
|
|
411
|
-
let n = await
|
|
411
|
+
let n = await s(e.credential);
|
|
412
412
|
if (i) return;
|
|
413
|
-
let a =
|
|
413
|
+
let a = c(e.eventsBaseUrl, `/v1/events/${encodeURIComponent(e.matchUrn)}`), l = await fetch(a, {
|
|
414
414
|
method: "GET",
|
|
415
415
|
headers: {
|
|
416
416
|
"x-api-key": n.apiKey,
|
|
@@ -423,20 +423,20 @@ function w(e) {
|
|
|
423
423
|
});
|
|
424
424
|
if (i) return;
|
|
425
425
|
!l.ok || !l.body ? e.onError?.(/* @__PURE__ */ Error(`live-state subscribe failed: HTTP ${l.status}`)) : await u(l.body, () => {
|
|
426
|
-
|
|
426
|
+
o = t;
|
|
427
427
|
});
|
|
428
428
|
} catch (t) {
|
|
429
429
|
if (i || r.signal.aborted) return;
|
|
430
430
|
e.onError?.(t);
|
|
431
431
|
}
|
|
432
432
|
if (i) return;
|
|
433
|
-
await a(
|
|
433
|
+
await a(o), o = Math.min(n, o * 2);
|
|
434
434
|
}
|
|
435
435
|
})(), { close() {
|
|
436
436
|
i || (i = !0, r.abort());
|
|
437
437
|
} };
|
|
438
438
|
}
|
|
439
|
-
function
|
|
439
|
+
function w(e) {
|
|
440
440
|
try {
|
|
441
441
|
let t = new URL(e);
|
|
442
442
|
return t.hostname = `events.${t.hostname}`, t.pathname = "", t.search = "", t.origin;
|
|
@@ -446,27 +446,27 @@ function T(e) {
|
|
|
446
446
|
}
|
|
447
447
|
//#endregion
|
|
448
448
|
//#region src/core/drm/deviceId.ts
|
|
449
|
-
var
|
|
450
|
-
function
|
|
451
|
-
if (
|
|
449
|
+
var T = "havik.player.deviceId", E;
|
|
450
|
+
function D() {
|
|
451
|
+
if (E) return E;
|
|
452
452
|
try {
|
|
453
|
-
let e = globalThis.localStorage?.getItem(
|
|
454
|
-
if (e) return
|
|
453
|
+
let e = globalThis.localStorage?.getItem(T);
|
|
454
|
+
if (e) return E = e, e;
|
|
455
455
|
} catch {}
|
|
456
|
-
let e =
|
|
457
|
-
|
|
456
|
+
let e = he();
|
|
457
|
+
E = e;
|
|
458
458
|
try {
|
|
459
|
-
globalThis.localStorage?.setItem(
|
|
459
|
+
globalThis.localStorage?.setItem(T, e);
|
|
460
460
|
} catch {}
|
|
461
461
|
return e;
|
|
462
462
|
}
|
|
463
|
-
function
|
|
464
|
-
|
|
463
|
+
function me() {
|
|
464
|
+
E = void 0;
|
|
465
465
|
try {
|
|
466
|
-
globalThis.localStorage?.removeItem(
|
|
466
|
+
globalThis.localStorage?.removeItem(T);
|
|
467
467
|
} catch {}
|
|
468
468
|
}
|
|
469
|
-
function
|
|
469
|
+
function he() {
|
|
470
470
|
let e = globalThis.crypto;
|
|
471
471
|
if (e && typeof e.randomUUID == "function") return e.randomUUID();
|
|
472
472
|
let t = /* @__PURE__ */ new Uint8Array(16);
|
|
@@ -478,22 +478,22 @@ function pe() {
|
|
|
478
478
|
}
|
|
479
479
|
//#endregion
|
|
480
480
|
//#region src/core/drm/headers.ts
|
|
481
|
-
function
|
|
481
|
+
function ge(e, t, n = {}) {
|
|
482
482
|
let r = {
|
|
483
483
|
"x-api-key": t.apiKey,
|
|
484
484
|
"X-Match-Urn": e.matchUrn,
|
|
485
|
-
"X-Device-Id": n.deviceId ??
|
|
485
|
+
"X-Device-Id": n.deviceId ?? D(),
|
|
486
486
|
"Content-Type": "application/octet-stream"
|
|
487
487
|
};
|
|
488
488
|
return n.userId && (r["X-User-Id"] = n.userId), r;
|
|
489
489
|
}
|
|
490
490
|
//#endregion
|
|
491
491
|
//#region src/core/drm/support.ts
|
|
492
|
-
var
|
|
492
|
+
var _e = 5e3, O = "video/mp4; codecs=\"avc1.42E01E\"", k = "audio/mp4; codecs=\"mp4a.40.2\"", ve = {
|
|
493
493
|
widevine: ["com.widevine.alpha"],
|
|
494
494
|
fairplay: ["com.apple.fps"]
|
|
495
495
|
};
|
|
496
|
-
function
|
|
496
|
+
function A(e) {
|
|
497
497
|
return e === "fairplay" ? [{
|
|
498
498
|
initDataTypes: [
|
|
499
499
|
"sinf",
|
|
@@ -501,24 +501,24 @@ function _e(e) {
|
|
|
501
501
|
"cenc"
|
|
502
502
|
],
|
|
503
503
|
videoCapabilities: [{
|
|
504
|
-
contentType:
|
|
504
|
+
contentType: O,
|
|
505
505
|
robustness: "",
|
|
506
506
|
encryptionScheme: "cbcs"
|
|
507
507
|
}],
|
|
508
508
|
audioCapabilities: [{
|
|
509
|
-
contentType:
|
|
509
|
+
contentType: k,
|
|
510
510
|
robustness: "",
|
|
511
511
|
encryptionScheme: "cbcs"
|
|
512
512
|
}]
|
|
513
513
|
}] : [{
|
|
514
514
|
initDataTypes: ["cenc"],
|
|
515
515
|
videoCapabilities: [{
|
|
516
|
-
contentType:
|
|
516
|
+
contentType: O,
|
|
517
517
|
robustness: "SW_SECURE_DECODE",
|
|
518
518
|
encryptionScheme: "cbcs"
|
|
519
519
|
}],
|
|
520
520
|
audioCapabilities: [{
|
|
521
|
-
contentType:
|
|
521
|
+
contentType: k,
|
|
522
522
|
robustness: "SW_SECURE_CRYPTO",
|
|
523
523
|
encryptionScheme: "cbcs"
|
|
524
524
|
}]
|
|
@@ -535,7 +535,7 @@ function j(e, t) {
|
|
|
535
535
|
});
|
|
536
536
|
}
|
|
537
537
|
async function M(e = {}) {
|
|
538
|
-
let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ??
|
|
538
|
+
let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? _e, r = {};
|
|
539
539
|
if (typeof window < "u" && window.isSecureContext === !1) return {
|
|
540
540
|
verdict: "insecure-context",
|
|
541
541
|
detail: r
|
|
@@ -547,7 +547,7 @@ async function M(e = {}) {
|
|
|
547
547
|
};
|
|
548
548
|
let a = !1, o = !1, s = Date.now() + n, c = () => Math.max(1, s - Date.now());
|
|
549
549
|
for (let e of t) {
|
|
550
|
-
let t =
|
|
550
|
+
let t = ve[e];
|
|
551
551
|
if (!t) {
|
|
552
552
|
r[e] = "unknown system";
|
|
553
553
|
continue;
|
|
@@ -558,7 +558,7 @@ async function M(e = {}) {
|
|
|
558
558
|
break;
|
|
559
559
|
}
|
|
560
560
|
try {
|
|
561
|
-
return await j((await j(i(n,
|
|
561
|
+
return await j((await j(i(n, A(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
|
|
562
562
|
verdict: "supported",
|
|
563
563
|
system: e,
|
|
564
564
|
detail: r
|
|
@@ -592,7 +592,7 @@ var F = { droppedFrames: 0 };
|
|
|
592
592
|
function I(e) {
|
|
593
593
|
return e.getVideoPlaybackQuality?.()?.droppedVideoFrames ?? 0;
|
|
594
594
|
}
|
|
595
|
-
function
|
|
595
|
+
function ye(e, t) {
|
|
596
596
|
let n = [
|
|
597
597
|
["playing", () => t("playing")],
|
|
598
598
|
["waiting", () => t("buffering")],
|
|
@@ -609,7 +609,7 @@ function ve(e, t) {
|
|
|
609
609
|
}
|
|
610
610
|
//#endregion
|
|
611
611
|
//#region src/core/engine/native.ts
|
|
612
|
-
function
|
|
612
|
+
function be(e) {
|
|
613
613
|
try {
|
|
614
614
|
let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
|
|
615
615
|
return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
|
|
@@ -639,12 +639,12 @@ var z = class {
|
|
|
639
639
|
this.deps = e;
|
|
640
640
|
}
|
|
641
641
|
async load() {
|
|
642
|
-
let { video: e, descriptor:
|
|
643
|
-
if (
|
|
644
|
-
if (!
|
|
642
|
+
let { video: e, descriptor: t, emit: r } = this.deps;
|
|
643
|
+
if (t.drmEnabled) {
|
|
644
|
+
if (!t.drm?.fairplay) {
|
|
645
645
|
r({
|
|
646
646
|
type: "error",
|
|
647
|
-
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"),
|
|
648
648
|
fatal: !0
|
|
649
649
|
});
|
|
650
650
|
return;
|
|
@@ -653,25 +653,25 @@ var z = class {
|
|
|
653
653
|
}
|
|
654
654
|
this.onLoaded = () => r({ type: "ready" }), this.onError = () => r({
|
|
655
655
|
type: "error",
|
|
656
|
-
error: new
|
|
656
|
+
error: new n("INTERNAL", 0, "native HLS playback error"),
|
|
657
657
|
fatal: !0
|
|
658
|
-
}), 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();
|
|
659
659
|
}
|
|
660
660
|
armFairPlay() {
|
|
661
|
-
let { video: e, descriptor:
|
|
661
|
+
let { video: e, descriptor: t, credential: r, deviceId: a, userId: o, emit: s } = this.deps, c = t.drm?.fairplay;
|
|
662
662
|
if (!c) return;
|
|
663
663
|
this.fairplayLicenseUrl = c.licenseUrl;
|
|
664
|
-
let l = (e,
|
|
664
|
+
let l = (e, t, r) => s({
|
|
665
665
|
type: "error",
|
|
666
|
-
error: new
|
|
667
|
-
fatal:
|
|
666
|
+
error: new n(i(t), t, e),
|
|
667
|
+
fatal: r
|
|
668
668
|
});
|
|
669
|
-
this.onEncrypted = (
|
|
670
|
-
let
|
|
671
|
-
!
|
|
669
|
+
this.onEncrypted = (n) => {
|
|
670
|
+
let i = n;
|
|
671
|
+
!i.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV = be(i.initData), (async () => {
|
|
672
672
|
try {
|
|
673
|
-
let
|
|
674
|
-
initDataTypes: [
|
|
673
|
+
let n = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
|
|
674
|
+
initDataTypes: [i.initDataType],
|
|
675
675
|
videoCapabilities: [{
|
|
676
676
|
contentType: "application/vnd.apple.mpegurl",
|
|
677
677
|
robustness: ""
|
|
@@ -679,37 +679,37 @@ var z = class {
|
|
|
679
679
|
distinctiveIdentifier: "not-allowed",
|
|
680
680
|
persistentState: "not-allowed",
|
|
681
681
|
sessionTypes: ["temporary"]
|
|
682
|
-
}])).createMediaKeys(), s = await fetch(c.certificateUrl, { headers: { "x-api-key":
|
|
682
|
+
}])).createMediaKeys(), s = await fetch(c.certificateUrl, { headers: { "x-api-key": r.apiKey } });
|
|
683
683
|
if (!s.ok) {
|
|
684
684
|
this.fairplayArmed = !1, l(`FairPlay certificate request failed: ${s.status}`, s.status, !0);
|
|
685
685
|
return;
|
|
686
686
|
}
|
|
687
|
-
await
|
|
688
|
-
let u =
|
|
687
|
+
await n.setServerCertificate(await s.arrayBuffer()), await e.setMediaKeys(n);
|
|
688
|
+
let u = n.createSession();
|
|
689
689
|
this.keySession = u, u.addEventListener("message", (e) => {
|
|
690
690
|
(async () => {
|
|
691
691
|
try {
|
|
692
|
-
let
|
|
692
|
+
let n = await fetch(this.fairplayLicenseUrl ?? c.licenseUrl, {
|
|
693
693
|
method: "POST",
|
|
694
694
|
body: e.message,
|
|
695
695
|
headers: {
|
|
696
|
-
"x-api-key":
|
|
697
|
-
"X-Match-Urn":
|
|
696
|
+
"x-api-key": r.apiKey,
|
|
697
|
+
"X-Match-Urn": t.matchUrn,
|
|
698
698
|
"X-Device-Id": a,
|
|
699
699
|
...o ? { "X-User-Id": o } : {},
|
|
700
700
|
...this.fairplayKeyIV ? { "X-Key-Iv": this.fairplayKeyIV } : {}
|
|
701
701
|
}
|
|
702
702
|
});
|
|
703
|
-
if (!
|
|
704
|
-
l(`FairPlay license request failed: ${
|
|
703
|
+
if (!n.ok) {
|
|
704
|
+
l(`FairPlay license request failed: ${n.status}`, n.status, !0);
|
|
705
705
|
return;
|
|
706
706
|
}
|
|
707
|
-
await u.update(new Uint8Array(await
|
|
707
|
+
await u.update(new Uint8Array(await n.arrayBuffer()));
|
|
708
708
|
} catch (e) {
|
|
709
709
|
l(`FairPlay license request failed: ${String(e)}`, 0, !0);
|
|
710
710
|
}
|
|
711
711
|
})();
|
|
712
|
-
}), await u.generateRequest(
|
|
712
|
+
}), await u.generateRequest(i.initDataType, i.initData);
|
|
713
713
|
} catch (e) {
|
|
714
714
|
this.fairplayArmed = !1, l(`FairPlay key session failed: ${String(e)}`, 0, !0);
|
|
715
715
|
}
|
|
@@ -783,25 +783,25 @@ var z = class {
|
|
|
783
783
|
} catch {}
|
|
784
784
|
}
|
|
785
785
|
};
|
|
786
|
-
function
|
|
786
|
+
function xe(e, t) {
|
|
787
787
|
return t ? `${e}${e.includes("?") ? "&" : "?"}CMCD=${encodeURIComponent(`sid="${t}"`)}` : e;
|
|
788
788
|
}
|
|
789
789
|
//#endregion
|
|
790
790
|
//#region src/core/engine/hls.ts
|
|
791
|
-
function
|
|
791
|
+
function Se() {
|
|
792
792
|
return e.isSupported();
|
|
793
793
|
}
|
|
794
|
-
var
|
|
795
|
-
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) {
|
|
796
796
|
if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
|
|
797
797
|
delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
|
|
798
798
|
return;
|
|
799
799
|
}
|
|
800
|
-
e.liveSyncDuration ??=
|
|
801
|
-
let t = Math.max(12, e.liveSyncDuration +
|
|
802
|
-
(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);
|
|
803
803
|
}
|
|
804
|
-
function
|
|
804
|
+
function Re(e, t) {
|
|
805
805
|
let n = (e, t, n) => Math.min(n, Math.max(t, e));
|
|
806
806
|
e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
|
|
807
807
|
}
|
|
@@ -834,27 +834,47 @@ var V = class {
|
|
|
834
834
|
chaseSuspended = !1;
|
|
835
835
|
savedLiveMaxLatency;
|
|
836
836
|
savedMaxSyncRate;
|
|
837
|
+
capObserver = null;
|
|
838
|
+
capTimer = null;
|
|
839
|
+
sizeCapLevel = null;
|
|
840
|
+
bitrateCapLevel = -1;
|
|
841
|
+
sizeCapManaged = !0;
|
|
842
|
+
dprMedia = null;
|
|
843
|
+
onDprChange = null;
|
|
837
844
|
constructor(e) {
|
|
838
845
|
this.deps = e, this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl;
|
|
839
846
|
}
|
|
840
847
|
async load() {
|
|
841
|
-
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;
|
|
842
849
|
if (e.drmEnabled && !o) {
|
|
843
850
|
i({
|
|
844
851
|
type: "error",
|
|
845
|
-
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)"),
|
|
846
853
|
fatal: !0
|
|
847
854
|
});
|
|
848
855
|
return;
|
|
849
856
|
}
|
|
850
|
-
this.startHls(),
|
|
857
|
+
this.startHls(), t !== !1 && typeof document < "u" && (this.onVisibility = () => {
|
|
851
858
|
let e = this.hls;
|
|
852
859
|
if (document.visibilityState !== "visible" || !e) return;
|
|
853
860
|
let t = e.liveSyncPosition;
|
|
854
861
|
if (typeof t != "number") return;
|
|
855
862
|
let n = typeof e.targetLatency == "number" && e.targetLatency > 0 ? e.targetLatency : 3;
|
|
856
863
|
t - r.currentTime > Math.max(n * 3, 8) && (r.currentTime = t);
|
|
857
|
-
}, 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;
|
|
858
878
|
}
|
|
859
879
|
clampOverSeek() {
|
|
860
880
|
let e = this.hls;
|
|
@@ -862,7 +882,7 @@ var V = class {
|
|
|
862
882
|
let t = e.liveSyncPosition;
|
|
863
883
|
if (typeof t != "number" || !Number.isFinite(t)) return;
|
|
864
884
|
let { video: n } = this.deps;
|
|
865
|
-
n.currentTime <= t +
|
|
885
|
+
n.currentTime <= t + Ie || (n.currentTime = t);
|
|
866
886
|
}
|
|
867
887
|
startHls() {
|
|
868
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 = {
|
|
@@ -873,7 +893,7 @@ var V = class {
|
|
|
873
893
|
maxBufferHole: .5,
|
|
874
894
|
maxLiveSyncPlaybackRate: 1.5,
|
|
875
895
|
abrEwmaDefaultEstimate: 1e6,
|
|
876
|
-
capLevelToPlayerSize: !
|
|
896
|
+
capLevelToPlayerSize: !1,
|
|
877
897
|
...u ? {
|
|
878
898
|
emeEnabled: !0,
|
|
879
899
|
drmSystems: { ...this.licenseUrl ? { "com.widevine.alpha": { licenseUrl: this.licenseUrl } } : {} },
|
|
@@ -898,14 +918,14 @@ var V = class {
|
|
|
898
918
|
} } : {},
|
|
899
919
|
...c
|
|
900
920
|
};
|
|
901
|
-
|
|
921
|
+
Le(d), this.sizeCapManaged = d.capLevelToPlayerSize !== !0;
|
|
902
922
|
let f = new e(d);
|
|
903
923
|
this.hls = f, f.on(e.Events.MANIFEST_PARSED, () => {
|
|
904
|
-
this.mediaRecoveries = 0, this.networkRecoveries = 0, l({ type: "ready" });
|
|
924
|
+
this.mediaRecoveries = 0, this.networkRecoveries = 0, this.applySizeCap(), l({ type: "ready" });
|
|
905
925
|
}), f.on(e.Events.FRAG_BUFFERED, (e, t) => {
|
|
906
926
|
t?.frag?.type === "main" && (this.mediaRecoveries > 0 || this.networkRecoveries > 0 || this.licenseRecoveries > 0 || this.starvationKicks > 0) && !this.recoveryResetTimer && (this.recoveryResetTimer = setTimeout(() => {
|
|
907
927
|
this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.recoveryResetTimer = null;
|
|
908
|
-
},
|
|
928
|
+
}, Ee));
|
|
909
929
|
}), f.on(e.Events.FRAG_LOADING, (e, t) => {
|
|
910
930
|
t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
|
|
911
931
|
}), f.on(e.Events.FRAG_LOADED, (e, t) => {
|
|
@@ -921,7 +941,7 @@ var V = class {
|
|
|
921
941
|
}));
|
|
922
942
|
}), f.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.endWatchdog = setInterval(() => {
|
|
923
943
|
this.checkEndStale(), this.checkStarvation();
|
|
924
|
-
},
|
|
944
|
+
}, Oe), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
|
|
925
945
|
this.renditionSwitches += 1, l({
|
|
926
946
|
type: "qualitychange",
|
|
927
947
|
index: t.level
|
|
@@ -951,15 +971,54 @@ var V = class {
|
|
|
951
971
|
}
|
|
952
972
|
setMaxBitrate(e) {
|
|
953
973
|
let t = this.hls;
|
|
954
|
-
if (
|
|
955
|
-
|
|
956
|
-
|
|
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();
|
|
957
999
|
return;
|
|
958
1000
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
}
|
|
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);
|
|
963
1022
|
}
|
|
964
1023
|
getAudioTracks() {
|
|
965
1024
|
return this.hls ? this.hls.audioTracks.map((e) => ({
|
|
@@ -1025,7 +1084,7 @@ var V = class {
|
|
|
1025
1084
|
return;
|
|
1026
1085
|
}
|
|
1027
1086
|
let e = Date.now() - this.lastAdvanceMs;
|
|
1028
|
-
!this.chaseSuspended && e > B && this.suspendChasing(), e >
|
|
1087
|
+
!this.chaseSuspended && e > B && this.suspendChasing(), e > De && this.emitEnded("stalled");
|
|
1029
1088
|
}
|
|
1030
1089
|
checkStarvation() {
|
|
1031
1090
|
let e = this.hls, { video: t } = this.deps;
|
|
@@ -1034,12 +1093,12 @@ var V = class {
|
|
|
1034
1093
|
this.starvedTicks = 0;
|
|
1035
1094
|
return;
|
|
1036
1095
|
}
|
|
1037
|
-
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;
|
|
1038
1097
|
if (!(t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused && n && r)) {
|
|
1039
1098
|
this.starvedTicks = 0;
|
|
1040
1099
|
return;
|
|
1041
1100
|
}
|
|
1042
|
-
if (!(++this.starvedTicks <
|
|
1101
|
+
if (!(++this.starvedTicks < Ae) && (this.starvedTicks = 0, !(this.starvationKicks >= je))) {
|
|
1043
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");
|
|
1044
1103
|
try {
|
|
1045
1104
|
e.stopLoad(), e.startLoad(-1);
|
|
@@ -1073,7 +1132,7 @@ var V = class {
|
|
|
1073
1132
|
} catch {}
|
|
1074
1133
|
}
|
|
1075
1134
|
destroy() {
|
|
1076
|
-
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) {
|
|
1077
1136
|
try {
|
|
1078
1137
|
this.hls.destroy();
|
|
1079
1138
|
} catch (e) {
|
|
@@ -1086,9 +1145,9 @@ var V = class {
|
|
|
1086
1145
|
this.recoveryResetTimer &&= (clearTimeout(this.recoveryResetTimer), null);
|
|
1087
1146
|
}
|
|
1088
1147
|
scheduleLicenseRetry(t) {
|
|
1089
|
-
if (
|
|
1148
|
+
if (t.details !== e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED && t.details !== e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED) return !1;
|
|
1090
1149
|
let n = t.response?.code ?? 0;
|
|
1091
|
-
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;
|
|
1092
1151
|
this.licenseRecoveries += 1;
|
|
1093
1152
|
let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
|
|
1094
1153
|
return this.licenseRetryTimer = setTimeout(() => {
|
|
@@ -1116,7 +1175,7 @@ var V = class {
|
|
|
1116
1175
|
return;
|
|
1117
1176
|
}
|
|
1118
1177
|
if (t.type === e.ErrorTypes.NETWORK_ERROR) {
|
|
1119
|
-
if (this.networkRecoveries <
|
|
1178
|
+
if (this.networkRecoveries < we) {
|
|
1120
1179
|
this.networkRecoveries += 1;
|
|
1121
1180
|
let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
|
|
1122
1181
|
this.networkRetryTimer = setTimeout(() => {
|
|
@@ -1132,7 +1191,7 @@ var V = class {
|
|
|
1132
1191
|
return;
|
|
1133
1192
|
}
|
|
1134
1193
|
if (t.type === e.ErrorTypes.MEDIA_ERROR) {
|
|
1135
|
-
if (this.mediaRecoveries <
|
|
1194
|
+
if (this.mediaRecoveries < Ce) {
|
|
1136
1195
|
this.mediaRecoveries += 1, n.recoverMediaError();
|
|
1137
1196
|
return;
|
|
1138
1197
|
}
|
|
@@ -1150,11 +1209,11 @@ var V = class {
|
|
|
1150
1209
|
});
|
|
1151
1210
|
}
|
|
1152
1211
|
}
|
|
1153
|
-
toError(e,
|
|
1154
|
-
let
|
|
1155
|
-
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})` : ""}`);
|
|
1156
1215
|
}
|
|
1157
|
-
},
|
|
1216
|
+
}, ze = 15e3, Be = 100, H = 1, U = {
|
|
1158
1217
|
idle: "PLAYER_STATE_IDLE",
|
|
1159
1218
|
waiting: "PLAYER_STATE_WAITING",
|
|
1160
1219
|
loading: "PLAYER_STATE_LOADING",
|
|
@@ -1164,7 +1223,7 @@ var V = class {
|
|
|
1164
1223
|
ended: "PLAYER_STATE_ENDED",
|
|
1165
1224
|
error: "PLAYER_STATE_ERROR"
|
|
1166
1225
|
};
|
|
1167
|
-
function
|
|
1226
|
+
function Ve(e) {
|
|
1168
1227
|
try {
|
|
1169
1228
|
let t = new URL(e), [n, ...r] = t.hostname.split(".");
|
|
1170
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);
|
|
@@ -1172,7 +1231,7 @@ function Re(e) {
|
|
|
1172
1231
|
return;
|
|
1173
1232
|
}
|
|
1174
1233
|
}
|
|
1175
|
-
var
|
|
1234
|
+
var He = class {
|
|
1176
1235
|
opts;
|
|
1177
1236
|
url;
|
|
1178
1237
|
now;
|
|
@@ -1207,7 +1266,7 @@ var ze = class {
|
|
|
1207
1266
|
constructor(e) {
|
|
1208
1267
|
this.opts = {
|
|
1209
1268
|
...e,
|
|
1210
|
-
heartbeatMs: e.heartbeatMs ??
|
|
1269
|
+
heartbeatMs: e.heartbeatMs ?? ze
|
|
1211
1270
|
}, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`, this.now = e.now ?? (() => Date.now()), this.bornMs = this.now();
|
|
1212
1271
|
}
|
|
1213
1272
|
start() {
|
|
@@ -1299,7 +1358,7 @@ var ze = class {
|
|
|
1299
1358
|
};
|
|
1300
1359
|
}
|
|
1301
1360
|
enqueue(e) {
|
|
1302
|
-
for (this.pending.push(this.wrap(e)); this.pending.length >
|
|
1361
|
+
for (this.pending.push(this.wrap(e)); this.pending.length > Be;) {
|
|
1303
1362
|
let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
|
|
1304
1363
|
this.pending.splice(e >= 0 ? e : 0, 1);
|
|
1305
1364
|
}
|
|
@@ -1321,7 +1380,7 @@ var ze = class {
|
|
|
1321
1380
|
keepalive: !0
|
|
1322
1381
|
}).catch(() => {});
|
|
1323
1382
|
}
|
|
1324
|
-
},
|
|
1383
|
+
}, Ue = "1.15.1", W = {
|
|
1325
1384
|
accent: "#E1B600",
|
|
1326
1385
|
accentText: "#1B1C23",
|
|
1327
1386
|
background: "#0e0f13",
|
|
@@ -1354,15 +1413,15 @@ function K(e, t) {
|
|
|
1354
1413
|
}
|
|
1355
1414
|
//#endregion
|
|
1356
1415
|
//#region src/ui/styles.ts
|
|
1357
|
-
var q = "havik-player-styles",
|
|
1358
|
-
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) {
|
|
1359
1418
|
if (e.getElementById(q)) return;
|
|
1360
1419
|
let t = e.createElement("style");
|
|
1361
|
-
t.id = q, t.textContent =
|
|
1420
|
+
t.id = q, t.textContent = We, e.head.appendChild(t);
|
|
1362
1421
|
}
|
|
1363
1422
|
//#endregion
|
|
1364
1423
|
//#region src/ui/icons.ts
|
|
1365
|
-
var
|
|
1424
|
+
var Ke = {
|
|
1366
1425
|
play: "<path d=\"M8 5v14l11-7z\"/>",
|
|
1367
1426
|
pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
|
|
1368
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\"/>",
|
|
@@ -1376,20 +1435,20 @@ var Ue = {
|
|
|
1376
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\"/>"
|
|
1377
1436
|
};
|
|
1378
1437
|
function J(e) {
|
|
1379
|
-
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>`;
|
|
1380
1439
|
}
|
|
1381
1440
|
//#endregion
|
|
1382
1441
|
//#region src/ui/controls.ts
|
|
1383
|
-
var
|
|
1442
|
+
var qe = 3e3, Je = /* @__PURE__ */ new Set([
|
|
1384
1443
|
"GONE",
|
|
1385
1444
|
"NOT_FOUND",
|
|
1386
1445
|
"INVALID_URN",
|
|
1387
1446
|
"UNAUTHORIZED"
|
|
1388
1447
|
]);
|
|
1389
|
-
function
|
|
1390
|
-
return new
|
|
1448
|
+
function Ye(e) {
|
|
1449
|
+
return new Xe(e);
|
|
1391
1450
|
}
|
|
1392
|
-
var
|
|
1451
|
+
var Xe = class {
|
|
1393
1452
|
root;
|
|
1394
1453
|
video;
|
|
1395
1454
|
player;
|
|
@@ -1429,7 +1488,7 @@ var qe = class {
|
|
|
1429
1488
|
scrubbing = !1;
|
|
1430
1489
|
offFns = [];
|
|
1431
1490
|
constructor(e) {
|
|
1432
|
-
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");
|
|
1433
1492
|
let t = (e, t, n) => {
|
|
1434
1493
|
let r = this.doc.createElement(e);
|
|
1435
1494
|
if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
|
|
@@ -1485,7 +1544,7 @@ var qe = class {
|
|
|
1485
1544
|
this.setVolumeFill(t), this.player.setVolume(t), t > 0 && e.muted && this.player.setMuted(!1);
|
|
1486
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) => {
|
|
1487
1546
|
this.lastError = e, this.statusKind = null, this.syncState();
|
|
1488
|
-
})), 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();
|
|
1489
1548
|
}
|
|
1490
1549
|
togglePlay() {
|
|
1491
1550
|
this.video.paused || this.video.ended ? this.player.play() : this.player.pause();
|
|
@@ -1516,7 +1575,7 @@ var qe = class {
|
|
|
1516
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);
|
|
1517
1576
|
} else {
|
|
1518
1577
|
let n = this.doc.createElement("div");
|
|
1519
|
-
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)) {
|
|
1520
1579
|
let e = this.doc.createElement("button");
|
|
1521
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);
|
|
1522
1581
|
}
|
|
@@ -1607,17 +1666,17 @@ var qe = class {
|
|
|
1607
1666
|
renderQualityMenu() {
|
|
1608
1667
|
let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
|
|
1609
1668
|
n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
|
|
1610
|
-
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)));
|
|
1611
1670
|
}
|
|
1612
1671
|
renderAudioMenu() {
|
|
1613
1672
|
let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
|
|
1614
1673
|
t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
|
|
1615
|
-
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)));
|
|
1616
1675
|
}
|
|
1617
1676
|
renderCcMenu() {
|
|
1618
1677
|
let e = this.player.getTextTracks(), t = this.doc.createElement("div");
|
|
1619
1678
|
t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
|
|
1620
|
-
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)));
|
|
1621
1680
|
}
|
|
1622
1681
|
onTracksChanged() {
|
|
1623
1682
|
this.openMenu && this.renderMenu(this.openMenu);
|
|
@@ -1636,14 +1695,18 @@ var qe = class {
|
|
|
1636
1695
|
this.pipBtn.setAttribute("aria-pressed", e ? "true" : "false");
|
|
1637
1696
|
}
|
|
1638
1697
|
async toggleFullscreen() {
|
|
1639
|
-
let e = this.root, t = this.doc;
|
|
1698
|
+
let e = this.root, t = this.doc, n = this.video;
|
|
1640
1699
|
try {
|
|
1641
|
-
|
|
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());
|
|
1642
1705
|
} catch {}
|
|
1643
1706
|
}
|
|
1644
1707
|
syncFullscreen() {
|
|
1645
|
-
let e = this.doc.fullscreenElement === this.root;
|
|
1646
|
-
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");
|
|
1647
1710
|
}
|
|
1648
1711
|
poke() {
|
|
1649
1712
|
this.show(), this.scheduleHide();
|
|
@@ -1723,27 +1786,27 @@ function Y(e) {
|
|
|
1723
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");
|
|
1724
1787
|
return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
|
|
1725
1788
|
}
|
|
1726
|
-
function
|
|
1789
|
+
function Ze(e) {
|
|
1727
1790
|
return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
|
|
1728
1791
|
}
|
|
1729
|
-
function
|
|
1792
|
+
function X(e) {
|
|
1730
1793
|
return e.name || e.lang || `Track ${e.id}`;
|
|
1731
1794
|
}
|
|
1732
|
-
function
|
|
1795
|
+
function Qe(e) {
|
|
1733
1796
|
return e.name || e.lang || `Track ${e.id}`;
|
|
1734
1797
|
}
|
|
1735
1798
|
//#endregion
|
|
1736
1799
|
//#region src/managed/index.ts
|
|
1737
|
-
var
|
|
1800
|
+
var $e = 6e3, Z = 45e3, et = 2e3, tt = 3, nt = 2e3, rt = 48e4;
|
|
1738
1801
|
async function Q(e) {
|
|
1739
|
-
if (!e.video || typeof e.video.play != "function") throw new
|
|
1740
|
-
if (!e.baseUrl) throw new
|
|
1741
|
-
if (!e.matchUrn) throw new
|
|
1742
|
-
if (!e.credential) throw new
|
|
1743
|
-
let
|
|
1744
|
-
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;
|
|
1745
1808
|
}
|
|
1746
|
-
var
|
|
1809
|
+
var it = class {
|
|
1747
1810
|
opts;
|
|
1748
1811
|
abort = new AbortController();
|
|
1749
1812
|
listeners = {};
|
|
@@ -1777,19 +1840,19 @@ var tt = class {
|
|
|
1777
1840
|
}
|
|
1778
1841
|
async start() {
|
|
1779
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");
|
|
1780
|
-
let e = ++this.loadGen,
|
|
1843
|
+
let e = ++this.loadGen, t = this.resolveOptions();
|
|
1781
1844
|
try {
|
|
1782
|
-
let
|
|
1783
|
-
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) {
|
|
1784
1847
|
let e = this._lastError;
|
|
1785
1848
|
throw this.destroy(), e;
|
|
1786
1849
|
}
|
|
1787
|
-
} catch (
|
|
1788
|
-
if (this.opts.waitForLive &&
|
|
1789
|
-
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);
|
|
1790
1853
|
return;
|
|
1791
1854
|
}
|
|
1792
|
-
throw
|
|
1855
|
+
throw t;
|
|
1793
1856
|
}
|
|
1794
1857
|
}
|
|
1795
1858
|
resolveOptions() {
|
|
@@ -1802,7 +1865,7 @@ var tt = class {
|
|
|
1802
1865
|
};
|
|
1803
1866
|
}
|
|
1804
1867
|
beginArming(e, t) {
|
|
1805
|
-
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);
|
|
1806
1869
|
n ? this.armWithSse(e, n, t) : this.beginArmingByPolling(e);
|
|
1807
1870
|
}
|
|
1808
1871
|
beginArmingByPolling(e) {
|
|
@@ -1812,57 +1875,57 @@ var tt = class {
|
|
|
1812
1875
|
waitForLive: this.buildWaitConfig(e)
|
|
1813
1876
|
}).then((t) => {
|
|
1814
1877
|
if (!(this.destroyed || this.abort.signal.aborted || e !== this.loadGen)) return this.onResolved(t, e);
|
|
1815
|
-
}).catch((
|
|
1816
|
-
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)));
|
|
1817
1880
|
});
|
|
1818
1881
|
}
|
|
1819
|
-
armWithSse(e,
|
|
1820
|
-
let i = !1,
|
|
1882
|
+
armWithSse(e, t, r) {
|
|
1883
|
+
let i = !1, a = !1, s = new AbortController();
|
|
1821
1884
|
this.armFallbackCtrl = s, this.setState("waiting");
|
|
1822
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);
|
|
1823
1886
|
setTimeout(() => {
|
|
1824
1887
|
i || this.destroyed || e !== this.loadGen || this.emit("waiting", {
|
|
1825
1888
|
phase: "tooEarly",
|
|
1826
|
-
retryInMs:
|
|
1889
|
+
retryInMs: Z,
|
|
1827
1890
|
attempt: 1,
|
|
1828
1891
|
elapsedMs: 0,
|
|
1829
1892
|
...d !== void 0 && d > Date.now() ? { ttkMs: d - Date.now() } : {}
|
|
1830
1893
|
});
|
|
1831
1894
|
}, 0);
|
|
1832
|
-
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) => {
|
|
1833
1896
|
if (!(i || f())) {
|
|
1834
1897
|
i = !0, this.closeArming(), s.abort();
|
|
1835
1898
|
try {
|
|
1836
|
-
await this.onResolved(
|
|
1899
|
+
await this.onResolved(t, e);
|
|
1837
1900
|
} catch (e) {
|
|
1838
1901
|
if (f()) return;
|
|
1839
|
-
this.fail(e instanceof
|
|
1902
|
+
this.fail(e instanceof n ? e : new n("INTERNAL", 0, String(e)));
|
|
1840
1903
|
}
|
|
1841
1904
|
}
|
|
1842
1905
|
}, m = () => {
|
|
1843
|
-
i ||
|
|
1906
|
+
i || a || f() || (a = !0, b({
|
|
1844
1907
|
...this.resolveOptions(),
|
|
1845
1908
|
signal: s.signal,
|
|
1846
1909
|
waitForLive: this.buildWaitConfig(e)
|
|
1847
1910
|
}).then((e) => p(e)).catch((e) => {
|
|
1848
|
-
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))));
|
|
1849
1912
|
}));
|
|
1850
1913
|
}, h = () => {
|
|
1851
1914
|
i || f() || y(this.resolveOptions()).then((e) => p(e)).catch((e) => {
|
|
1852
|
-
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))));
|
|
1853
1916
|
});
|
|
1854
1917
|
}, g = (e) => {
|
|
1855
1918
|
this.armSseTimer && clearTimeout(this.armSseTimer), this.armSseTimer = setTimeout(() => {
|
|
1856
1919
|
!i && !f() && m();
|
|
1857
1920
|
}, e);
|
|
1858
1921
|
};
|
|
1859
|
-
g(
|
|
1860
|
-
eventsBaseUrl:
|
|
1922
|
+
g($e), this.armingSub = C({
|
|
1923
|
+
eventsBaseUrl: t,
|
|
1861
1924
|
matchUrn: this.opts.matchUrn,
|
|
1862
1925
|
credential: this.opts.credential,
|
|
1863
|
-
onAlive: () => g(
|
|
1926
|
+
onAlive: () => g(Z),
|
|
1864
1927
|
onState: (e) => {
|
|
1865
|
-
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);
|
|
1866
1929
|
},
|
|
1867
1930
|
onError: () => m()
|
|
1868
1931
|
});
|
|
@@ -1882,11 +1945,11 @@ var tt = class {
|
|
|
1882
1945
|
async onResolved(e, t) {
|
|
1883
1946
|
if (this.destroyed || t !== this.loadGen) return;
|
|
1884
1947
|
this._descriptor = e, this.endedHandled = !1;
|
|
1885
|
-
let n = await
|
|
1948
|
+
let n = await s(this.opts.credential);
|
|
1886
1949
|
if (this.destroyed || t !== this.loadGen) return;
|
|
1887
1950
|
this.setState("loading");
|
|
1888
|
-
let r = this.createEngine(e, n), i =
|
|
1889
|
-
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);
|
|
1890
1953
|
});
|
|
1891
1954
|
if (await r.load(), this.destroyed || t !== this.loadGen) {
|
|
1892
1955
|
i(), r.destroy();
|
|
@@ -1902,14 +1965,14 @@ var tt = class {
|
|
|
1902
1965
|
if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachSeekListener?.(), this.opts.analytics === !1) return;
|
|
1903
1966
|
let r = e.analytics?.sid;
|
|
1904
1967
|
if (!r) return;
|
|
1905
|
-
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);
|
|
1906
1969
|
if (!i) return;
|
|
1907
|
-
let a = this.opts.video, o = new
|
|
1970
|
+
let a = this.opts.video, o = new He({
|
|
1908
1971
|
endpoint: i,
|
|
1909
1972
|
sid: r,
|
|
1910
1973
|
apiKey: n,
|
|
1911
1974
|
player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
|
|
1912
|
-
sdkVersion:
|
|
1975
|
+
sdkVersion: Ue,
|
|
1913
1976
|
sample: () => {
|
|
1914
1977
|
let e = this.engine?.getStats() ?? F, t = a.buffered.length ? Math.max(0, a.buffered.end(a.buffered.length - 1) - a.currentTime) : 0;
|
|
1915
1978
|
return {
|
|
@@ -1924,14 +1987,14 @@ var tt = class {
|
|
|
1924
1987
|
let s = () => o.noteSeek();
|
|
1925
1988
|
a.addEventListener("seeking", s), this.detachSeekListener = () => a.removeEventListener("seeking", s), o.start();
|
|
1926
1989
|
}
|
|
1927
|
-
createEngine(e,
|
|
1990
|
+
createEngine(e, t) {
|
|
1928
1991
|
let r = { ...this.opts.hlsConfig };
|
|
1929
1992
|
this.opts.startLevel != null && (r.startLevel = this.opts.startLevel), this.opts.liveLatencyTarget != null && (r.liveSyncDuration = this.opts.liveLatencyTarget);
|
|
1930
1993
|
let i = {
|
|
1931
1994
|
video: this.opts.video,
|
|
1932
1995
|
descriptor: e,
|
|
1933
|
-
credential:
|
|
1934
|
-
deviceId:
|
|
1996
|
+
credential: t,
|
|
1997
|
+
deviceId: D(),
|
|
1935
1998
|
userId: this.opts.userId,
|
|
1936
1999
|
lowLatency: this.opts.lowLatency === void 0 || this.opts.lowLatency === "auto" || this.opts.lowLatency,
|
|
1937
2000
|
snapToLiveOnRefocus: this.opts.snapToLiveOnRefocus,
|
|
@@ -1942,9 +2005,9 @@ var tt = class {
|
|
|
1942
2005
|
if (a === "native") return new z(i);
|
|
1943
2006
|
if (a === "hls") return new V(i);
|
|
1944
2007
|
if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && L(this.opts.video) && R()) return new z(i);
|
|
1945
|
-
if (
|
|
2008
|
+
if (Se()) return new V(i);
|
|
1946
2009
|
if (L(this.opts.video)) return new z(i);
|
|
1947
|
-
throw new
|
|
2010
|
+
throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
|
|
1948
2011
|
}
|
|
1949
2012
|
onEngineEvent(e) {
|
|
1950
2013
|
if (!this.destroyed) switch (e.type) {
|
|
@@ -1966,19 +2029,17 @@ var tt = class {
|
|
|
1966
2029
|
case "ended":
|
|
1967
2030
|
this.onStreamEnded();
|
|
1968
2031
|
break;
|
|
1969
|
-
case "error":
|
|
1970
|
-
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);
|
|
1971
|
-
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);
|
|
1972
2033
|
}
|
|
1973
2034
|
}
|
|
1974
2035
|
startLiveState(e) {
|
|
1975
2036
|
if (this.opts.liveStateEvents === !1) return;
|
|
1976
|
-
let
|
|
1977
|
-
if (!
|
|
2037
|
+
let t = this.opts.eventsBaseUrl ?? w(this.opts.baseUrl);
|
|
2038
|
+
if (!t) return;
|
|
1978
2039
|
this.closeLiveState();
|
|
1979
2040
|
let r = 0, i = !1;
|
|
1980
|
-
this.liveStateSub =
|
|
1981
|
-
eventsBaseUrl:
|
|
2041
|
+
this.liveStateSub = C({
|
|
2042
|
+
eventsBaseUrl: t,
|
|
1982
2043
|
matchUrn: this.opts.matchUrn,
|
|
1983
2044
|
credential: this.opts.credential,
|
|
1984
2045
|
onState: (t) => {
|
|
@@ -1988,7 +2049,7 @@ var tt = class {
|
|
|
1988
2049
|
r = 0, i = !1;
|
|
1989
2050
|
},
|
|
1990
2051
|
onError: () => {
|
|
1991
|
-
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.`))));
|
|
1992
2053
|
}
|
|
1993
2054
|
});
|
|
1994
2055
|
}
|
|
@@ -2010,7 +2071,7 @@ var tt = class {
|
|
|
2010
2071
|
}
|
|
2011
2072
|
t.controls = !1;
|
|
2012
2073
|
let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
|
|
2013
|
-
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({
|
|
2014
2075
|
root: n,
|
|
2015
2076
|
video: t,
|
|
2016
2077
|
player: this,
|
|
@@ -2033,7 +2094,7 @@ var tt = class {
|
|
|
2033
2094
|
}
|
|
2034
2095
|
startStats() {
|
|
2035
2096
|
this.stopStats();
|
|
2036
|
-
let e = Math.max(500, this.opts.statsIntervalMs ??
|
|
2097
|
+
let e = Math.max(500, this.opts.statsIntervalMs ?? nt);
|
|
2037
2098
|
this.statsTimer = setInterval(() => {
|
|
2038
2099
|
if (!this.engine) return;
|
|
2039
2100
|
let e = this.engine.getStats();
|
|
@@ -2045,7 +2106,7 @@ var tt = class {
|
|
|
2045
2106
|
}
|
|
2046
2107
|
scheduleLicenseRefresh() {
|
|
2047
2108
|
this.stopRefresh();
|
|
2048
|
-
let e = this.opts.licenseRefreshMs ??
|
|
2109
|
+
let e = this.opts.licenseRefreshMs ?? rt;
|
|
2049
2110
|
!e || !this._descriptor?.drmEnabled || (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
|
|
2050
2111
|
}
|
|
2051
2112
|
stopRefresh() {
|
|
@@ -2062,7 +2123,7 @@ var tt = class {
|
|
|
2062
2123
|
fairplay: r
|
|
2063
2124
|
}), this._descriptor = t);
|
|
2064
2125
|
} catch (e) {
|
|
2065
|
-
e instanceof
|
|
2126
|
+
e instanceof n && !o(e.code) && (this.stopRefresh(), this.destroyed || this.emit("warning", e));
|
|
2066
2127
|
}
|
|
2067
2128
|
}
|
|
2068
2129
|
async play() {
|
|
@@ -2133,9 +2194,9 @@ var tt = class {
|
|
|
2133
2194
|
waitForLive: this.opts.waitForLive ? this.buildWaitConfig(e) : void 0
|
|
2134
2195
|
});
|
|
2135
2196
|
if (await this.onResolved(t, e), e === this.loadGen && this._state === "error" && this._lastError) throw this._lastError;
|
|
2136
|
-
} catch (
|
|
2197
|
+
} catch (t) {
|
|
2137
2198
|
if (this.destroyed || this.abort.signal.aborted || e !== this.loadGen) return;
|
|
2138
|
-
throw
|
|
2199
|
+
throw t !== this._lastError && this.fail(t instanceof n ? t : new n("INTERNAL", 0, String(t))), t;
|
|
2139
2200
|
}
|
|
2140
2201
|
}
|
|
2141
2202
|
on(e, t) {
|
|
@@ -2168,10 +2229,7 @@ var tt = class {
|
|
|
2168
2229
|
case "paused":
|
|
2169
2230
|
this.emit("paused");
|
|
2170
2231
|
break;
|
|
2171
|
-
case "ended":
|
|
2172
|
-
this.emit("ended");
|
|
2173
|
-
break;
|
|
2174
|
-
default: break;
|
|
2232
|
+
case "ended": this.emit("ended");
|
|
2175
2233
|
}
|
|
2176
2234
|
}
|
|
2177
2235
|
emit(e, t) {
|
|
@@ -2189,7 +2247,7 @@ function $(e) {
|
|
|
2189
2247
|
}
|
|
2190
2248
|
//#endregion
|
|
2191
2249
|
//#region src/embed/host.ts
|
|
2192
|
-
function
|
|
2250
|
+
function at(e) {
|
|
2193
2251
|
if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
|
|
2194
2252
|
if (!e.src) throw Error("mountEmbed: opts.src is required");
|
|
2195
2253
|
if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
|
|
@@ -2248,8 +2306,8 @@ function nt(e) {
|
|
|
2248
2306
|
}
|
|
2249
2307
|
//#endregion
|
|
2250
2308
|
//#region src/embed/page.ts
|
|
2251
|
-
var
|
|
2252
|
-
function
|
|
2309
|
+
var ot = /^[A-Za-z0-9:_.-]{1,128}$/;
|
|
2310
|
+
function st() {
|
|
2253
2311
|
let e = window.HAVIK_EMBED_CONFIG;
|
|
2254
2312
|
if (e) return {
|
|
2255
2313
|
baseUrl: e.baseUrl ?? "",
|
|
@@ -2271,11 +2329,11 @@ function it() {
|
|
|
2271
2329
|
waitForLive: !0
|
|
2272
2330
|
};
|
|
2273
2331
|
}
|
|
2274
|
-
async function
|
|
2275
|
-
let e =
|
|
2276
|
-
|
|
2277
|
-
let
|
|
2278
|
-
|
|
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);
|
|
2279
2337
|
let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
|
|
2280
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.");
|
|
2281
2339
|
let i = (e) => {
|
|
@@ -2328,13 +2386,13 @@ async function at() {
|
|
|
2328
2386
|
code: e.code,
|
|
2329
2387
|
message: e.message
|
|
2330
2388
|
})), e.on("autoplayblocked", () => {
|
|
2331
|
-
i({ type: "oddin:autoplayblocked" }),
|
|
2389
|
+
i({ type: "oddin:autoplayblocked" }), lt(t, e);
|
|
2332
2390
|
});
|
|
2333
2391
|
}, s = async (r) => {
|
|
2334
2392
|
a?.destroy(), a = null;
|
|
2335
2393
|
try {
|
|
2336
|
-
let
|
|
2337
|
-
video:
|
|
2394
|
+
let n = await Q({
|
|
2395
|
+
video: t,
|
|
2338
2396
|
baseUrl: e.baseUrl,
|
|
2339
2397
|
matchUrn: r,
|
|
2340
2398
|
credential: { apiKey: e.apiKey },
|
|
@@ -2342,14 +2400,14 @@ async function at() {
|
|
|
2342
2400
|
muted: e.muted,
|
|
2343
2401
|
waitForLive: e.waitForLive
|
|
2344
2402
|
});
|
|
2345
|
-
a =
|
|
2346
|
-
let i =
|
|
2347
|
-
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);
|
|
2348
2406
|
} catch (e) {
|
|
2349
|
-
let
|
|
2407
|
+
let t = e instanceof n ? e.code : "INTERNAL", r = e instanceof Error ? e.message : String(e);
|
|
2350
2408
|
i({
|
|
2351
2409
|
type: "oddin:error",
|
|
2352
|
-
code:
|
|
2410
|
+
code: t,
|
|
2353
2411
|
message: r
|
|
2354
2412
|
});
|
|
2355
2413
|
}
|
|
@@ -2394,11 +2452,9 @@ async function at() {
|
|
|
2394
2452
|
a?.retry();
|
|
2395
2453
|
break;
|
|
2396
2454
|
case "oddin:load":
|
|
2397
|
-
typeof e.matchUrn == "string" &&
|
|
2398
|
-
break;
|
|
2399
|
-
case "oddin:destroy":
|
|
2400
|
-
c();
|
|
2455
|
+
typeof e.matchUrn == "string" && ot.test(e.matchUrn) && s(e.matchUrn);
|
|
2401
2456
|
break;
|
|
2457
|
+
case "oddin:destroy": c();
|
|
2402
2458
|
}
|
|
2403
2459
|
}, u = (e) => {
|
|
2404
2460
|
!r || e.origin !== r || $(e.data) && l(e.data);
|
|
@@ -2408,18 +2464,18 @@ async function at() {
|
|
|
2408
2464
|
protocol: 1
|
|
2409
2465
|
});
|
|
2410
2466
|
}
|
|
2411
|
-
function
|
|
2467
|
+
function lt(e, t) {
|
|
2412
2468
|
if (document.getElementById("havik-tap")) return;
|
|
2413
2469
|
let n = document.createElement("button");
|
|
2414
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", () => {
|
|
2415
2471
|
n.remove(), t.play();
|
|
2416
2472
|
}), document.body.appendChild(n);
|
|
2417
2473
|
}
|
|
2418
|
-
function
|
|
2474
|
+
function ut() {
|
|
2419
2475
|
let e = document.createElement("style");
|
|
2420
2476
|
e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
|
|
2421
2477
|
}
|
|
2422
2478
|
//#endregion
|
|
2423
|
-
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 };
|
|
2424
2480
|
|
|
2425
2481
|
//# sourceMappingURL=index.js.map
|