@oddin-gg/havik-player 2.19.0 → 2.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/havik-player.global.js +3 -3
- package/dist/havik-player.global.js.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -2
- package/dist/index.d.ts +14 -2
- package/dist/index.js +157 -144
- package/dist/index.js.map +1 -1
- package/docs/API.md +47 -39
- package/embed/index.html +1 -1
- package/examples/vanilla.html +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,8 +7,9 @@ var n = class extends Error {
|
|
|
7
7
|
requestId;
|
|
8
8
|
serverCode;
|
|
9
9
|
liveStartsAtMs;
|
|
10
|
+
country;
|
|
10
11
|
constructor(e, t, n, r) {
|
|
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
|
+
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, this.country = r?.country, r?.cause !== void 0 && (this.cause = r.cause);
|
|
12
13
|
}
|
|
13
14
|
};
|
|
14
15
|
function r(e) {
|
|
@@ -20,6 +21,7 @@ function r(e) {
|
|
|
20
21
|
case 410: return "GONE";
|
|
21
22
|
case 425: return "TOO_EARLY";
|
|
22
23
|
case 429: return "RATE_LIMITED";
|
|
24
|
+
case 451: return "GEO_DENIED";
|
|
23
25
|
case 503: return "UNAVAILABLE";
|
|
24
26
|
default: return "INTERNAL";
|
|
25
27
|
}
|
|
@@ -215,19 +217,21 @@ function y(e) {
|
|
|
215
217
|
return e.waitForLive ? de(t, e.waitForLive, e.signal) : t();
|
|
216
218
|
}
|
|
217
219
|
async function fe(e) {
|
|
218
|
-
let t = f(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;
|
|
220
|
+
let t = f(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, c;
|
|
219
221
|
try {
|
|
220
222
|
let t = await e.json();
|
|
221
223
|
if (t?.error?.message && (a = t.error.message), t?.error?.code && (o = t.error.code), typeof t?.liveStartsAt == "string") {
|
|
222
224
|
let e = Date.parse(t.liveStartsAt);
|
|
223
225
|
Number.isFinite(e) && (s = e);
|
|
224
226
|
}
|
|
227
|
+
e.status === 451 && typeof t?.country == "string" && /^[A-Z]{2}$/.test(t.country) && (c = t.country);
|
|
225
228
|
} catch {}
|
|
226
229
|
return new n(r(e.status), e.status, a, {
|
|
227
230
|
retryAfterMs: t,
|
|
228
231
|
requestId: i,
|
|
229
232
|
serverCode: o,
|
|
230
|
-
liveStartsAtMs: s
|
|
233
|
+
liveStartsAtMs: s,
|
|
234
|
+
country: c
|
|
231
235
|
});
|
|
232
236
|
}
|
|
233
237
|
var pe = /* @__PURE__ */ new Set([
|
|
@@ -420,6 +424,7 @@ function xe(e) {
|
|
|
420
424
|
//#region src/core/poll.ts
|
|
421
425
|
var Se = /* @__PURE__ */ new Set([
|
|
422
426
|
"NOT_FOUND",
|
|
427
|
+
"GEO_DENIED",
|
|
423
428
|
"GONE",
|
|
424
429
|
"INVALID_URN"
|
|
425
430
|
]);
|
|
@@ -606,18 +611,18 @@ function De(e, t, n = {}) {
|
|
|
606
611
|
//#endregion
|
|
607
612
|
//#region src/core/bandwidthMemory.ts
|
|
608
613
|
var j = "havik.player.bandwidth", Oe = .8, ke = 2e7, Ae = 3e5;
|
|
609
|
-
function
|
|
614
|
+
function je() {
|
|
610
615
|
return globalThis.navigator?.connection;
|
|
611
616
|
}
|
|
612
|
-
function
|
|
613
|
-
let e =
|
|
617
|
+
function Me() {
|
|
618
|
+
let e = je()?.type;
|
|
614
619
|
return typeof e == "string" && e !== "" && e !== "unknown" ? e : "any";
|
|
615
620
|
}
|
|
616
|
-
function
|
|
617
|
-
let e =
|
|
621
|
+
function Ne() {
|
|
622
|
+
let e = je()?.downlink;
|
|
618
623
|
return typeof e == "number" && Number.isFinite(e) && e > 0 ? e * 1e6 : void 0;
|
|
619
624
|
}
|
|
620
|
-
function
|
|
625
|
+
function Pe() {
|
|
621
626
|
try {
|
|
622
627
|
let e = globalThis.localStorage?.getItem(j);
|
|
623
628
|
if (!e) return {};
|
|
@@ -627,18 +632,18 @@ function Ne() {
|
|
|
627
632
|
return {};
|
|
628
633
|
}
|
|
629
634
|
}
|
|
630
|
-
function
|
|
631
|
-
let t =
|
|
635
|
+
function Fe(e = Date.now()) {
|
|
636
|
+
let t = Pe()[Me()];
|
|
632
637
|
if (!t || typeof t.bps != "number" || typeof t.at != "number" || !Number.isFinite(t.bps) || t.bps <= 0) return;
|
|
633
638
|
let n = e - t.at;
|
|
634
639
|
if (n < 0 || n > 2592e5) return;
|
|
635
|
-
let r =
|
|
640
|
+
let r = Ne(), i = r === void 0 ? t.bps : Math.min(t.bps, r);
|
|
636
641
|
return Math.round(Math.min(ke, Math.max(Ae, i * Oe)));
|
|
637
642
|
}
|
|
638
|
-
function
|
|
643
|
+
function Ie(e, t = Date.now()) {
|
|
639
644
|
if (!Number.isFinite(e) || e <= 0) return;
|
|
640
|
-
let n =
|
|
641
|
-
n[
|
|
645
|
+
let n = Pe();
|
|
646
|
+
n[Me()] = {
|
|
642
647
|
bps: Math.round(e),
|
|
643
648
|
at: t
|
|
644
649
|
};
|
|
@@ -646,17 +651,17 @@ function Fe(e, t = Date.now()) {
|
|
|
646
651
|
globalThis.localStorage?.setItem(j, JSON.stringify(n));
|
|
647
652
|
} catch {}
|
|
648
653
|
}
|
|
649
|
-
function
|
|
654
|
+
function Le() {
|
|
650
655
|
try {
|
|
651
656
|
globalThis.localStorage?.removeItem(j);
|
|
652
657
|
} catch {}
|
|
653
658
|
}
|
|
654
659
|
//#endregion
|
|
655
660
|
//#region src/core/drm/cdm.ts
|
|
656
|
-
var
|
|
657
|
-
function
|
|
658
|
-
let n =
|
|
659
|
-
n || (n = /* @__PURE__ */ new Map(),
|
|
661
|
+
var Re = /* @__PURE__ */ new WeakMap();
|
|
662
|
+
function ze(e, t) {
|
|
663
|
+
let n = Re.get(e);
|
|
664
|
+
n || (n = /* @__PURE__ */ new Map(), Re.set(e, n));
|
|
660
665
|
let r = n.get(t.keySystem);
|
|
661
666
|
if (r) return r;
|
|
662
667
|
let i = t.createMediaKeys().catch((e) => {
|
|
@@ -664,22 +669,22 @@ function Re(e, t) {
|
|
|
664
669
|
});
|
|
665
670
|
return n.set(t.keySystem, i), i;
|
|
666
671
|
}
|
|
667
|
-
function
|
|
672
|
+
function M() {
|
|
668
673
|
return typeof navigator < "u" && typeof navigator.requestMediaKeySystemAccess == "function";
|
|
669
674
|
}
|
|
670
|
-
var
|
|
671
|
-
function
|
|
675
|
+
var N = ["SW_SECURE_DECODE", "SW_SECURE_CRYPTO"];
|
|
676
|
+
function Be(e) {
|
|
672
677
|
return e.startsWith("com.widevine");
|
|
673
678
|
}
|
|
674
|
-
function
|
|
675
|
-
if (!
|
|
679
|
+
function P(e, t) {
|
|
680
|
+
if (!Be(e)) return t;
|
|
676
681
|
let n = (e, t) => {
|
|
677
682
|
let n = e.videoCapabilities;
|
|
678
683
|
return !n?.length || !n.some((e) => e.robustness) ? !1 : n.every((e) => {
|
|
679
|
-
let n =
|
|
680
|
-
return n === -1 ||
|
|
684
|
+
let n = N.indexOf(e.robustness);
|
|
685
|
+
return n === -1 || N.indexOf(t) > n;
|
|
681
686
|
});
|
|
682
|
-
}, r =
|
|
687
|
+
}, r = N.flatMap((e) => t.filter((t) => n(t, e)).map((t) => ({
|
|
683
688
|
...t,
|
|
684
689
|
videoCapabilities: t.videoCapabilities?.map((t) => ({
|
|
685
690
|
...t,
|
|
@@ -688,53 +693,53 @@ function F(e, t) {
|
|
|
688
693
|
})));
|
|
689
694
|
return [...t, ...r];
|
|
690
695
|
}
|
|
691
|
-
function
|
|
692
|
-
return (t, n) =>
|
|
696
|
+
function Ve(e) {
|
|
697
|
+
return (t, n) => M() ? navigator.requestMediaKeySystemAccess(t, P(t, n)).then((t) => ({
|
|
693
698
|
keySystem: t.keySystem,
|
|
694
699
|
getConfiguration: () => t.getConfiguration(),
|
|
695
|
-
createMediaKeys: () =>
|
|
700
|
+
createMediaKeys: () => ze(e, t)
|
|
696
701
|
})) : Promise.reject(/* @__PURE__ */ Error("navigator.requestMediaKeySystemAccess is unavailable — EME needs a secure context (https, or localhost)"));
|
|
697
702
|
}
|
|
698
|
-
var
|
|
703
|
+
var F = {
|
|
699
704
|
video: "SW_SECURE_DECODE",
|
|
700
705
|
audio: "SW_SECURE_CRYPTO",
|
|
701
706
|
scheme: "cbcs"
|
|
702
707
|
};
|
|
703
|
-
function
|
|
704
|
-
if (!
|
|
708
|
+
function He(e) {
|
|
709
|
+
if (!M()) return;
|
|
705
710
|
let t = (e, t) => ({
|
|
706
711
|
contentType: e,
|
|
707
712
|
robustness: t,
|
|
708
|
-
encryptionScheme:
|
|
713
|
+
encryptionScheme: F.scheme
|
|
709
714
|
});
|
|
710
|
-
|
|
715
|
+
Ve(e)("com.widevine.alpha", [{
|
|
711
716
|
initDataTypes: ["cenc"],
|
|
712
717
|
distinctiveIdentifier: "optional",
|
|
713
718
|
persistentState: "optional",
|
|
714
719
|
sessionTypes: ["temporary"],
|
|
715
|
-
audioCapabilities: [t("audio/mp4; codecs=\"mp4a.40.2\"",
|
|
716
|
-
videoCapabilities: [t("video/mp4; codecs=\"avc1.42e01e\"",
|
|
720
|
+
audioCapabilities: [t("audio/mp4; codecs=\"mp4a.40.2\"", F.audio)],
|
|
721
|
+
videoCapabilities: [t("video/mp4; codecs=\"avc1.42e01e\"", F.video)]
|
|
717
722
|
}]).then((e) => e.createMediaKeys()).catch(() => {});
|
|
718
723
|
}
|
|
719
|
-
function
|
|
720
|
-
let t =
|
|
724
|
+
function I(e) {
|
|
725
|
+
let t = L(e);
|
|
721
726
|
if (/^avc[13]/.test(t)) return "avc";
|
|
722
727
|
if (/^(hvc1|hev1)/.test(t)) return "hevc";
|
|
723
728
|
if (/^(dvh1|dvhe)/.test(t)) return "dv";
|
|
724
729
|
if (t.startsWith("av01")) return "av1";
|
|
725
730
|
if (/^vp0?9/.test(t)) return "vp9";
|
|
726
731
|
}
|
|
727
|
-
function
|
|
732
|
+
function L(e) {
|
|
728
733
|
return (e ?? "").trim().toLowerCase();
|
|
729
734
|
}
|
|
730
|
-
var
|
|
735
|
+
var Ue = {
|
|
731
736
|
hevc: "hvc1.1.6.L93.90",
|
|
732
737
|
dv: "dvh1.05.06",
|
|
733
738
|
av1: "av01.0.05M.08",
|
|
734
739
|
vp9: "vp09.00.10.08"
|
|
735
|
-
},
|
|
736
|
-
function
|
|
737
|
-
let t =
|
|
740
|
+
}, We = /* @__PURE__ */ new Map(), R = /* @__PURE__ */ new Map();
|
|
741
|
+
function Ge(e) {
|
|
742
|
+
let t = L(e), n = We.get(t);
|
|
738
743
|
if (n) return n;
|
|
739
744
|
let r = {
|
|
740
745
|
initDataTypes: ["cenc"],
|
|
@@ -743,32 +748,32 @@ function We(e) {
|
|
|
743
748
|
sessionTypes: ["temporary"],
|
|
744
749
|
videoCapabilities: [{
|
|
745
750
|
contentType: `video/mp4; codecs="${e.trim()}"`,
|
|
746
|
-
robustness:
|
|
747
|
-
encryptionScheme:
|
|
751
|
+
robustness: F.video,
|
|
752
|
+
encryptionScheme: F.scheme
|
|
748
753
|
}]
|
|
749
754
|
}, i = (async () => {
|
|
750
755
|
try {
|
|
751
|
-
return await navigator.requestMediaKeySystemAccess("com.widevine.alpha",
|
|
756
|
+
return await navigator.requestMediaKeySystemAccess("com.widevine.alpha", P("com.widevine.alpha", [r])), !0;
|
|
752
757
|
} catch {
|
|
753
758
|
return !1;
|
|
754
759
|
}
|
|
755
|
-
})().then((e) => (
|
|
756
|
-
return
|
|
760
|
+
})().then((e) => (R.set(t, e), e));
|
|
761
|
+
return We.set(t, i), i;
|
|
757
762
|
}
|
|
758
|
-
function
|
|
759
|
-
if (
|
|
763
|
+
function Ke() {
|
|
764
|
+
if (M()) for (let e of Object.values(Ue)) Ge(e);
|
|
760
765
|
}
|
|
761
|
-
function
|
|
762
|
-
let t =
|
|
763
|
-
if (t) return t === "avc" ? !0 :
|
|
766
|
+
function qe(e) {
|
|
767
|
+
let t = I(e);
|
|
768
|
+
if (t) return t === "avc" ? !0 : R.get(L(e)) ?? R.get(L(Ue[t]));
|
|
764
769
|
}
|
|
765
770
|
//#endregion
|
|
766
771
|
//#region src/core/drm/support.ts
|
|
767
|
-
var
|
|
772
|
+
var Je = 5e3, Ye = "video/mp4; codecs=\"avc1.42E01E\"", Xe = "audio/mp4; codecs=\"mp4a.40.2\"", Ze = {
|
|
768
773
|
widevine: ["com.widevine.alpha"],
|
|
769
774
|
fairplay: ["com.apple.fps"]
|
|
770
775
|
};
|
|
771
|
-
function
|
|
776
|
+
function Qe(e) {
|
|
772
777
|
return e === "fairplay" ? [{
|
|
773
778
|
initDataTypes: [
|
|
774
779
|
"sinf",
|
|
@@ -776,30 +781,30 @@ function Ze(e) {
|
|
|
776
781
|
"cenc"
|
|
777
782
|
],
|
|
778
783
|
videoCapabilities: [{
|
|
779
|
-
contentType:
|
|
784
|
+
contentType: Ye,
|
|
780
785
|
robustness: "",
|
|
781
786
|
encryptionScheme: "cbcs"
|
|
782
787
|
}],
|
|
783
788
|
audioCapabilities: [{
|
|
784
|
-
contentType:
|
|
789
|
+
contentType: Xe,
|
|
785
790
|
robustness: "",
|
|
786
791
|
encryptionScheme: "cbcs"
|
|
787
792
|
}]
|
|
788
|
-
}] :
|
|
793
|
+
}] : P("com.widevine.alpha", [{
|
|
789
794
|
initDataTypes: ["cenc"],
|
|
790
795
|
videoCapabilities: [{
|
|
791
|
-
contentType:
|
|
792
|
-
robustness:
|
|
793
|
-
encryptionScheme:
|
|
796
|
+
contentType: Ye,
|
|
797
|
+
robustness: F.video,
|
|
798
|
+
encryptionScheme: F.scheme
|
|
794
799
|
}],
|
|
795
800
|
audioCapabilities: [{
|
|
796
|
-
contentType:
|
|
797
|
-
robustness:
|
|
798
|
-
encryptionScheme:
|
|
801
|
+
contentType: Xe,
|
|
802
|
+
robustness: F.audio,
|
|
803
|
+
encryptionScheme: F.scheme
|
|
799
804
|
}]
|
|
800
805
|
}]);
|
|
801
806
|
}
|
|
802
|
-
function
|
|
807
|
+
function $e(e, t) {
|
|
803
808
|
return new Promise((n, r) => {
|
|
804
809
|
let i = setTimeout(() => r(/* @__PURE__ */ Error("probe-timeout")), t);
|
|
805
810
|
e.then((e) => {
|
|
@@ -809,8 +814,8 @@ function Qe(e, t) {
|
|
|
809
814
|
});
|
|
810
815
|
});
|
|
811
816
|
}
|
|
812
|
-
async function
|
|
813
|
-
let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ??
|
|
817
|
+
async function et(e = {}) {
|
|
818
|
+
let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? Je, r = {};
|
|
814
819
|
if (typeof window < "u" && window.isSecureContext === !1) return {
|
|
815
820
|
verdict: "insecure-context",
|
|
816
821
|
detail: r
|
|
@@ -822,7 +827,7 @@ async function $e(e = {}) {
|
|
|
822
827
|
};
|
|
823
828
|
let a = !1, o = !1, s = Date.now() + n, c = () => Math.max(1, s - Date.now());
|
|
824
829
|
for (let e of t) {
|
|
825
|
-
let t =
|
|
830
|
+
let t = Ze[e];
|
|
826
831
|
if (!t) {
|
|
827
832
|
r[e] = "unknown system";
|
|
828
833
|
continue;
|
|
@@ -833,7 +838,7 @@ async function $e(e = {}) {
|
|
|
833
838
|
break;
|
|
834
839
|
}
|
|
835
840
|
try {
|
|
836
|
-
return await
|
|
841
|
+
return await $e((await $e(i(n, Qe(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
|
|
837
842
|
verdict: "supported",
|
|
838
843
|
system: e,
|
|
839
844
|
detail: r
|
|
@@ -857,24 +862,24 @@ async function $e(e = {}) {
|
|
|
857
862
|
}
|
|
858
863
|
//#endregion
|
|
859
864
|
//#region src/core/manifest.ts
|
|
860
|
-
var
|
|
861
|
-
function
|
|
862
|
-
return
|
|
865
|
+
var tt = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
|
|
866
|
+
function nt(e) {
|
|
867
|
+
return tt.test(e);
|
|
863
868
|
}
|
|
864
869
|
//#endregion
|
|
865
870
|
//#region src/core/env.ts
|
|
866
|
-
var
|
|
871
|
+
var rt = {
|
|
867
872
|
integration: "https://feed-dev.oddin-video.gg",
|
|
868
873
|
production: "https://feed.oddin-video.gg"
|
|
869
874
|
};
|
|
870
|
-
function
|
|
871
|
-
let t = Object.prototype.hasOwnProperty.call(
|
|
875
|
+
function it(e) {
|
|
876
|
+
let t = Object.prototype.hasOwnProperty.call(rt, e) ? rt[e] : void 0;
|
|
872
877
|
if (!t) throw new n("INTERNAL", 0, `unknown env ${JSON.stringify(e)} — expected 'integration' or 'production'`);
|
|
873
878
|
return t;
|
|
874
879
|
}
|
|
875
880
|
//#endregion
|
|
876
881
|
//#region src/core/stats.ts
|
|
877
|
-
var
|
|
882
|
+
var z = { droppedFrames: 0 };
|
|
878
883
|
function at(e) {
|
|
879
884
|
try {
|
|
880
885
|
return new URL(e).host;
|
|
@@ -1551,7 +1556,7 @@ var _n = class {
|
|
|
1551
1556
|
});
|
|
1552
1557
|
return;
|
|
1553
1558
|
}
|
|
1554
|
-
e.drmEnabled && o && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && (
|
|
1559
|
+
e.drmEnabled && o && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && (He(r), Ke()), this.startHls(), t !== !1 && typeof document < "u" && (this.onVisibility = () => {
|
|
1555
1560
|
let e = this.hls;
|
|
1556
1561
|
if (document.visibilityState !== "visible" || !e) return;
|
|
1557
1562
|
let t = e.liveSyncPosition;
|
|
@@ -1641,12 +1646,12 @@ var _n = class {
|
|
|
1641
1646
|
emeEnabled: !0,
|
|
1642
1647
|
drmSystems: { ...this.licenseUrl ? { "com.widevine.alpha": { licenseUrl: this.licenseUrl } } : {} },
|
|
1643
1648
|
drmSystemOptions: {
|
|
1644
|
-
videoRobustness:
|
|
1645
|
-
audioRobustness:
|
|
1646
|
-
videoEncryptionScheme:
|
|
1647
|
-
audioEncryptionScheme:
|
|
1649
|
+
videoRobustness: F.video,
|
|
1650
|
+
audioRobustness: F.audio,
|
|
1651
|
+
videoEncryptionScheme: F.scheme,
|
|
1652
|
+
audioEncryptionScheme: F.scheme
|
|
1648
1653
|
},
|
|
1649
|
-
...
|
|
1654
|
+
...M() ? { requestMediaKeySystemAccessFunc: Ve(t) } : {},
|
|
1650
1655
|
licenseXhrSetup: (e, t) => {
|
|
1651
1656
|
let o = performance.now(), s = () => {
|
|
1652
1657
|
e.open("POST", this.licenseUrl ?? t, !0), e.setRequestHeader("x-api-key", r.apiKey), e.setRequestHeader("X-Match-Urn", n.matchUrn), e.setRequestHeader("X-Device-Id", i), a && e.setRequestHeader("X-User-Id", a), this.licenseRequested = !0, e.addEventListener("loadend", () => {
|
|
@@ -1794,9 +1799,9 @@ var _n = class {
|
|
|
1794
1799
|
this.ladderHeights = t.length ? [...new Set(t)].sort((e, t) => e - t) : void 0, this.ladderTopBitrateKbps = e.levels.length ? Math.round(Math.max(...e.levels.map((e) => e.bitrate)) / 1e3) : 0;
|
|
1795
1800
|
}
|
|
1796
1801
|
dropLevelsWidevineCannotPlay(e) {
|
|
1797
|
-
this.removeLevelsWhere(e, (e) =>
|
|
1798
|
-
let t = new Set(e.levels.map((e) => e.videoCodec).filter((e) => !!e &&
|
|
1799
|
-
for (let n of t)
|
|
1802
|
+
this.removeLevelsWhere(e, (e) => qe(e) !== !0);
|
|
1803
|
+
let t = new Set(e.levels.map((e) => e.videoCodec).filter((e) => !!e && I(e) !== "avc" && !!I(e)));
|
|
1804
|
+
for (let n of t) Ge(n).then((t) => {
|
|
1800
1805
|
t || this.hls !== e || this.removeLevelsWhere(e, (e) => e === n) && (this.measureLadder(e), this.applySizeCap());
|
|
1801
1806
|
});
|
|
1802
1807
|
}
|
|
@@ -1804,10 +1809,10 @@ var _n = class {
|
|
|
1804
1809
|
let n = e.levels ?? [], r = [], i = /* @__PURE__ */ new Set();
|
|
1805
1810
|
if (n.forEach((e, n) => {
|
|
1806
1811
|
let a = e.videoCodec;
|
|
1807
|
-
a &&
|
|
1812
|
+
a && I(a) && t(a) && (r.push(n), i.add(a));
|
|
1808
1813
|
}), !r.length) return !1;
|
|
1809
1814
|
let a = [...i].join(", ");
|
|
1810
|
-
if (!n.some((e, t) => r.includes(t) ? !1 : !
|
|
1815
|
+
if (!n.some((e, t) => r.includes(t) ? !1 : !I(e.videoCodec) || qe(e.videoCodec) === !0)) return console.warn(`havik-player: every rendition is ${a}, which Widevine may not play on this browser; keeping them`), !1;
|
|
1811
1816
|
for (let t = r.length - 1; t >= 0; --t) e.removeLevel(r[t]);
|
|
1812
1817
|
return console.info(`havik-player: skipped ${r.length} rendition(s) Widevine cannot play on this browser (${a})`), !0;
|
|
1813
1818
|
}
|
|
@@ -2047,14 +2052,14 @@ var _n = class {
|
|
|
2047
2052
|
} catch {}
|
|
2048
2053
|
}
|
|
2049
2054
|
startEstimate() {
|
|
2050
|
-
return this.deps.rememberBandwidth === !1 ? Pt :
|
|
2055
|
+
return this.deps.rememberBandwidth === !1 ? Pt : Fe() ?? Pt;
|
|
2051
2056
|
}
|
|
2052
2057
|
maybeRememberBandwidth(e) {
|
|
2053
2058
|
if (this.deps.rememberBandwidth === !1 || this.bwFragsLoaded < Ft) return;
|
|
2054
2059
|
let t = Date.now();
|
|
2055
2060
|
if (!e && t - this.bwSavedAt < It) return;
|
|
2056
2061
|
let n = this.hls?.bandwidthEstimate;
|
|
2057
|
-
typeof n != "number" || !Number.isFinite(n) || n <= 0 || (this.bwSavedAt = t,
|
|
2062
|
+
typeof n != "number" || !Number.isFinite(n) || n <= 0 || (this.bwSavedAt = t, Ie(n));
|
|
2058
2063
|
}
|
|
2059
2064
|
destroy() {
|
|
2060
2065
|
if (this.maybeRememberBandwidth(!0), this.detachFrames?.(), this.detachFrames = null, this.prefetch?.destroy(), this.prefetch = null, 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.onTimeUpdate &&= (this.deps.video.removeEventListener("timeupdate", this.onTimeUpdate), null), this.onPause &&= (this.deps.video.removeEventListener("pause", this.onPause), null), this.onPlay &&= (this.deps.video.removeEventListener("play", this.onPlay), null), this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.networkRetryTimer &&= (clearTimeout(this.networkRetryTimer), null), this.licenseRetryTimer &&= (clearTimeout(this.licenseRetryTimer), null), this.hls) {
|
|
@@ -2160,6 +2165,7 @@ function Dn(e, t) {
|
|
|
2160
2165
|
var On = /* @__PURE__ */ new Set([
|
|
2161
2166
|
"INVALID_URN",
|
|
2162
2167
|
"NOT_FOUND",
|
|
2168
|
+
"GEO_DENIED",
|
|
2163
2169
|
"GONE",
|
|
2164
2170
|
"TOO_EARLY",
|
|
2165
2171
|
"UNAVAILABLE",
|
|
@@ -2623,7 +2629,7 @@ async function Vn() {
|
|
|
2623
2629
|
}
|
|
2624
2630
|
//#endregion
|
|
2625
2631
|
//#region src/core/version.ts
|
|
2626
|
-
var Hn = "2.
|
|
2632
|
+
var Hn = "2.20.0", Un = {
|
|
2627
2633
|
accent: "#E1B600",
|
|
2628
2634
|
accentText: "#1B1C23",
|
|
2629
2635
|
background: "#0e0f13",
|
|
@@ -2646,7 +2652,7 @@ var Hn = "2.19.0", Un = {
|
|
|
2646
2652
|
radius: "--havik-radius",
|
|
2647
2653
|
fontFamily: "--havik-font"
|
|
2648
2654
|
};
|
|
2649
|
-
function
|
|
2655
|
+
function Gn(e, t) {
|
|
2650
2656
|
let n = {
|
|
2651
2657
|
...Un,
|
|
2652
2658
|
...t
|
|
@@ -2656,12 +2662,12 @@ function Z(e, t) {
|
|
|
2656
2662
|
}
|
|
2657
2663
|
//#endregion
|
|
2658
2664
|
//#region src/ui/styles.ts
|
|
2659
|
-
var
|
|
2665
|
+
var Z = "havik-player-styles", Kn = "\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 touch-action:manipulation}\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/* Absolutely centred, NOT a flex sibling of .havik-center: the hidden center\n button (.havik-gone) still occupies its 74px in the row, which pushed an\n in-flow spinner 37px right of centre. */\n.havik-spinner{position:absolute;inset:0;margin:auto;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", qn = /* @__PURE__ */ new WeakMap();
|
|
2660
2666
|
function Jn(e = document, t) {
|
|
2661
|
-
let n = e.querySelector(`style#${
|
|
2667
|
+
let n = e.querySelector(`style#${Z}`);
|
|
2662
2668
|
if (n && (!t || qn.get(n) === t)) return;
|
|
2663
2669
|
let r = e.createElement("style");
|
|
2664
|
-
r.id =
|
|
2670
|
+
r.id = Z, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = Kn, qn.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
|
|
2665
2671
|
}
|
|
2666
2672
|
//#endregion
|
|
2667
2673
|
//#region src/ui/icons.ts
|
|
@@ -2686,13 +2692,18 @@ function Q(e) {
|
|
|
2686
2692
|
var Xn = 3e3, Zn = 250, Qn = /* @__PURE__ */ new Set([
|
|
2687
2693
|
"GONE",
|
|
2688
2694
|
"NOT_FOUND",
|
|
2695
|
+
"GEO_DENIED",
|
|
2689
2696
|
"INVALID_URN",
|
|
2690
2697
|
"UNAUTHORIZED"
|
|
2691
2698
|
]);
|
|
2692
2699
|
function $n(e) {
|
|
2693
|
-
|
|
2700
|
+
let t = "This match isn't available in your country";
|
|
2701
|
+
return e ? `${t} (${e})` : t;
|
|
2702
|
+
}
|
|
2703
|
+
function er(e) {
|
|
2704
|
+
return new tr(e);
|
|
2694
2705
|
}
|
|
2695
|
-
var
|
|
2706
|
+
var tr = class {
|
|
2696
2707
|
root;
|
|
2697
2708
|
video;
|
|
2698
2709
|
player;
|
|
@@ -2846,7 +2857,7 @@ var er = class {
|
|
|
2846
2857
|
this.statusOverlays && (e === "ended" ? t = "ended" : e === "error" ? t = "error" : e === "waiting" ? t = "waiting" : !this.hasPlayed && (e === "idle" || e === "paused") && (t = "preplay")), this.renderStatus(t);
|
|
2847
2858
|
}
|
|
2848
2859
|
statusMessageFor(e) {
|
|
2849
|
-
return e === "ended" ? this.player.endedReason === "interrupted" ? this.interruptedMessage : this.endedMessage : e === "waiting" ? this.hasPlayed ? "Reconnecting — please stand by" : "Starting soon…" : e === "error" ? this.errorMessage
|
|
2860
|
+
return e === "ended" ? this.player.endedReason === "interrupted" ? this.interruptedMessage : this.endedMessage : e === "waiting" ? this.hasPlayed ? "Reconnecting — please stand by" : "Starting soon…" : e === "error" ? this.errorMessage === void 0 ? this.lastError?.code === "GEO_DENIED" ? $n(this.lastError.country) : this.lastError?.message ?? "Playback unavailable" : this.errorMessage : null;
|
|
2850
2861
|
}
|
|
2851
2862
|
renderStatus(e) {
|
|
2852
2863
|
let t = this.statusMessageFor(e);
|
|
@@ -2902,7 +2913,7 @@ var er = class {
|
|
|
2902
2913
|
let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
|
|
2903
2914
|
this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
|
|
2904
2915
|
}
|
|
2905
|
-
!this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${
|
|
2916
|
+
!this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${nr(this.video.currentTime)} / ${nr(this.video.duration)}`);
|
|
2906
2917
|
}
|
|
2907
2918
|
syncLive() {
|
|
2908
2919
|
let e = this.player.getStats(), t = e.isLive !== !1, n = this.player.state === "ended", r = !t && !this.streamEnded();
|
|
@@ -2952,17 +2963,17 @@ var er = class {
|
|
|
2952
2963
|
renderQualityMenu() {
|
|
2953
2964
|
let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
|
|
2954
2965
|
n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
|
|
2955
|
-
for (let n of e) this.menu.append(this.menuItem(
|
|
2966
|
+
for (let n of e) this.menu.append(this.menuItem(rr(n), t === n.index, () => this.player.setQuality(n.index)));
|
|
2956
2967
|
}
|
|
2957
2968
|
renderAudioMenu() {
|
|
2958
2969
|
let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
|
|
2959
2970
|
t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
|
|
2960
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
2971
|
+
for (let t of e) this.menu.append(this.menuItem(ir(t), t.default, () => this.player.setAudioTrack(t.id)));
|
|
2961
2972
|
}
|
|
2962
2973
|
renderCcMenu() {
|
|
2963
2974
|
let e = this.player.getTextTracks(), t = this.doc.createElement("div");
|
|
2964
2975
|
t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
|
|
2965
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
2976
|
+
for (let t of e) this.menu.append(this.menuItem(ar(t), !1, () => this.player.setTextTrack(t.id)));
|
|
2966
2977
|
}
|
|
2967
2978
|
onTracksChanged() {
|
|
2968
2979
|
this.openMenu && this.renderMenu(this.openMenu);
|
|
@@ -3067,41 +3078,41 @@ var er = class {
|
|
|
3067
3078
|
this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
|
|
3068
3079
|
}
|
|
3069
3080
|
};
|
|
3070
|
-
function
|
|
3081
|
+
function nr(e) {
|
|
3071
3082
|
(!Number.isFinite(e) || e < 0) && (e = 0);
|
|
3072
3083
|
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");
|
|
3073
3084
|
return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
|
|
3074
3085
|
}
|
|
3075
|
-
function
|
|
3086
|
+
function rr(e) {
|
|
3076
3087
|
return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
|
|
3077
3088
|
}
|
|
3078
|
-
function
|
|
3089
|
+
function ir(e) {
|
|
3079
3090
|
return e.name || e.lang || `Track ${e.id}`;
|
|
3080
3091
|
}
|
|
3081
|
-
function
|
|
3092
|
+
function ar(e) {
|
|
3082
3093
|
return e.name || e.lang || `Track ${e.id}`;
|
|
3083
3094
|
}
|
|
3084
3095
|
//#endregion
|
|
3085
3096
|
//#region src/managed/index.ts
|
|
3086
|
-
var
|
|
3087
|
-
function
|
|
3097
|
+
var or = 6e3, sr = 45e3, cr = 2e3, lr = 3, ur = 5e3, dr = 2e3, fr = 48e4, pr = "keySystemLicenseRequestFailed";
|
|
3098
|
+
function mr(e) {
|
|
3088
3099
|
if (e.env !== void 0 && e.baseUrl !== void 0) throw new n("INTERNAL", 0, `createPlayer: pass env OR baseUrl, not both (got env=${JSON.stringify(e.env)} and baseUrl=${JSON.stringify(e.baseUrl)})`);
|
|
3089
|
-
if (e.env !== void 0) return
|
|
3100
|
+
if (e.env !== void 0) return it(e.env);
|
|
3090
3101
|
if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
|
|
3091
3102
|
return e.baseUrl;
|
|
3092
3103
|
}
|
|
3093
|
-
async function
|
|
3104
|
+
async function hr(e) {
|
|
3094
3105
|
if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
|
|
3095
|
-
let t =
|
|
3106
|
+
let t = mr(e);
|
|
3096
3107
|
if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
|
|
3097
3108
|
if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
|
|
3098
|
-
let r = new
|
|
3109
|
+
let r = new gr({
|
|
3099
3110
|
...e,
|
|
3100
3111
|
baseUrl: t
|
|
3101
3112
|
});
|
|
3102
3113
|
return await r.start(), r;
|
|
3103
3114
|
}
|
|
3104
|
-
var
|
|
3115
|
+
var gr = class {
|
|
3105
3116
|
opts;
|
|
3106
3117
|
abort = new AbortController();
|
|
3107
3118
|
listeners = {};
|
|
@@ -3196,7 +3207,7 @@ var hr = class {
|
|
|
3196
3207
|
setTimeout(() => {
|
|
3197
3208
|
a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
|
|
3198
3209
|
phase: i ? "unavailable" : "tooEarly",
|
|
3199
|
-
retryInMs:
|
|
3210
|
+
retryInMs: sr,
|
|
3200
3211
|
attempt: 1,
|
|
3201
3212
|
elapsedMs: 0,
|
|
3202
3213
|
...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
|
|
@@ -3235,13 +3246,13 @@ var hr = class {
|
|
|
3235
3246
|
!a && !p() && h();
|
|
3236
3247
|
}, e);
|
|
3237
3248
|
};
|
|
3238
|
-
_(
|
|
3249
|
+
_(or), this.armingSub = E({
|
|
3239
3250
|
eventsBaseUrl: t,
|
|
3240
3251
|
matchUrn: this.opts.matchUrn,
|
|
3241
3252
|
credential: this.opts.credential,
|
|
3242
|
-
onAlive: () => _(
|
|
3253
|
+
onAlive: () => _(sr),
|
|
3243
3254
|
onState: (e) => {
|
|
3244
|
-
a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() *
|
|
3255
|
+
a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * cr);
|
|
3245
3256
|
},
|
|
3246
3257
|
onError: () => h()
|
|
3247
3258
|
});
|
|
@@ -3305,7 +3316,7 @@ var hr = class {
|
|
|
3305
3316
|
this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, e));
|
|
3306
3317
|
}, 0),
|
|
3307
3318
|
sample: () => {
|
|
3308
|
-
let e = this.engine?.getStats() ??
|
|
3319
|
+
let e = this.engine?.getStats() ?? z, t = c.buffered.length ? Math.max(0, c.buffered.end(c.buffered.length - 1) - c.currentTime) : 0;
|
|
3309
3320
|
return {
|
|
3310
3321
|
...e,
|
|
3311
3322
|
bufferedMs: Math.round(t * 1e3),
|
|
@@ -3412,7 +3423,7 @@ var hr = class {
|
|
|
3412
3423
|
r = 0, i = !1;
|
|
3413
3424
|
},
|
|
3414
3425
|
onError: () => {
|
|
3415
|
-
this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >=
|
|
3426
|
+
this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= lr && (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.`))));
|
|
3416
3427
|
}
|
|
3417
3428
|
});
|
|
3418
3429
|
}
|
|
@@ -3426,7 +3437,7 @@ var hr = class {
|
|
|
3426
3437
|
return;
|
|
3427
3438
|
}
|
|
3428
3439
|
let e = this.hiddenAtMs;
|
|
3429
|
-
this.hiddenAtMs = null, e != null && Date.now() - e >=
|
|
3440
|
+
this.hiddenAtMs = null, e != null && Date.now() - e >= ur && this.resyncLiveState();
|
|
3430
3441
|
}, this.onReturnPageShow = (e) => {
|
|
3431
3442
|
e.persisted && this.resyncLiveState();
|
|
3432
3443
|
}, document.addEventListener("visibilitychange", this.onReturnVisible), typeof window < "u" && window.addEventListener("pageshow", this.onReturnPageShow));
|
|
@@ -3458,7 +3469,7 @@ var hr = class {
|
|
|
3458
3469
|
scheduleRejoin(e) {
|
|
3459
3470
|
this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
|
|
3460
3471
|
this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
|
|
3461
|
-
}, Math.random() *
|
|
3472
|
+
}, Math.random() * cr));
|
|
3462
3473
|
}
|
|
3463
3474
|
cancelRejoin() {
|
|
3464
3475
|
this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
|
|
@@ -3482,7 +3493,7 @@ var hr = class {
|
|
|
3482
3493
|
}
|
|
3483
3494
|
t.controls = !1;
|
|
3484
3495
|
let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
|
|
3485
|
-
r && r.insertBefore(n, t), n.appendChild(t),
|
|
3496
|
+
r && r.insertBefore(n, t), n.appendChild(t), Gn(n, this.opts.theme), this.container = n, this.controlBar = er({
|
|
3486
3497
|
root: n,
|
|
3487
3498
|
video: t,
|
|
3488
3499
|
player: this,
|
|
@@ -3520,7 +3531,7 @@ var hr = class {
|
|
|
3520
3531
|
}
|
|
3521
3532
|
startStats() {
|
|
3522
3533
|
this.stopStats();
|
|
3523
|
-
let e = Math.max(500, this.opts.statsIntervalMs ??
|
|
3534
|
+
let e = Math.max(500, this.opts.statsIntervalMs ?? dr);
|
|
3524
3535
|
this.statsTimer = setInterval(() => {
|
|
3525
3536
|
if (!this.engine) return;
|
|
3526
3537
|
let e = this.engine.getStats();
|
|
@@ -3531,14 +3542,14 @@ var hr = class {
|
|
|
3531
3542
|
this.statsTimer && clearInterval(this.statsTimer), this.statsTimer = void 0;
|
|
3532
3543
|
}
|
|
3533
3544
|
isStaleLicenseDenial(e) {
|
|
3534
|
-
return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !==
|
|
3545
|
+
return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== pr || e.error.httpStatus !== 403 ? !1 : K(this._descriptor?.drm?.widevine?.licenseUrl);
|
|
3535
3546
|
}
|
|
3536
3547
|
scheduleLicenseRefresh() {
|
|
3537
3548
|
if (this.stopRefresh(), !this._descriptor?.drmEnabled) return;
|
|
3538
3549
|
typeof document < "u" && this.engine?.name === "hls" && (this.onRefocus = () => {
|
|
3539
3550
|
document.visibilityState !== "visible" || this.destroyed || K(this._descriptor?.drm?.widevine?.licenseUrl) && this.refreshLicense();
|
|
3540
3551
|
}, document.addEventListener("visibilitychange", this.onRefocus));
|
|
3541
|
-
let e = this.opts.licenseRefreshMs ??
|
|
3552
|
+
let e = this.opts.licenseRefreshMs ?? fr;
|
|
3542
3553
|
e && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
|
|
3543
3554
|
}
|
|
3544
3555
|
stopRefresh() {
|
|
@@ -3595,7 +3606,7 @@ var hr = class {
|
|
|
3595
3606
|
this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
|
|
3596
3607
|
}
|
|
3597
3608
|
getStats() {
|
|
3598
|
-
return this.engine?.getStats() ??
|
|
3609
|
+
return this.engine?.getStats() ?? z;
|
|
3599
3610
|
}
|
|
3600
3611
|
getQualityLevels() {
|
|
3601
3612
|
return this.engine?.getQualityLevels() ?? [];
|
|
@@ -3708,7 +3719,7 @@ function $(e) {
|
|
|
3708
3719
|
}
|
|
3709
3720
|
//#endregion
|
|
3710
3721
|
//#region src/embed/host.ts
|
|
3711
|
-
function
|
|
3722
|
+
function _r(e) {
|
|
3712
3723
|
if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
|
|
3713
3724
|
if (!e.src) throw Error("mountEmbed: opts.src is required");
|
|
3714
3725
|
if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
|
|
@@ -3767,15 +3778,15 @@ function gr(e) {
|
|
|
3767
3778
|
}
|
|
3768
3779
|
//#endregion
|
|
3769
3780
|
//#region src/embed/page.ts
|
|
3770
|
-
var
|
|
3771
|
-
function
|
|
3781
|
+
var vr = /^[A-Za-z0-9:_.-]{1,128}$/;
|
|
3782
|
+
function yr(e) {
|
|
3772
3783
|
if (typeof e == "boolean") return e;
|
|
3773
3784
|
if (typeof e == "object" && e) {
|
|
3774
3785
|
let t = e.endpoint;
|
|
3775
3786
|
return typeof t == "string" ? { endpoint: t } : {};
|
|
3776
3787
|
}
|
|
3777
3788
|
}
|
|
3778
|
-
function
|
|
3789
|
+
function br() {
|
|
3779
3790
|
let e = window.HAVIK_EMBED_CONFIG;
|
|
3780
3791
|
if (e) return {
|
|
3781
3792
|
baseUrl: e.baseUrl ?? "",
|
|
@@ -3785,7 +3796,7 @@ function yr() {
|
|
|
3785
3796
|
autoplay: e.autoplay ?? !0,
|
|
3786
3797
|
muted: e.muted ?? !0,
|
|
3787
3798
|
waitForLive: e.waitForLive ?? !0,
|
|
3788
|
-
analytics:
|
|
3799
|
+
analytics: yr(e.analytics)
|
|
3789
3800
|
};
|
|
3790
3801
|
let t = new URLSearchParams(location.search);
|
|
3791
3802
|
return {
|
|
@@ -3798,9 +3809,9 @@ function yr() {
|
|
|
3798
3809
|
waitForLive: !0
|
|
3799
3810
|
};
|
|
3800
3811
|
}
|
|
3801
|
-
async function
|
|
3802
|
-
let e =
|
|
3803
|
-
|
|
3812
|
+
async function xr() {
|
|
3813
|
+
let e = br();
|
|
3814
|
+
Cr();
|
|
3804
3815
|
let t = document.createElement("video");
|
|
3805
3816
|
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);
|
|
3806
3817
|
let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
|
|
@@ -3853,14 +3864,15 @@ async function br() {
|
|
|
3853
3864
|
})), e.on("error", (e) => i({
|
|
3854
3865
|
type: "oddin:error",
|
|
3855
3866
|
code: e.code,
|
|
3856
|
-
message: e.message
|
|
3867
|
+
message: e.message,
|
|
3868
|
+
...e.country === void 0 ? {} : { country: e.country }
|
|
3857
3869
|
})), e.on("autoplayblocked", () => {
|
|
3858
|
-
i({ type: "oddin:autoplayblocked" }),
|
|
3870
|
+
i({ type: "oddin:autoplayblocked" }), Sr(t, e);
|
|
3859
3871
|
});
|
|
3860
3872
|
}, s = async (r) => {
|
|
3861
3873
|
a?.destroy(), a = null;
|
|
3862
3874
|
try {
|
|
3863
|
-
let n = await
|
|
3875
|
+
let n = await hr({
|
|
3864
3876
|
video: t,
|
|
3865
3877
|
baseUrl: e.baseUrl,
|
|
3866
3878
|
matchUrn: r,
|
|
@@ -3874,11 +3886,12 @@ async function br() {
|
|
|
3874
3886
|
let i = t.parentElement;
|
|
3875
3887
|
i?.classList.contains("havik-player") && (i.style.cssText = "position:absolute;inset:0;width:100%;height:100%;aspect-ratio:auto"), o(n);
|
|
3876
3888
|
} catch (e) {
|
|
3877
|
-
let t = e instanceof n ? e.code : "INTERNAL", r = e instanceof Error ? e.message : String(e);
|
|
3889
|
+
let t = e instanceof n ? e.code : "INTERNAL", r = e instanceof Error ? e.message : String(e), a = e instanceof n ? e.country : void 0;
|
|
3878
3890
|
i({
|
|
3879
3891
|
type: "oddin:error",
|
|
3880
3892
|
code: t,
|
|
3881
|
-
message: r
|
|
3893
|
+
message: r,
|
|
3894
|
+
...a === void 0 ? {} : { country: a }
|
|
3882
3895
|
});
|
|
3883
3896
|
}
|
|
3884
3897
|
}, c = () => {
|
|
@@ -3922,7 +3935,7 @@ async function br() {
|
|
|
3922
3935
|
a?.retry();
|
|
3923
3936
|
break;
|
|
3924
3937
|
case "oddin:load":
|
|
3925
|
-
typeof e.matchUrn == "string" &&
|
|
3938
|
+
typeof e.matchUrn == "string" && vr.test(e.matchUrn) && s(e.matchUrn);
|
|
3926
3939
|
break;
|
|
3927
3940
|
case "oddin:destroy": c();
|
|
3928
3941
|
}
|
|
@@ -3934,18 +3947,18 @@ async function br() {
|
|
|
3934
3947
|
protocol: 1
|
|
3935
3948
|
});
|
|
3936
3949
|
}
|
|
3937
|
-
function
|
|
3950
|
+
function Sr(e, t) {
|
|
3938
3951
|
if (document.getElementById("havik-tap")) return;
|
|
3939
3952
|
let n = document.createElement("button");
|
|
3940
3953
|
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", () => {
|
|
3941
3954
|
n.remove(), t.play();
|
|
3942
3955
|
}), document.body.appendChild(n);
|
|
3943
3956
|
}
|
|
3944
|
-
function
|
|
3957
|
+
function Cr() {
|
|
3945
3958
|
let e = document.createElement("style");
|
|
3946
3959
|
e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
|
|
3947
3960
|
}
|
|
3948
3961
|
//#endregion
|
|
3949
|
-
export { Un as ODDIN_THEME, n as PlaybackError,
|
|
3962
|
+
export { Un as ODDIN_THEME, n as PlaybackError, Gn as applyTheme, it as baseUrlForEnv, xr as bootEmbed, Te as clearDeviceId, Le as clearRememberedBandwidth, hr as createPlayer, D as deriveEventsBaseUrl, et as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, _e as fetchCatalog, ye as fetchTournament, xe as flattenMatches, C as fullscreenSupported, A as getDeviceId, nt as isLowLatencyManifest, $ as isOddinMessage, De as licenseRequestHeaders, _r as mountEmbed, de as pollToLive, y as resolveStream, E as subscribeLiveState, S as videoFullscreenActive, Ce as watchStatus };
|
|
3950
3963
|
|
|
3951
3964
|
//# sourceMappingURL=index.js.map
|