@oddin-gg/havik-player 1.8.0 → 1.8.2
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 +25 -7
- package/dist/havik-player.global.js.map +1 -1
- package/dist/index.cjs +26 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +134 -124
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -171,7 +171,7 @@ async function y(e) {
|
|
|
171
171
|
signal: e.signal
|
|
172
172
|
});
|
|
173
173
|
if (!n.ok) throw await ie(n);
|
|
174
|
-
return
|
|
174
|
+
return S(await n.json(), e.matchUrn);
|
|
175
175
|
}
|
|
176
176
|
function b(e) {
|
|
177
177
|
let t = () => y(e);
|
|
@@ -193,7 +193,7 @@ async function ie(e) {
|
|
|
193
193
|
liveStartsAtMs: c
|
|
194
194
|
});
|
|
195
195
|
}
|
|
196
|
-
var
|
|
196
|
+
var ae = /* @__PURE__ */ new Set([
|
|
197
197
|
"matchUrn",
|
|
198
198
|
"protocol",
|
|
199
199
|
"drmEnabled",
|
|
@@ -203,7 +203,7 @@ var x = /* @__PURE__ */ new Set([
|
|
|
203
203
|
"liveStartsAt",
|
|
204
204
|
"expiresAt"
|
|
205
205
|
]);
|
|
206
|
-
function
|
|
206
|
+
function x(e, n) {
|
|
207
207
|
let r;
|
|
208
208
|
try {
|
|
209
209
|
r = new URL(n);
|
|
@@ -213,29 +213,29 @@ function S(e, n) {
|
|
|
213
213
|
if (r.protocol !== "https:" && r.protocol !== "http:") throw new t("INTERNAL", 0, `playback ${e} has a disallowed scheme: ${r.protocol}`);
|
|
214
214
|
return n;
|
|
215
215
|
}
|
|
216
|
-
function
|
|
216
|
+
function S(e, n) {
|
|
217
217
|
if (!e.manifestUrl) throw new t("INTERNAL", 0, "playback response is missing manifestUrl");
|
|
218
218
|
let r = e.drmEnabled ?? !!e.drm, i = {
|
|
219
219
|
matchUrn: e.matchUrn ?? n,
|
|
220
220
|
protocol: "hls",
|
|
221
221
|
drmEnabled: r,
|
|
222
|
-
manifestUrl:
|
|
222
|
+
manifestUrl: x("manifestUrl", e.manifestUrl),
|
|
223
223
|
serverTime: e.serverTime ?? ""
|
|
224
224
|
};
|
|
225
225
|
if (e.liveStartsAt && (i.liveStartsAt = e.liveStartsAt), r && e.drm) {
|
|
226
226
|
let t = {};
|
|
227
|
-
e.drm.widevine?.licenseUrl && (t.widevine = { licenseUrl:
|
|
228
|
-
licenseUrl:
|
|
229
|
-
certificateUrl:
|
|
227
|
+
e.drm.widevine?.licenseUrl && (t.widevine = { licenseUrl: x("widevine licenseUrl", e.drm.widevine.licenseUrl) }), e.drm.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && (t.fairplay = {
|
|
228
|
+
licenseUrl: x("fairplay licenseUrl", e.drm.fairplay.licenseUrl),
|
|
229
|
+
certificateUrl: x("fairplay certificateUrl", e.drm.fairplay.certificateUrl)
|
|
230
230
|
}), (t.widevine || t.fairplay) && (i.drm = t);
|
|
231
231
|
}
|
|
232
232
|
let a = {};
|
|
233
|
-
for (let [t, n] of Object.entries(e))
|
|
233
|
+
for (let [t, n] of Object.entries(e)) ae.has(t) || (a[t] = n);
|
|
234
234
|
return Object.keys(a).length > 0 && (i.extensions = a), i;
|
|
235
235
|
}
|
|
236
236
|
//#endregion
|
|
237
237
|
//#region src/core/catalog.ts
|
|
238
|
-
async function
|
|
238
|
+
async function C(e) {
|
|
239
239
|
let r = new URLSearchParams();
|
|
240
240
|
e.status && r.set("status", Array.isArray(e.status) ? e.status.join(",") : e.status), e.sport && r.set("sport", e.sport);
|
|
241
241
|
let i = r.toString(), a = o(e.baseUrl, "/v1/catalog" + (i ? `?${i}` : "")), s = new Headers();
|
|
@@ -252,12 +252,12 @@ async function w(e) {
|
|
|
252
252
|
};
|
|
253
253
|
if (!l.ok) throw new t(n(l.status), l.status, `catalog request failed (HTTP ${l.status})`);
|
|
254
254
|
return {
|
|
255
|
-
tournaments:
|
|
255
|
+
tournaments: oe(await l.json()),
|
|
256
256
|
etag: l.headers.get("etag") ?? void 0,
|
|
257
257
|
notModified: !1
|
|
258
258
|
};
|
|
259
259
|
}
|
|
260
|
-
function
|
|
260
|
+
function oe(e) {
|
|
261
261
|
return (e.tournaments ?? []).map((e) => {
|
|
262
262
|
let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
|
|
263
263
|
return {
|
|
@@ -277,7 +277,7 @@ function ae(e) {
|
|
|
277
277
|
};
|
|
278
278
|
});
|
|
279
279
|
}
|
|
280
|
-
async function
|
|
280
|
+
async function se(e) {
|
|
281
281
|
let r = o(e.baseUrl, `/v1/tournaments/${encodeURIComponent(e.urn)}`), i = new Headers();
|
|
282
282
|
e.etag && i.set("If-None-Match", e.etag);
|
|
283
283
|
let a = await c(r, {
|
|
@@ -296,13 +296,13 @@ async function oe(e) {
|
|
|
296
296
|
};
|
|
297
297
|
if (!a.ok) throw new t(n(a.status), a.status, `tournament request failed (HTTP ${a.status})`);
|
|
298
298
|
return {
|
|
299
|
-
tournament:
|
|
299
|
+
tournament: ce(await a.json()),
|
|
300
300
|
etag: a.headers.get("etag") ?? void 0,
|
|
301
301
|
notModified: !1,
|
|
302
302
|
notFound: !1
|
|
303
303
|
};
|
|
304
304
|
}
|
|
305
|
-
function
|
|
305
|
+
function ce(e) {
|
|
306
306
|
let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
|
|
307
307
|
return {
|
|
308
308
|
urn: t,
|
|
@@ -320,12 +320,12 @@ function se(e) {
|
|
|
320
320
|
}))
|
|
321
321
|
};
|
|
322
322
|
}
|
|
323
|
-
function
|
|
323
|
+
function le(e) {
|
|
324
324
|
return e.tournaments.flatMap((e) => e.matches);
|
|
325
325
|
}
|
|
326
326
|
//#endregion
|
|
327
327
|
//#region src/core/poll.ts
|
|
328
|
-
function
|
|
328
|
+
function ue(e) {
|
|
329
329
|
let r = Math.max(2e3, e.intervalMs ?? 1e4), i = new AbortController(), a, s, l, u = !1, d = () => {
|
|
330
330
|
u || (a = setTimeout(f, r));
|
|
331
331
|
}, f = async () => {
|
|
@@ -360,7 +360,7 @@ function le(e) {
|
|
|
360
360
|
}
|
|
361
361
|
//#endregion
|
|
362
362
|
//#region src/core/events.ts
|
|
363
|
-
function
|
|
363
|
+
function w(e) {
|
|
364
364
|
let t = Math.max(250, e.minBackoffMs ?? 1e3), n = Math.max(t, e.maxBackoffMs ?? 15e3), r = new AbortController(), i = !1, s = (e) => new Promise((t) => {
|
|
365
365
|
if (r.signal.aborted) return t();
|
|
366
366
|
let n = setTimeout(t, e);
|
|
@@ -427,7 +427,7 @@ function T(e) {
|
|
|
427
427
|
i || (i = !0, r.abort());
|
|
428
428
|
} };
|
|
429
429
|
}
|
|
430
|
-
function
|
|
430
|
+
function T(e) {
|
|
431
431
|
try {
|
|
432
432
|
let t = new URL(e);
|
|
433
433
|
return t.hostname = `events.${t.hostname}`, t.pathname = "", t.search = "", t.origin;
|
|
@@ -437,27 +437,27 @@ function E(e) {
|
|
|
437
437
|
}
|
|
438
438
|
//#endregion
|
|
439
439
|
//#region src/core/drm/deviceId.ts
|
|
440
|
-
var
|
|
441
|
-
function
|
|
442
|
-
if (
|
|
440
|
+
var E = "havik.player.deviceId", D;
|
|
441
|
+
function O() {
|
|
442
|
+
if (D) return D;
|
|
443
443
|
try {
|
|
444
|
-
let e = globalThis.localStorage?.getItem(
|
|
445
|
-
if (e) return
|
|
444
|
+
let e = globalThis.localStorage?.getItem(E);
|
|
445
|
+
if (e) return D = e, e;
|
|
446
446
|
} catch {}
|
|
447
|
-
let e =
|
|
448
|
-
|
|
447
|
+
let e = fe();
|
|
448
|
+
D = e;
|
|
449
449
|
try {
|
|
450
|
-
globalThis.localStorage?.setItem(
|
|
450
|
+
globalThis.localStorage?.setItem(E, e);
|
|
451
451
|
} catch {}
|
|
452
452
|
return e;
|
|
453
453
|
}
|
|
454
|
-
function
|
|
455
|
-
|
|
454
|
+
function de() {
|
|
455
|
+
D = void 0;
|
|
456
456
|
try {
|
|
457
|
-
globalThis.localStorage?.removeItem(
|
|
457
|
+
globalThis.localStorage?.removeItem(E);
|
|
458
458
|
} catch {}
|
|
459
459
|
}
|
|
460
|
-
function
|
|
460
|
+
function fe() {
|
|
461
461
|
let e = globalThis.crypto;
|
|
462
462
|
if (e && typeof e.randomUUID == "function") return e.randomUUID();
|
|
463
463
|
let t = /* @__PURE__ */ new Uint8Array(16);
|
|
@@ -469,22 +469,22 @@ function de() {
|
|
|
469
469
|
}
|
|
470
470
|
//#endregion
|
|
471
471
|
//#region src/core/drm/headers.ts
|
|
472
|
-
function
|
|
472
|
+
function pe(e, t, n = {}) {
|
|
473
473
|
let r = {
|
|
474
474
|
"x-api-key": t.apiKey,
|
|
475
475
|
"X-Match-Urn": e.matchUrn,
|
|
476
|
-
"X-Device-Id": n.deviceId ??
|
|
476
|
+
"X-Device-Id": n.deviceId ?? O(),
|
|
477
477
|
"Content-Type": "application/octet-stream"
|
|
478
478
|
};
|
|
479
479
|
return n.userId && (r["X-User-Id"] = n.userId), r;
|
|
480
480
|
}
|
|
481
481
|
//#endregion
|
|
482
482
|
//#region src/core/drm/support.ts
|
|
483
|
-
var
|
|
483
|
+
var me = 5e3, k = "video/mp4; codecs=\"avc1.42E01E\"", A = "audio/mp4; codecs=\"mp4a.40.2\"", j = {
|
|
484
484
|
widevine: ["com.widevine.alpha"],
|
|
485
485
|
fairplay: ["com.apple.fps"]
|
|
486
486
|
};
|
|
487
|
-
function
|
|
487
|
+
function M(e) {
|
|
488
488
|
return e === "fairplay" ? [{
|
|
489
489
|
initDataTypes: [
|
|
490
490
|
"sinf",
|
|
@@ -492,30 +492,30 @@ function N(e) {
|
|
|
492
492
|
"cenc"
|
|
493
493
|
],
|
|
494
494
|
videoCapabilities: [{
|
|
495
|
-
contentType:
|
|
495
|
+
contentType: k,
|
|
496
496
|
robustness: "",
|
|
497
497
|
encryptionScheme: "cbcs"
|
|
498
498
|
}],
|
|
499
499
|
audioCapabilities: [{
|
|
500
|
-
contentType:
|
|
500
|
+
contentType: A,
|
|
501
501
|
robustness: "",
|
|
502
502
|
encryptionScheme: "cbcs"
|
|
503
503
|
}]
|
|
504
504
|
}] : [{
|
|
505
505
|
initDataTypes: ["cenc"],
|
|
506
506
|
videoCapabilities: [{
|
|
507
|
-
contentType:
|
|
507
|
+
contentType: k,
|
|
508
508
|
robustness: "SW_SECURE_DECODE",
|
|
509
509
|
encryptionScheme: "cbcs"
|
|
510
510
|
}],
|
|
511
511
|
audioCapabilities: [{
|
|
512
|
-
contentType:
|
|
512
|
+
contentType: A,
|
|
513
513
|
robustness: "SW_SECURE_CRYPTO",
|
|
514
514
|
encryptionScheme: "cbcs"
|
|
515
515
|
}]
|
|
516
516
|
}];
|
|
517
517
|
}
|
|
518
|
-
function
|
|
518
|
+
function N(e, t) {
|
|
519
519
|
return new Promise((n, r) => {
|
|
520
520
|
let i = setTimeout(() => r(/* @__PURE__ */ Error("probe-timeout")), t);
|
|
521
521
|
e.then((e) => {
|
|
@@ -525,8 +525,8 @@ function P(e, t) {
|
|
|
525
525
|
});
|
|
526
526
|
});
|
|
527
527
|
}
|
|
528
|
-
async function
|
|
529
|
-
let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ??
|
|
528
|
+
async function P(e = {}) {
|
|
529
|
+
let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? me, r = {};
|
|
530
530
|
if (typeof window < "u" && window.isSecureContext === !1) return {
|
|
531
531
|
verdict: "insecure-context",
|
|
532
532
|
detail: r
|
|
@@ -538,7 +538,7 @@ async function F(e = {}) {
|
|
|
538
538
|
};
|
|
539
539
|
let a = !1, o = !1, s = Date.now() + n, c = () => Math.max(1, s - Date.now());
|
|
540
540
|
for (let e of t) {
|
|
541
|
-
let t =
|
|
541
|
+
let t = j[e];
|
|
542
542
|
if (!t) {
|
|
543
543
|
r[e] = "unknown system";
|
|
544
544
|
continue;
|
|
@@ -549,7 +549,7 @@ async function F(e = {}) {
|
|
|
549
549
|
break;
|
|
550
550
|
}
|
|
551
551
|
try {
|
|
552
|
-
return await
|
|
552
|
+
return await N((await N(i(n, M(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
|
|
553
553
|
verdict: "supported",
|
|
554
554
|
system: e,
|
|
555
555
|
detail: r
|
|
@@ -573,17 +573,17 @@ async function F(e = {}) {
|
|
|
573
573
|
}
|
|
574
574
|
//#endregion
|
|
575
575
|
//#region src/core/manifest.ts
|
|
576
|
-
var
|
|
577
|
-
function
|
|
578
|
-
return
|
|
576
|
+
var F = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
|
|
577
|
+
function I(e) {
|
|
578
|
+
return F.test(e);
|
|
579
579
|
}
|
|
580
580
|
//#endregion
|
|
581
581
|
//#region src/core/stats.ts
|
|
582
|
-
var
|
|
583
|
-
function
|
|
582
|
+
var L = { droppedFrames: 0 };
|
|
583
|
+
function R(e) {
|
|
584
584
|
return e.getVideoPlaybackQuality?.()?.droppedVideoFrames ?? 0;
|
|
585
585
|
}
|
|
586
|
-
function
|
|
586
|
+
function z(e, t) {
|
|
587
587
|
let n = [
|
|
588
588
|
["playing", () => t("playing")],
|
|
589
589
|
["waiting", () => t("buffering")],
|
|
@@ -600,7 +600,7 @@ function B(e, t) {
|
|
|
600
600
|
}
|
|
601
601
|
//#endregion
|
|
602
602
|
//#region src/core/engine/native.ts
|
|
603
|
-
function
|
|
603
|
+
function he(e) {
|
|
604
604
|
try {
|
|
605
605
|
let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
|
|
606
606
|
return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
|
|
@@ -608,9 +608,14 @@ function me(e) {
|
|
|
608
608
|
return;
|
|
609
609
|
}
|
|
610
610
|
}
|
|
611
|
-
function
|
|
611
|
+
function B(e) {
|
|
612
612
|
return !!(e ?? (typeof document < "u" ? document.createElement("video") : void 0))?.canPlayType("application/vnd.apple.mpegurl");
|
|
613
613
|
}
|
|
614
|
+
function V() {
|
|
615
|
+
if (typeof window > "u") return !1;
|
|
616
|
+
let e = "WebKitMediaKeys" in window || typeof HTMLMediaElement < "u" && "webkitSetMediaKeys" in HTMLMediaElement.prototype, t = typeof navigator < "u" && /Apple/i.test(navigator.vendor ?? "");
|
|
617
|
+
return e || t;
|
|
618
|
+
}
|
|
614
619
|
var H = class {
|
|
615
620
|
name = "native";
|
|
616
621
|
deps;
|
|
@@ -654,7 +659,7 @@ var H = class {
|
|
|
654
659
|
});
|
|
655
660
|
this.onEncrypted = (t) => {
|
|
656
661
|
let o = t;
|
|
657
|
-
!o.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV =
|
|
662
|
+
!o.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV = he(o.initData), (async () => {
|
|
658
663
|
try {
|
|
659
664
|
let t = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
|
|
660
665
|
initDataTypes: [o.initDataType],
|
|
@@ -746,7 +751,7 @@ var H = class {
|
|
|
746
751
|
e.seekable.length > 0 && (e.currentTime = e.seekable.end(e.seekable.length - 1));
|
|
747
752
|
}
|
|
748
753
|
getStats() {
|
|
749
|
-
let e = this.deps.video, t = { droppedFrames:
|
|
754
|
+
let e = this.deps.video, t = { droppedFrames: R(e) };
|
|
750
755
|
return e.duration === Infinity && (t.isLive = !0, e.seekable.length > 0 && (t.atLiveEdge = e.currentTime >= e.seekable.end(e.seekable.length - 1) - 2)), t;
|
|
751
756
|
}
|
|
752
757
|
stop() {
|
|
@@ -768,20 +773,20 @@ var H = class {
|
|
|
768
773
|
};
|
|
769
774
|
//#endregion
|
|
770
775
|
//#region src/core/engine/hls.ts
|
|
771
|
-
function
|
|
776
|
+
function ge() {
|
|
772
777
|
return e.isSupported();
|
|
773
778
|
}
|
|
774
|
-
var
|
|
775
|
-
function
|
|
779
|
+
var _e = 3, ve = 5, ye = 3, be = 3e4, xe = 12e3, Se = 3e3, Ce = 6e3, we = 2, Te = 9, Ee = .5;
|
|
780
|
+
function De(e) {
|
|
776
781
|
if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
|
|
777
782
|
delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
|
|
778
783
|
return;
|
|
779
784
|
}
|
|
780
|
-
e.liveSyncDuration ??=
|
|
781
|
-
let t = Math.max(12, e.liveSyncDuration +
|
|
782
|
-
(e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t),
|
|
785
|
+
e.liveSyncDuration ??= we;
|
|
786
|
+
let t = Math.max(12, e.liveSyncDuration + Te);
|
|
787
|
+
(e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Oe(e, e.liveSyncDuration);
|
|
783
788
|
}
|
|
784
|
-
function
|
|
789
|
+
function Oe(e, t) {
|
|
785
790
|
let n = (e, t, n) => Math.min(n, Math.max(t, e));
|
|
786
791
|
e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
|
|
787
792
|
}
|
|
@@ -811,7 +816,7 @@ var U = class {
|
|
|
811
816
|
this.deps = e, this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl;
|
|
812
817
|
}
|
|
813
818
|
async load() {
|
|
814
|
-
let { descriptor: e, snapToLiveOnRefocus: n, video: r, emit: i } = this.deps, a =
|
|
819
|
+
let { descriptor: e, snapToLiveOnRefocus: n, video: r, emit: i } = this.deps, a = B(r) && V(), o = !!e.drm?.widevine?.licenseUrl && !a;
|
|
815
820
|
if (e.drmEnabled && !o) {
|
|
816
821
|
i({
|
|
817
822
|
type: "error",
|
|
@@ -835,7 +840,7 @@ var U = class {
|
|
|
835
840
|
let t = e.liveSyncPosition;
|
|
836
841
|
if (typeof t != "number" || !Number.isFinite(t)) return;
|
|
837
842
|
let { video: n } = this.deps;
|
|
838
|
-
n.currentTime <= t +
|
|
843
|
+
n.currentTime <= t + Ee || (n.currentTime = t);
|
|
839
844
|
}
|
|
840
845
|
startHls() {
|
|
841
846
|
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 = {
|
|
@@ -862,14 +867,14 @@ var U = class {
|
|
|
862
867
|
} : {},
|
|
863
868
|
...c
|
|
864
869
|
};
|
|
865
|
-
|
|
870
|
+
De(d);
|
|
866
871
|
let f = new e(d);
|
|
867
872
|
this.hls = f, f.on(e.Events.MANIFEST_PARSED, () => {
|
|
868
873
|
this.mediaRecoveries = 0, this.networkRecoveries = 0, l({ type: "ready" });
|
|
869
874
|
}), f.on(e.Events.FRAG_BUFFERED, () => {
|
|
870
875
|
(this.mediaRecoveries > 0 || this.networkRecoveries > 0 || this.licenseRecoveries > 0) && !this.recoveryResetTimer && (this.recoveryResetTimer = setTimeout(() => {
|
|
871
876
|
this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.recoveryResetTimer = null;
|
|
872
|
-
},
|
|
877
|
+
}, be));
|
|
873
878
|
}), f.on(e.Events.LEVEL_LOADED, (e, t) => {
|
|
874
879
|
if (this.detectedLowLatency) return;
|
|
875
880
|
let n = t.details;
|
|
@@ -877,7 +882,7 @@ var U = class {
|
|
|
877
882
|
type: "lowLatencyDetected",
|
|
878
883
|
value: !0
|
|
879
884
|
}));
|
|
880
|
-
}), f.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.endWatchdog = setInterval(() => this.checkEndStale(),
|
|
885
|
+
}), f.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.endWatchdog = setInterval(() => this.checkEndStale(), Se), f.on(e.Events.LEVEL_SWITCHED, (e, t) => l({
|
|
881
886
|
type: "qualitychange",
|
|
882
887
|
index: t.level
|
|
883
888
|
})), f.on(e.Events.AUDIO_TRACK_SWITCHED, (e, t) => l({
|
|
@@ -946,7 +951,7 @@ var U = class {
|
|
|
946
951
|
}
|
|
947
952
|
getStats() {
|
|
948
953
|
let e = this.hls, t = {
|
|
949
|
-
droppedFrames:
|
|
954
|
+
droppedFrames: R(this.deps.video),
|
|
950
955
|
lowLatency: this.detectedLowLatency
|
|
951
956
|
};
|
|
952
957
|
if (e) {
|
|
@@ -975,7 +980,7 @@ var U = class {
|
|
|
975
980
|
return;
|
|
976
981
|
}
|
|
977
982
|
let e = Date.now() - this.lastAdvanceMs;
|
|
978
|
-
!this.chaseSuspended && e >
|
|
983
|
+
!this.chaseSuspended && e > Ce && this.suspendChasing(), e > xe && this.emitEnded("stalled");
|
|
979
984
|
}
|
|
980
985
|
suspendChasing() {
|
|
981
986
|
let e = this.hls;
|
|
@@ -1017,7 +1022,7 @@ var U = class {
|
|
|
1017
1022
|
scheduleLicenseRetry(t) {
|
|
1018
1023
|
if (!(t.details === e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED || t.details === e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED)) return !1;
|
|
1019
1024
|
let n = t.response?.code ?? 0;
|
|
1020
|
-
if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >=
|
|
1025
|
+
if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= ye) return !1;
|
|
1021
1026
|
this.licenseRecoveries += 1;
|
|
1022
1027
|
let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
|
|
1023
1028
|
return this.licenseRetryTimer = setTimeout(() => {
|
|
@@ -1045,7 +1050,7 @@ var U = class {
|
|
|
1045
1050
|
return;
|
|
1046
1051
|
}
|
|
1047
1052
|
if (t.type === e.ErrorTypes.NETWORK_ERROR) {
|
|
1048
|
-
if (this.networkRecoveries <
|
|
1053
|
+
if (this.networkRecoveries < ve) {
|
|
1049
1054
|
this.networkRecoveries += 1;
|
|
1050
1055
|
let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
|
|
1051
1056
|
this.networkRetryTimer = setTimeout(() => {
|
|
@@ -1061,7 +1066,7 @@ var U = class {
|
|
|
1061
1066
|
return;
|
|
1062
1067
|
}
|
|
1063
1068
|
if (t.type === e.ErrorTypes.MEDIA_ERROR) {
|
|
1064
|
-
if (this.mediaRecoveries <
|
|
1069
|
+
if (this.mediaRecoveries < _e) {
|
|
1065
1070
|
this.mediaRecoveries += 1, n.recoverMediaError();
|
|
1066
1071
|
return;
|
|
1067
1072
|
}
|
|
@@ -1116,15 +1121,15 @@ function K(e, t) {
|
|
|
1116
1121
|
}
|
|
1117
1122
|
//#endregion
|
|
1118
1123
|
//#region src/ui/styles.ts
|
|
1119
|
-
var q = "havik-player-styles",
|
|
1120
|
-
function
|
|
1124
|
+
var q = "havik-player-styles", ke = "\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\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";
|
|
1125
|
+
function Ae(e = document) {
|
|
1121
1126
|
if (e.getElementById(q)) return;
|
|
1122
1127
|
let t = e.createElement("style");
|
|
1123
|
-
t.id = q, t.textContent =
|
|
1128
|
+
t.id = q, t.textContent = ke, e.head.appendChild(t);
|
|
1124
1129
|
}
|
|
1125
1130
|
//#endregion
|
|
1126
1131
|
//#region src/ui/icons.ts
|
|
1127
|
-
var
|
|
1132
|
+
var je = {
|
|
1128
1133
|
play: "<path d=\"M8 5v14l11-7z\"/>",
|
|
1129
1134
|
pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
|
|
1130
1135
|
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\"/>",
|
|
@@ -1138,20 +1143,20 @@ var Ae = {
|
|
|
1138
1143
|
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\"/>"
|
|
1139
1144
|
};
|
|
1140
1145
|
function J(e) {
|
|
1141
|
-
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${
|
|
1146
|
+
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${je[e]}</svg>`;
|
|
1142
1147
|
}
|
|
1143
1148
|
//#endregion
|
|
1144
1149
|
//#region src/ui/controls.ts
|
|
1145
|
-
var
|
|
1150
|
+
var Me = 3e3, Ne = /* @__PURE__ */ new Set([
|
|
1146
1151
|
"GONE",
|
|
1147
1152
|
"NOT_FOUND",
|
|
1148
1153
|
"INVALID_URN",
|
|
1149
1154
|
"UNAUTHORIZED"
|
|
1150
1155
|
]);
|
|
1151
|
-
function
|
|
1152
|
-
return new
|
|
1156
|
+
function Pe(e) {
|
|
1157
|
+
return new Fe(e);
|
|
1153
1158
|
}
|
|
1154
|
-
var
|
|
1159
|
+
var Fe = class {
|
|
1155
1160
|
root;
|
|
1156
1161
|
video;
|
|
1157
1162
|
player;
|
|
@@ -1191,7 +1196,7 @@ var Pe = class {
|
|
|
1191
1196
|
scrubbing = !1;
|
|
1192
1197
|
offFns = [];
|
|
1193
1198
|
constructor(e) {
|
|
1194
|
-
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ??
|
|
1199
|
+
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Me, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.errorMessage = e.errorMessage, Ae(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");
|
|
1195
1200
|
let t = (e, t, n) => {
|
|
1196
1201
|
let r = this.doc.createElement(e);
|
|
1197
1202
|
if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
|
|
@@ -1244,7 +1249,7 @@ var Pe = class {
|
|
|
1244
1249
|
e.target === this.overlay && this.togglePlay();
|
|
1245
1250
|
}), this.on(this.muteBtn, "click", () => this.player.setMuted(!e.muted)), this.on(this.volSlider, "input", () => {
|
|
1246
1251
|
let t = Number(this.volSlider.value);
|
|
1247
|
-
this.player.setVolume(t), t > 0 && e.muted && this.player.setMuted(!1);
|
|
1252
|
+
this.setVolumeFill(t), this.player.setVolume(t), t > 0 && e.muted && this.player.setMuted(!1);
|
|
1248
1253
|
}), 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) => {
|
|
1249
1254
|
this.lastError = e, this.statusKind = null, this.syncState();
|
|
1250
1255
|
})), 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();
|
|
@@ -1278,7 +1283,7 @@ var Pe = class {
|
|
|
1278
1283
|
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);
|
|
1279
1284
|
} else {
|
|
1280
1285
|
let n = this.doc.createElement("div");
|
|
1281
|
-
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 && !
|
|
1286
|
+
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 && !Ne.has(this.lastError.code)) {
|
|
1282
1287
|
let e = this.doc.createElement("button");
|
|
1283
1288
|
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);
|
|
1284
1289
|
}
|
|
@@ -1287,7 +1292,12 @@ var Pe = class {
|
|
|
1287
1292
|
}
|
|
1288
1293
|
syncVolume() {
|
|
1289
1294
|
let e = this.video, t = e.muted || e.volume === 0;
|
|
1290
|
-
this.setIcon(this.muteBtn, t ? "volumeMute" : "volumeHigh"), this.muteBtn.setAttribute("aria-label", t ? "Unmute" : "Mute")
|
|
1295
|
+
this.setIcon(this.muteBtn, t ? "volumeMute" : "volumeHigh"), this.muteBtn.setAttribute("aria-label", t ? "Unmute" : "Mute");
|
|
1296
|
+
let n = t ? 0 : e.volume;
|
|
1297
|
+
this.volSlider.value = String(n), this.setVolumeFill(n);
|
|
1298
|
+
}
|
|
1299
|
+
setVolumeFill(e) {
|
|
1300
|
+
this.volSlider.style.setProperty("--havik-vol", String(Math.min(1, Math.max(0, e))));
|
|
1291
1301
|
}
|
|
1292
1302
|
seekableRange() {
|
|
1293
1303
|
let e = this.video, t, n;
|
|
@@ -1364,17 +1374,17 @@ var Pe = class {
|
|
|
1364
1374
|
renderQualityMenu() {
|
|
1365
1375
|
let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
|
|
1366
1376
|
n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
|
|
1367
|
-
for (let n of e) this.menu.append(this.menuItem(
|
|
1377
|
+
for (let n of e) this.menu.append(this.menuItem(Ie(n), t === n.index, () => this.player.setQuality(n.index)));
|
|
1368
1378
|
}
|
|
1369
1379
|
renderAudioMenu() {
|
|
1370
1380
|
let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
|
|
1371
1381
|
t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
|
|
1372
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
1382
|
+
for (let t of e) this.menu.append(this.menuItem(Le(t), t.default, () => this.player.setAudioTrack(t.id)));
|
|
1373
1383
|
}
|
|
1374
1384
|
renderCcMenu() {
|
|
1375
1385
|
let e = this.player.getTextTracks(), t = this.doc.createElement("div");
|
|
1376
1386
|
t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
|
|
1377
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
1387
|
+
for (let t of e) this.menu.append(this.menuItem(Re(t), !1, () => this.player.setTextTrack(t.id)));
|
|
1378
1388
|
}
|
|
1379
1389
|
onTracksChanged() {
|
|
1380
1390
|
this.openMenu && this.renderMenu(this.openMenu);
|
|
@@ -1480,27 +1490,27 @@ function Y(e) {
|
|
|
1480
1490
|
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");
|
|
1481
1491
|
return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
|
|
1482
1492
|
}
|
|
1483
|
-
function
|
|
1493
|
+
function Ie(e) {
|
|
1484
1494
|
return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
|
|
1485
1495
|
}
|
|
1486
|
-
function
|
|
1496
|
+
function Le(e) {
|
|
1487
1497
|
return e.name || e.lang || `Track ${e.id}`;
|
|
1488
1498
|
}
|
|
1489
|
-
function
|
|
1499
|
+
function Re(e) {
|
|
1490
1500
|
return e.name || e.lang || `Track ${e.id}`;
|
|
1491
1501
|
}
|
|
1492
1502
|
//#endregion
|
|
1493
1503
|
//#region src/managed/index.ts
|
|
1494
|
-
var
|
|
1495
|
-
async function
|
|
1504
|
+
var ze = 6e3, X = 45e3, Be = 2e3, Ve = 3, He = 2e3, Ue = 480 * 1e3;
|
|
1505
|
+
async function Z(e) {
|
|
1496
1506
|
if (!e.video || typeof e.video.play != "function") throw new t("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
|
|
1497
1507
|
if (!e.baseUrl) throw new t("INTERNAL", 0, "createPlayer: baseUrl is required");
|
|
1498
1508
|
if (!e.matchUrn) throw new t("INVALID_URN", 0, "createPlayer: matchUrn is required");
|
|
1499
1509
|
if (!e.credential) throw new t("INTERNAL", 0, "createPlayer: credential is required");
|
|
1500
|
-
let n = new
|
|
1510
|
+
let n = new We(e);
|
|
1501
1511
|
return await n.start(), n;
|
|
1502
1512
|
}
|
|
1503
|
-
var
|
|
1513
|
+
var We = class {
|
|
1504
1514
|
opts;
|
|
1505
1515
|
abort = new AbortController();
|
|
1506
1516
|
listeners = {};
|
|
@@ -1556,7 +1566,7 @@ var He = class {
|
|
|
1556
1566
|
};
|
|
1557
1567
|
}
|
|
1558
1568
|
beginArming(e, t) {
|
|
1559
|
-
let n = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ??
|
|
1569
|
+
let n = this.opts.liveStateEvents === !1 ? void 0 : this.opts.eventsBaseUrl ?? T(this.opts.baseUrl);
|
|
1560
1570
|
n ? this.armWithSse(e, n, t) : this.beginArmingByPolling(e);
|
|
1561
1571
|
}
|
|
1562
1572
|
beginArmingByPolling(e) {
|
|
@@ -1610,13 +1620,13 @@ var He = class {
|
|
|
1610
1620
|
!a && !f() && m();
|
|
1611
1621
|
}, e);
|
|
1612
1622
|
};
|
|
1613
|
-
g(
|
|
1623
|
+
g(ze), this.armingSub = w({
|
|
1614
1624
|
eventsBaseUrl: n,
|
|
1615
1625
|
matchUrn: this.opts.matchUrn,
|
|
1616
1626
|
credential: this.opts.credential,
|
|
1617
1627
|
onAlive: () => g(X),
|
|
1618
1628
|
onState: (e) => {
|
|
1619
|
-
a || f() || (e.state === "live" || e.state === "gone") && setTimeout(h, Math.random() *
|
|
1629
|
+
a || f() || (e.state === "live" || e.state === "gone") && setTimeout(h, Math.random() * Be);
|
|
1620
1630
|
},
|
|
1621
1631
|
onError: () => m()
|
|
1622
1632
|
});
|
|
@@ -1639,7 +1649,7 @@ var He = class {
|
|
|
1639
1649
|
let n = await a(this.opts.credential);
|
|
1640
1650
|
if (this.destroyed || t !== this.loadGen) return;
|
|
1641
1651
|
this.setState("loading");
|
|
1642
|
-
let r = this.createEngine(e, n), i =
|
|
1652
|
+
let r = this.createEngine(e, n), i = z(this.opts.video, (e) => {
|
|
1643
1653
|
this._state === "ended" || this._state === "error" || this.setState(e);
|
|
1644
1654
|
});
|
|
1645
1655
|
if (await r.load(), this.destroyed || t !== this.loadGen) {
|
|
@@ -1659,7 +1669,7 @@ var He = class {
|
|
|
1659
1669
|
video: this.opts.video,
|
|
1660
1670
|
descriptor: e,
|
|
1661
1671
|
credential: n,
|
|
1662
|
-
deviceId:
|
|
1672
|
+
deviceId: O(),
|
|
1663
1673
|
userId: this.opts.userId,
|
|
1664
1674
|
lowLatency: this.opts.lowLatency === void 0 || this.opts.lowLatency === "auto" || this.opts.lowLatency,
|
|
1665
1675
|
snapToLiveOnRefocus: this.opts.snapToLiveOnRefocus,
|
|
@@ -1669,9 +1679,9 @@ var He = class {
|
|
|
1669
1679
|
}, a = this.opts.engine ?? "auto";
|
|
1670
1680
|
if (a === "native") return new H(i);
|
|
1671
1681
|
if (a === "hls") return new U(i);
|
|
1672
|
-
if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl &&
|
|
1673
|
-
if (
|
|
1674
|
-
if (
|
|
1682
|
+
if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && B(this.opts.video) && V()) return new H(i);
|
|
1683
|
+
if (ge()) return new U(i);
|
|
1684
|
+
if (B(this.opts.video)) return new H(i);
|
|
1675
1685
|
throw new t("INTERNAL", 0, "no supported HLS playback engine in this browser");
|
|
1676
1686
|
}
|
|
1677
1687
|
onEngineEvent(e) {
|
|
@@ -1701,11 +1711,11 @@ var He = class {
|
|
|
1701
1711
|
}
|
|
1702
1712
|
startLiveState(e) {
|
|
1703
1713
|
if (this.opts.liveStateEvents === !1) return;
|
|
1704
|
-
let n = this.opts.eventsBaseUrl ??
|
|
1714
|
+
let n = this.opts.eventsBaseUrl ?? T(this.opts.baseUrl);
|
|
1705
1715
|
if (!n) return;
|
|
1706
1716
|
this.closeLiveState();
|
|
1707
1717
|
let r = 0, i = !1;
|
|
1708
|
-
this.liveStateSub =
|
|
1718
|
+
this.liveStateSub = w({
|
|
1709
1719
|
eventsBaseUrl: n,
|
|
1710
1720
|
matchUrn: this.opts.matchUrn,
|
|
1711
1721
|
credential: this.opts.credential,
|
|
@@ -1716,7 +1726,7 @@ var He = class {
|
|
|
1716
1726
|
r = 0, i = !1;
|
|
1717
1727
|
},
|
|
1718
1728
|
onError: () => {
|
|
1719
|
-
this.destroyed || e !== this.loadGen || i || (r += 1, r >=
|
|
1729
|
+
this.destroyed || e !== this.loadGen || i || (r += 1, r >= Ve && (i = !0, this.emit("warning", new t("NETWORK", 0, `live-state events unreachable at ${n} after ${r} attempts — end-of-stream detection degraded to the HLS staleness watchdog. Check the events host, the api-key's allowed origins, and CSP connect-src.`))));
|
|
1720
1730
|
}
|
|
1721
1731
|
});
|
|
1722
1732
|
}
|
|
@@ -1738,7 +1748,7 @@ var He = class {
|
|
|
1738
1748
|
}
|
|
1739
1749
|
t.controls = !1;
|
|
1740
1750
|
let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
|
|
1741
|
-
r && r.insertBefore(n, t), n.appendChild(t), K(n, this.opts.theme), this.container = n, this.controlBar =
|
|
1751
|
+
r && r.insertBefore(n, t), n.appendChild(t), K(n, this.opts.theme), this.container = n, this.controlBar = Pe({
|
|
1742
1752
|
root: n,
|
|
1743
1753
|
video: t,
|
|
1744
1754
|
player: this,
|
|
@@ -1761,7 +1771,7 @@ var He = class {
|
|
|
1761
1771
|
}
|
|
1762
1772
|
startStats() {
|
|
1763
1773
|
this.stopStats();
|
|
1764
|
-
let e = Math.max(500, this.opts.statsIntervalMs ??
|
|
1774
|
+
let e = Math.max(500, this.opts.statsIntervalMs ?? He);
|
|
1765
1775
|
this.statsTimer = setInterval(() => {
|
|
1766
1776
|
this.engine && this.emit("stats", this.engine.getStats());
|
|
1767
1777
|
}, e);
|
|
@@ -1771,7 +1781,7 @@ var He = class {
|
|
|
1771
1781
|
}
|
|
1772
1782
|
scheduleLicenseRefresh() {
|
|
1773
1783
|
this.stopRefresh();
|
|
1774
|
-
let e = this.opts.licenseRefreshMs ??
|
|
1784
|
+
let e = this.opts.licenseRefreshMs ?? Ue;
|
|
1775
1785
|
!e || !this._descriptor?.drmEnabled || (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
|
|
1776
1786
|
}
|
|
1777
1787
|
stopRefresh() {
|
|
@@ -1808,7 +1818,7 @@ var He = class {
|
|
|
1808
1818
|
this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
|
|
1809
1819
|
}
|
|
1810
1820
|
getStats() {
|
|
1811
|
-
return this.engine?.getStats() ??
|
|
1821
|
+
return this.engine?.getStats() ?? L;
|
|
1812
1822
|
}
|
|
1813
1823
|
getQualityLevels() {
|
|
1814
1824
|
return this.engine?.getQualityLevels() ?? [];
|
|
@@ -1904,12 +1914,12 @@ var He = class {
|
|
|
1904
1914
|
};
|
|
1905
1915
|
//#endregion
|
|
1906
1916
|
//#region src/embed/protocol.ts
|
|
1907
|
-
function
|
|
1917
|
+
function Q(e) {
|
|
1908
1918
|
return !!e && typeof e == "object" && typeof e.type == "string" && e.type.startsWith("oddin:");
|
|
1909
1919
|
}
|
|
1910
1920
|
//#endregion
|
|
1911
1921
|
//#region src/embed/host.ts
|
|
1912
|
-
function
|
|
1922
|
+
function $(e) {
|
|
1913
1923
|
if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
|
|
1914
1924
|
if (!e.src) throw Error("mountEmbed: opts.src is required");
|
|
1915
1925
|
if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
|
|
@@ -1918,7 +1928,7 @@ function Ue(e) {
|
|
|
1918
1928
|
let r = document.createElement("iframe");
|
|
1919
1929
|
r.src = t.toString(), r.allow = "autoplay; encrypted-media; fullscreen", r.setAttribute("allowfullscreen", ""), r.referrerPolicy = "no-referrer", r.setAttribute("sandbox", e.sandbox ?? "allow-scripts allow-same-origin allow-presentation"), r.style.cssText = "border:0;width:100%;height:100%;display:block", e.container.appendChild(r);
|
|
1920
1930
|
let i = /* @__PURE__ */ new Set(), a = (t) => {
|
|
1921
|
-
if (t.source !== r.contentWindow || t.origin !== n ||
|
|
1931
|
+
if (t.source !== r.contentWindow || t.origin !== n || !Q(t.data)) return;
|
|
1922
1932
|
let a = t.data;
|
|
1923
1933
|
a.type === "oddin:ready" && a.protocol !== 1 && console.warn(`havik-player: embed protocol v${a.protocol} != host v1. Pin the embed page version to the SDK version to avoid skew.`), e.onEvent?.(a), i.forEach((e) => e(a));
|
|
1924
1934
|
};
|
|
@@ -1968,8 +1978,8 @@ function Ue(e) {
|
|
|
1968
1978
|
}
|
|
1969
1979
|
//#endregion
|
|
1970
1980
|
//#region src/embed/page.ts
|
|
1971
|
-
var
|
|
1972
|
-
function
|
|
1981
|
+
var Ge = /^[A-Za-z0-9:_.-]{1,128}$/;
|
|
1982
|
+
function Ke() {
|
|
1973
1983
|
let e = window.HAVIK_EMBED_CONFIG;
|
|
1974
1984
|
if (e) return {
|
|
1975
1985
|
baseUrl: e.baseUrl ?? "",
|
|
@@ -1991,9 +2001,9 @@ function Ge() {
|
|
|
1991
2001
|
waitForLive: !0
|
|
1992
2002
|
};
|
|
1993
2003
|
}
|
|
1994
|
-
async function
|
|
1995
|
-
let e =
|
|
1996
|
-
|
|
2004
|
+
async function qe() {
|
|
2005
|
+
let e = Ke();
|
|
2006
|
+
Ye();
|
|
1997
2007
|
let n = document.createElement("video");
|
|
1998
2008
|
n.setAttribute("playsinline", ""), n.muted = e.muted ?? !0, n.style.cssText = "width:100%;height:100%;object-fit:contain;background:#000;aspect-ratio:auto", document.body.appendChild(n);
|
|
1999
2009
|
let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
|
|
@@ -2048,12 +2058,12 @@ async function Ke() {
|
|
|
2048
2058
|
code: e.code,
|
|
2049
2059
|
message: e.message
|
|
2050
2060
|
})), e.on("autoplayblocked", () => {
|
|
2051
|
-
i({ type: "oddin:autoplayblocked" }),
|
|
2061
|
+
i({ type: "oddin:autoplayblocked" }), Je(n, e);
|
|
2052
2062
|
});
|
|
2053
2063
|
}, s = async (r) => {
|
|
2054
2064
|
a?.destroy(), a = null;
|
|
2055
2065
|
try {
|
|
2056
|
-
let t = await
|
|
2066
|
+
let t = await Z({
|
|
2057
2067
|
video: n,
|
|
2058
2068
|
baseUrl: e.baseUrl,
|
|
2059
2069
|
matchUrn: r,
|
|
@@ -2114,32 +2124,32 @@ async function Ke() {
|
|
|
2114
2124
|
a?.retry();
|
|
2115
2125
|
break;
|
|
2116
2126
|
case "oddin:load":
|
|
2117
|
-
typeof e.matchUrn == "string" &&
|
|
2127
|
+
typeof e.matchUrn == "string" && Ge.test(e.matchUrn) && s(e.matchUrn);
|
|
2118
2128
|
break;
|
|
2119
2129
|
case "oddin:destroy":
|
|
2120
2130
|
c();
|
|
2121
2131
|
break;
|
|
2122
2132
|
}
|
|
2123
2133
|
}, u = (e) => {
|
|
2124
|
-
!r || e.origin !== r ||
|
|
2134
|
+
!r || e.origin !== r || Q(e.data) && l(e.data);
|
|
2125
2135
|
};
|
|
2126
2136
|
r && window.addEventListener("message", u), window.addEventListener("pagehide", c, { once: !0 }), await s(e.matchUrn), i({
|
|
2127
2137
|
type: "oddin:ready",
|
|
2128
2138
|
protocol: 1
|
|
2129
2139
|
});
|
|
2130
2140
|
}
|
|
2131
|
-
function
|
|
2141
|
+
function Je(e, t) {
|
|
2132
2142
|
if (document.getElementById("havik-tap")) return;
|
|
2133
2143
|
let n = document.createElement("button");
|
|
2134
2144
|
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", () => {
|
|
2135
2145
|
n.remove(), t.play();
|
|
2136
2146
|
}), document.body.appendChild(n);
|
|
2137
2147
|
}
|
|
2138
|
-
function
|
|
2148
|
+
function Ye() {
|
|
2139
2149
|
let e = document.createElement("style");
|
|
2140
2150
|
e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
|
|
2141
2151
|
}
|
|
2142
2152
|
//#endregion
|
|
2143
|
-
export { W as ODDIN_THEME, t as PlaybackError, K as applyTheme,
|
|
2153
|
+
export { W as ODDIN_THEME, t as PlaybackError, K as applyTheme, qe as bootEmbed, de as clearDeviceId, Z as createPlayer, T as deriveEventsBaseUrl, P as detectDrmSupport, C as fetchCatalog, se as fetchTournament, le as flattenMatches, O as getDeviceId, I as isLowLatencyManifest, Q as isOddinMessage, pe as licenseRequestHeaders, $ as mountEmbed, v as pollToLive, b as resolveStream, w as subscribeLiveState, ue as watchStatus };
|
|
2144
2154
|
|
|
2145
2155
|
//# sourceMappingURL=index.js.map
|