@oddin-gg/havik-player 2.2.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/havik-player.global.js +17 -17
- 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.js +198 -177
- package/dist/index.js.map +1 -1
- package/embed/index.html +1 -1
- package/examples/vanilla.html +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -516,12 +516,56 @@ function ve(e, t, n = {}) {
|
|
|
516
516
|
return n.userId && (r["X-User-Id"] = n.userId), r;
|
|
517
517
|
}
|
|
518
518
|
//#endregion
|
|
519
|
+
//#region src/core/drm/cdm.ts
|
|
520
|
+
var j = /* @__PURE__ */ new WeakMap();
|
|
521
|
+
function ye(e, t) {
|
|
522
|
+
let n = j.get(e);
|
|
523
|
+
n || (n = /* @__PURE__ */ new Map(), j.set(e, n));
|
|
524
|
+
let r = n.get(t.keySystem);
|
|
525
|
+
if (r) return r;
|
|
526
|
+
let i = t.createMediaKeys().catch((e) => {
|
|
527
|
+
throw n.get(t.keySystem) === i && n.delete(t.keySystem), e;
|
|
528
|
+
});
|
|
529
|
+
return n.set(t.keySystem, i), i;
|
|
530
|
+
}
|
|
531
|
+
function M() {
|
|
532
|
+
return typeof navigator < "u" && typeof navigator.requestMediaKeySystemAccess == "function";
|
|
533
|
+
}
|
|
534
|
+
var N = ["SW_SECURE_DECODE", "SW_SECURE_CRYPTO"];
|
|
535
|
+
function be(e) {
|
|
536
|
+
return e.startsWith("com.widevine");
|
|
537
|
+
}
|
|
538
|
+
function P(e, t) {
|
|
539
|
+
if (!be(e)) return t;
|
|
540
|
+
let n = (e, t) => {
|
|
541
|
+
let n = e.videoCapabilities;
|
|
542
|
+
return !n?.length || !n.some((e) => e.robustness) ? !1 : n.every((e) => {
|
|
543
|
+
let n = N.indexOf(e.robustness);
|
|
544
|
+
return n === -1 || N.indexOf(t) > n;
|
|
545
|
+
});
|
|
546
|
+
}, r = N.flatMap((e) => t.filter((t) => n(t, e)).map((t) => ({
|
|
547
|
+
...t,
|
|
548
|
+
videoCapabilities: t.videoCapabilities?.map((t) => ({
|
|
549
|
+
...t,
|
|
550
|
+
robustness: e
|
|
551
|
+
}))
|
|
552
|
+
})));
|
|
553
|
+
return [...t, ...r];
|
|
554
|
+
}
|
|
555
|
+
function xe(e) {
|
|
556
|
+
return (t, n) => M() ? navigator.requestMediaKeySystemAccess(t, P(t, n)).then((t) => ({
|
|
557
|
+
keySystem: t.keySystem,
|
|
558
|
+
getConfiguration: () => t.getConfiguration(),
|
|
559
|
+
createMediaKeys: () => ye(e, t)
|
|
560
|
+
})) : Promise.reject(/* @__PURE__ */ Error("navigator.requestMediaKeySystemAccess is unavailable — EME needs a secure context (https, or localhost)"));
|
|
561
|
+
}
|
|
562
|
+
//#endregion
|
|
519
563
|
//#region src/core/drm/support.ts
|
|
520
|
-
var
|
|
564
|
+
var Se = 5e3, Ce = "video/mp4; codecs=\"avc1.42E01E\"", F = "audio/mp4; codecs=\"mp4a.40.2\"", we = {
|
|
521
565
|
widevine: ["com.widevine.alpha"],
|
|
522
566
|
fairplay: ["com.apple.fps"]
|
|
523
567
|
};
|
|
524
|
-
function
|
|
568
|
+
function Te(e) {
|
|
525
569
|
return e === "fairplay" ? [{
|
|
526
570
|
initDataTypes: [
|
|
527
571
|
"sinf",
|
|
@@ -529,30 +573,30 @@ function xe(e) {
|
|
|
529
573
|
"cenc"
|
|
530
574
|
],
|
|
531
575
|
videoCapabilities: [{
|
|
532
|
-
contentType:
|
|
576
|
+
contentType: Ce,
|
|
533
577
|
robustness: "",
|
|
534
578
|
encryptionScheme: "cbcs"
|
|
535
579
|
}],
|
|
536
580
|
audioCapabilities: [{
|
|
537
|
-
contentType:
|
|
581
|
+
contentType: F,
|
|
538
582
|
robustness: "",
|
|
539
583
|
encryptionScheme: "cbcs"
|
|
540
584
|
}]
|
|
541
|
-
}] : [{
|
|
585
|
+
}] : P("com.widevine.alpha", [{
|
|
542
586
|
initDataTypes: ["cenc"],
|
|
543
587
|
videoCapabilities: [{
|
|
544
|
-
contentType:
|
|
588
|
+
contentType: Ce,
|
|
545
589
|
robustness: "SW_SECURE_DECODE",
|
|
546
590
|
encryptionScheme: "cbcs"
|
|
547
591
|
}],
|
|
548
592
|
audioCapabilities: [{
|
|
549
|
-
contentType:
|
|
593
|
+
contentType: F,
|
|
550
594
|
robustness: "SW_SECURE_CRYPTO",
|
|
551
595
|
encryptionScheme: "cbcs"
|
|
552
596
|
}]
|
|
553
|
-
}];
|
|
597
|
+
}]);
|
|
554
598
|
}
|
|
555
|
-
function
|
|
599
|
+
function I(e, t) {
|
|
556
600
|
return new Promise((n, r) => {
|
|
557
601
|
let i = setTimeout(() => r(/* @__PURE__ */ Error("probe-timeout")), t);
|
|
558
602
|
e.then((e) => {
|
|
@@ -562,8 +606,8 @@ function N(e, t) {
|
|
|
562
606
|
});
|
|
563
607
|
});
|
|
564
608
|
}
|
|
565
|
-
async function
|
|
566
|
-
let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ??
|
|
609
|
+
async function Ee(e = {}) {
|
|
610
|
+
let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? Se, r = {};
|
|
567
611
|
if (typeof window < "u" && window.isSecureContext === !1) return {
|
|
568
612
|
verdict: "insecure-context",
|
|
569
613
|
detail: r
|
|
@@ -575,7 +619,7 @@ async function Se(e = {}) {
|
|
|
575
619
|
};
|
|
576
620
|
let a = !1, o = !1, s = Date.now() + n, c = () => Math.max(1, s - Date.now());
|
|
577
621
|
for (let e of t) {
|
|
578
|
-
let t =
|
|
622
|
+
let t = we[e];
|
|
579
623
|
if (!t) {
|
|
580
624
|
r[e] = "unknown system";
|
|
581
625
|
continue;
|
|
@@ -586,7 +630,7 @@ async function Se(e = {}) {
|
|
|
586
630
|
break;
|
|
587
631
|
}
|
|
588
632
|
try {
|
|
589
|
-
return await
|
|
633
|
+
return await I((await I(i(n, Te(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
|
|
590
634
|
verdict: "supported",
|
|
591
635
|
system: e,
|
|
592
636
|
detail: r
|
|
@@ -610,44 +654,21 @@ async function Se(e = {}) {
|
|
|
610
654
|
}
|
|
611
655
|
//#endregion
|
|
612
656
|
//#region src/core/manifest.ts
|
|
613
|
-
var
|
|
614
|
-
function
|
|
615
|
-
return
|
|
616
|
-
}
|
|
617
|
-
//#endregion
|
|
618
|
-
//#region src/core/drm/cdm.ts
|
|
619
|
-
var Te = /* @__PURE__ */ new WeakMap();
|
|
620
|
-
function Ee(e, t) {
|
|
621
|
-
let n = Te.get(e);
|
|
622
|
-
n || (n = /* @__PURE__ */ new Map(), Te.set(e, n));
|
|
623
|
-
let r = n.get(t.keySystem);
|
|
624
|
-
if (r) return r;
|
|
625
|
-
let i = t.createMediaKeys().catch((e) => {
|
|
626
|
-
throw n.get(t.keySystem) === i && n.delete(t.keySystem), e;
|
|
627
|
-
});
|
|
628
|
-
return n.set(t.keySystem, i), i;
|
|
629
|
-
}
|
|
630
|
-
function P() {
|
|
631
|
-
return typeof navigator < "u" && typeof navigator.requestMediaKeySystemAccess == "function";
|
|
632
|
-
}
|
|
633
|
-
function De(e) {
|
|
634
|
-
return (t, n) => P() ? navigator.requestMediaKeySystemAccess(t, n).then((t) => ({
|
|
635
|
-
keySystem: t.keySystem,
|
|
636
|
-
getConfiguration: () => t.getConfiguration(),
|
|
637
|
-
createMediaKeys: () => Ee(e, t)
|
|
638
|
-
})) : Promise.reject(/* @__PURE__ */ Error("navigator.requestMediaKeySystemAccess is unavailable — EME needs a secure context (https, or localhost)"));
|
|
657
|
+
var De = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
|
|
658
|
+
function Oe(e) {
|
|
659
|
+
return De.test(e);
|
|
639
660
|
}
|
|
640
661
|
//#endregion
|
|
641
662
|
//#region src/core/stats.ts
|
|
642
|
-
var
|
|
643
|
-
function
|
|
663
|
+
var L = { droppedFrames: 0 };
|
|
664
|
+
function R(e) {
|
|
644
665
|
let t = e.getVideoPlaybackQuality?.();
|
|
645
666
|
return {
|
|
646
667
|
dropped: t?.droppedVideoFrames ?? 0,
|
|
647
668
|
decoded: t?.totalVideoFrames ?? 0
|
|
648
669
|
};
|
|
649
670
|
}
|
|
650
|
-
function
|
|
671
|
+
function ke(e, t) {
|
|
651
672
|
let n = [
|
|
652
673
|
["playing", () => t("playing")],
|
|
653
674
|
["waiting", () => t("buffering")],
|
|
@@ -664,7 +685,7 @@ function Oe(e, t) {
|
|
|
664
685
|
}
|
|
665
686
|
//#endregion
|
|
666
687
|
//#region src/core/engine/native.ts
|
|
667
|
-
function
|
|
688
|
+
function Ae(e) {
|
|
668
689
|
try {
|
|
669
690
|
let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
|
|
670
691
|
return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
|
|
@@ -672,15 +693,15 @@ function ke(e) {
|
|
|
672
693
|
return;
|
|
673
694
|
}
|
|
674
695
|
}
|
|
675
|
-
function
|
|
696
|
+
function z(e) {
|
|
676
697
|
return !!(e ?? (typeof document < "u" ? document.createElement("video") : void 0))?.canPlayType("application/vnd.apple.mpegurl");
|
|
677
698
|
}
|
|
678
|
-
function
|
|
699
|
+
function B() {
|
|
679
700
|
if (typeof window > "u") return !1;
|
|
680
701
|
let e = "WebKitMediaKeys" in window || typeof HTMLMediaElement < "u" && "webkitSetMediaKeys" in HTMLMediaElement.prototype, t = typeof navigator < "u" && /Apple/i.test(navigator.vendor ?? "");
|
|
681
702
|
return e || t;
|
|
682
703
|
}
|
|
683
|
-
var
|
|
704
|
+
var V = class {
|
|
684
705
|
name = "native";
|
|
685
706
|
deps;
|
|
686
707
|
onLoaded;
|
|
@@ -714,7 +735,7 @@ var z = class {
|
|
|
714
735
|
type: "error",
|
|
715
736
|
error: new n("INTERNAL", 0, "native HLS playback error"),
|
|
716
737
|
fatal: !0
|
|
717
|
-
}), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src =
|
|
738
|
+
}), this.onLoaded && e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = je(t.manifestUrl, t.analytics?.sid), e.load();
|
|
718
739
|
}
|
|
719
740
|
startLoad() {
|
|
720
741
|
this.loadStarted || (this.loadStarted = !0, this.restorePreload());
|
|
@@ -736,7 +757,7 @@ var z = class {
|
|
|
736
757
|
});
|
|
737
758
|
this.onEncrypted = (n) => {
|
|
738
759
|
let i = n;
|
|
739
|
-
!i.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV =
|
|
760
|
+
!i.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV = Ae(i.initData), (async () => {
|
|
740
761
|
try {
|
|
741
762
|
let n = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
|
|
742
763
|
initDataTypes: [i.initDataType],
|
|
@@ -831,7 +852,7 @@ var z = class {
|
|
|
831
852
|
e.seekable.length > 0 && (e.currentTime = e.seekable.end(e.seekable.length - 1));
|
|
832
853
|
}
|
|
833
854
|
getStats() {
|
|
834
|
-
let e = this.deps.video, t =
|
|
855
|
+
let e = this.deps.video, t = R(e), n = {
|
|
835
856
|
droppedFrames: t.dropped,
|
|
836
857
|
decodedFrames: t.decoded
|
|
837
858
|
};
|
|
@@ -854,36 +875,36 @@ var z = class {
|
|
|
854
875
|
} catch {}
|
|
855
876
|
}
|
|
856
877
|
};
|
|
857
|
-
function
|
|
878
|
+
function je(e, t) {
|
|
858
879
|
return t ? `${e}${e.includes("?") ? "&" : "?"}CMCD=${encodeURIComponent(`sid="${t}"`)}` : e;
|
|
859
880
|
}
|
|
860
881
|
//#endregion
|
|
861
882
|
//#region src/core/engine/hls.ts
|
|
862
|
-
function
|
|
883
|
+
function Me() {
|
|
863
884
|
return e.isSupported();
|
|
864
885
|
}
|
|
865
|
-
var
|
|
866
|
-
function
|
|
886
|
+
var Ne = 3, Pe = 5, Fe = 3, Ie = 3e4, Le = 12e3, Re = 3e3, H = 6e3, ze = 4e3, Be = 2, Ve = 5, He = 2, Ue = 2, We = 2e3, Ge = 3e4, Ke = 2, qe = 9, Je = .5, Ye = 64;
|
|
887
|
+
function Xe(e) {
|
|
867
888
|
try {
|
|
868
889
|
return new URL(e).host;
|
|
869
890
|
} catch {
|
|
870
891
|
return "";
|
|
871
892
|
}
|
|
872
893
|
}
|
|
873
|
-
function
|
|
894
|
+
function Ze(e) {
|
|
874
895
|
if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
|
|
875
896
|
delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
|
|
876
897
|
return;
|
|
877
898
|
}
|
|
878
|
-
e.liveSyncDuration ??=
|
|
879
|
-
let t = Math.max(12, e.liveSyncDuration +
|
|
880
|
-
(e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t),
|
|
899
|
+
e.liveSyncDuration ??= Ke;
|
|
900
|
+
let t = Math.max(12, e.liveSyncDuration + qe);
|
|
901
|
+
(e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Qe(e, e.liveSyncDuration);
|
|
881
902
|
}
|
|
882
|
-
function
|
|
903
|
+
function Qe(e, t) {
|
|
883
904
|
let n = (e, t, n) => Math.min(n, Math.max(t, e));
|
|
884
905
|
e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
|
|
885
906
|
}
|
|
886
|
-
var
|
|
907
|
+
var U = class {
|
|
887
908
|
name = "hls";
|
|
888
909
|
hls = null;
|
|
889
910
|
deps;
|
|
@@ -937,7 +958,7 @@ var V = class {
|
|
|
937
958
|
this.deps = e, this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl, this.loadStarted = e.deferLoad !== !0;
|
|
938
959
|
}
|
|
939
960
|
async load() {
|
|
940
|
-
let { descriptor: e, snapToLiveOnRefocus: t, video: r, emit: i } = this.deps, a =
|
|
961
|
+
let { descriptor: e, snapToLiveOnRefocus: t, video: r, emit: i } = this.deps, a = z(r) && B(), o = !!e.drm?.widevine?.licenseUrl && !a;
|
|
941
962
|
if (e.drmEnabled && !o) {
|
|
942
963
|
i({
|
|
943
964
|
type: "error",
|
|
@@ -977,7 +998,7 @@ var V = class {
|
|
|
977
998
|
let t = e.liveSyncPosition;
|
|
978
999
|
if (typeof t != "number" || !Number.isFinite(t)) return;
|
|
979
1000
|
let { video: n } = this.deps;
|
|
980
|
-
n.currentTime <= t +
|
|
1001
|
+
n.currentTime <= t + Je || (n.currentTime = t);
|
|
981
1002
|
}
|
|
982
1003
|
startHls() {
|
|
983
1004
|
let { video: t, descriptor: n, credential: r, deviceId: i, userId: a, lowLatency: o, debug: s, hlsConfig: c, emit: l } = this.deps;
|
|
@@ -1001,7 +1022,7 @@ var V = class {
|
|
|
1001
1022
|
videoEncryptionScheme: "cbcs",
|
|
1002
1023
|
audioEncryptionScheme: "cbcs"
|
|
1003
1024
|
},
|
|
1004
|
-
...
|
|
1025
|
+
...M() ? { requestMediaKeySystemAccessFunc: xe(t) } : {},
|
|
1005
1026
|
licenseXhrSetup: (e, t) => {
|
|
1006
1027
|
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;
|
|
1007
1028
|
let o = performance.now();
|
|
@@ -1017,7 +1038,7 @@ var V = class {
|
|
|
1017
1038
|
} } : {},
|
|
1018
1039
|
...c
|
|
1019
1040
|
};
|
|
1020
|
-
|
|
1041
|
+
Ze(d), this.sizeCapManaged = d.capLevelToPlayerSize !== !0, this.loadStarted = d.autoStartLoad !== !1;
|
|
1021
1042
|
let f = new e(d);
|
|
1022
1043
|
this.hls = f, f.on(e.Events.MANIFEST_PARSED, () => {
|
|
1023
1044
|
this.mediaRecoveries = 0, this.networkRecoveries = 0;
|
|
@@ -1026,17 +1047,17 @@ var V = class {
|
|
|
1026
1047
|
}), f.on(e.Events.FRAG_BUFFERED, (e, t) => {
|
|
1027
1048
|
t?.frag?.type === "main" && (this.kickPending = !1, this.kickTicks = 0, (this.mediaRecoveries > 0 || this.networkRecoveries > 0 || this.licenseRecoveries > 0 || this.starvationKicks > 0 || this.starvationRebuilds > 0) && !this.recoveryResetTimer && (this.recoveryResetTimer = setTimeout(() => {
|
|
1028
1049
|
this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.starvationRebuilds = 0, this.recoveryResetTimer = null;
|
|
1029
|
-
},
|
|
1050
|
+
}, Ie)));
|
|
1030
1051
|
}), f.on(e.Events.FRAG_LOADING, (e, t) => {
|
|
1031
1052
|
t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
|
|
1032
1053
|
}), f.on(e.Events.FRAG_LOADED, (e, t) => {
|
|
1033
1054
|
let n = t?.frag?.stats, r = n?.loaded;
|
|
1034
1055
|
typeof r == "number" && (this.bytesLoaded += r);
|
|
1035
1056
|
let i = n?.loading;
|
|
1036
|
-
i && i.end > 0 && i.start > 0 && (this.requestCount += 1, this.requestTimesMs.push(Math.max(0, Math.round(i.end - i.start))), this.requestTimesMs.length >
|
|
1057
|
+
i && i.end > 0 && i.start > 0 && (this.requestCount += 1, this.requestTimesMs.push(Math.max(0, Math.round(i.end - i.start))), this.requestTimesMs.length > Ye && this.requestTimesMs.shift());
|
|
1037
1058
|
let a = t?.frag?.url;
|
|
1038
1059
|
if (a) {
|
|
1039
|
-
let e =
|
|
1060
|
+
let e = Xe(a);
|
|
1040
1061
|
e && (this.cdnHost = e);
|
|
1041
1062
|
}
|
|
1042
1063
|
}), f.on(e.Events.LEVEL_LOADED, (e, t) => {
|
|
@@ -1049,7 +1070,7 @@ var V = class {
|
|
|
1049
1070
|
}));
|
|
1050
1071
|
}), f.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.endWatchdog && clearInterval(this.endWatchdog), this.endWatchdog = setInterval(() => {
|
|
1051
1072
|
this.checkEndStale(), this.checkStarvation();
|
|
1052
|
-
},
|
|
1073
|
+
}, Re), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
|
|
1053
1074
|
this.renditionSwitches += 1;
|
|
1054
1075
|
let n = this.lastLevelIndex >= 0 ? f.levels[this.lastLevelIndex] : void 0, r = f.levels[t.level];
|
|
1055
1076
|
n && r && r.bitrate !== n.bitrate && (r.bitrate > n.bitrate ? this.upshifts += 1 : this.downshifts += 1), this.lastLevelIndex = t.level, l({
|
|
@@ -1113,7 +1134,7 @@ var V = class {
|
|
|
1113
1134
|
return;
|
|
1114
1135
|
}
|
|
1115
1136
|
this.capTimer && clearTimeout(this.capTimer);
|
|
1116
|
-
let t = e > this.sizeCapLevel ?
|
|
1137
|
+
let t = e > this.sizeCapLevel ? We : Ge;
|
|
1117
1138
|
this.capTimer = setTimeout(() => {
|
|
1118
1139
|
this.capTimer = null, this.computeSizeCapTarget() === e ? this.applySizeCap() : this.onMediaResize();
|
|
1119
1140
|
}, t);
|
|
@@ -1160,7 +1181,7 @@ var V = class {
|
|
|
1160
1181
|
e.widevine && (this.licenseUrl = e.widevine);
|
|
1161
1182
|
}
|
|
1162
1183
|
getStats() {
|
|
1163
|
-
let e = this.hls, t =
|
|
1184
|
+
let e = this.hls, t = R(this.deps.video), n = {
|
|
1164
1185
|
droppedFrames: t.dropped,
|
|
1165
1186
|
decodedFrames: t.decoded,
|
|
1166
1187
|
lowLatency: this.detectedLowLatency,
|
|
@@ -1202,7 +1223,7 @@ var V = class {
|
|
|
1202
1223
|
return;
|
|
1203
1224
|
}
|
|
1204
1225
|
let e = Date.now() - this.lastAdvanceMs;
|
|
1205
|
-
!this.chaseSuspended && e >
|
|
1226
|
+
!this.chaseSuspended && e > H && this.suspendChasing(), e > Le && this.emitEnded("stalled");
|
|
1206
1227
|
}
|
|
1207
1228
|
checkStarvation() {
|
|
1208
1229
|
let e = this.hls, { video: t } = this.deps;
|
|
@@ -1211,17 +1232,17 @@ var V = class {
|
|
|
1211
1232
|
this.starvedTicks = 0;
|
|
1212
1233
|
return;
|
|
1213
1234
|
}
|
|
1214
|
-
let n = this.lastAdvanceMs !== 0 && Date.now() - this.lastAdvanceMs <
|
|
1235
|
+
let n = this.lastAdvanceMs !== 0 && Date.now() - this.lastAdvanceMs < H, r = this.lastFragLoadingMs !== 0 && Date.now() - this.lastFragLoadingMs > ze;
|
|
1215
1236
|
if (!(t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused && n && r)) {
|
|
1216
1237
|
this.starvedTicks = 0;
|
|
1217
1238
|
return;
|
|
1218
1239
|
}
|
|
1219
|
-
if (this.kickPending && (this.kickTicks += 1), !(++this.starvedTicks <
|
|
1220
|
-
if (this.starvedTicks = 0, this.kickPending && this.kickTicks >=
|
|
1240
|
+
if (this.kickPending && (this.kickTicks += 1), !(++this.starvedTicks < Be)) {
|
|
1241
|
+
if (this.starvedTicks = 0, this.kickPending && this.kickTicks >= He && (this.kickPending = !1, this.kickTicks = 0, this.starvationRebuilds < Ue)) {
|
|
1221
1242
|
this.starvationRebuilds += 1, console.warn("havik-player: restarting the load did not resume video (the wedge is in hls.js state that only a rebuild clears) — rebuilding the player"), this.rebuildInstance("starvation rebuild");
|
|
1222
1243
|
return;
|
|
1223
1244
|
}
|
|
1224
|
-
if (!(this.starvationKicks >=
|
|
1245
|
+
if (!(this.starvationKicks >= Ve)) {
|
|
1225
1246
|
this.starvationKicks += 1, this.kickPending = !0, this.kickTicks = 0, console.warn("havik-player: video starved while the live playlist advances (wedged hls.js fragment pipeline) — restarting the load");
|
|
1226
1247
|
try {
|
|
1227
1248
|
e.stopLoad(), e.startLoad(-1);
|
|
@@ -1271,7 +1292,7 @@ var V = class {
|
|
|
1271
1292
|
scheduleLicenseRetry(t) {
|
|
1272
1293
|
if (t.details !== e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED && t.details !== e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED) return !1;
|
|
1273
1294
|
let n = t.response?.code ?? 0;
|
|
1274
|
-
if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >=
|
|
1295
|
+
if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Fe) return !1;
|
|
1275
1296
|
this.licenseRecoveries += 1;
|
|
1276
1297
|
let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
|
|
1277
1298
|
return this.licenseRetryTimer = setTimeout(() => {
|
|
@@ -1303,7 +1324,7 @@ var V = class {
|
|
|
1303
1324
|
return;
|
|
1304
1325
|
}
|
|
1305
1326
|
if (t.type === e.ErrorTypes.NETWORK_ERROR) {
|
|
1306
|
-
if (this.networkRecoveries <
|
|
1327
|
+
if (this.networkRecoveries < Pe) {
|
|
1307
1328
|
this.networkRecoveries += 1;
|
|
1308
1329
|
let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
|
|
1309
1330
|
this.networkRetryTimer = setTimeout(() => {
|
|
@@ -1320,7 +1341,7 @@ var V = class {
|
|
|
1320
1341
|
return;
|
|
1321
1342
|
}
|
|
1322
1343
|
if (t.type === e.ErrorTypes.MEDIA_ERROR) {
|
|
1323
|
-
if (this.mediaRecoveries <
|
|
1344
|
+
if (this.mediaRecoveries < Ne) {
|
|
1324
1345
|
this.mediaRecoveries += 1, n.recoverMediaError();
|
|
1325
1346
|
return;
|
|
1326
1347
|
}
|
|
@@ -1344,19 +1365,19 @@ var V = class {
|
|
|
1344
1365
|
let r = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
|
|
1345
1366
|
return new n(t === "DRM" ? i(r) : r === 401 ? "UNAUTHORIZED" : r === 403 ? "FORBIDDEN" : "INTERNAL", r, `${t} error: ${a}${o ? ` (${o})` : ""}`);
|
|
1346
1367
|
}
|
|
1347
|
-
},
|
|
1368
|
+
}, $e = 15e3, et = 100, W = 1, tt = 1e3, G = 3e4, nt = {
|
|
1348
1369
|
inline: "PRESENTATION_INLINE",
|
|
1349
1370
|
fullscreen: "PRESENTATION_FULLSCREEN",
|
|
1350
1371
|
pip: "PRESENTATION_PIP"
|
|
1351
|
-
},
|
|
1372
|
+
}, rt = {
|
|
1352
1373
|
widevine: "KEY_SYSTEM_WIDEVINE",
|
|
1353
1374
|
fairplay: "KEY_SYSTEM_FAIRPLAY",
|
|
1354
1375
|
none: "KEY_SYSTEM_NONE"
|
|
1355
1376
|
};
|
|
1356
|
-
function
|
|
1377
|
+
function K(e, t) {
|
|
1357
1378
|
return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
|
|
1358
1379
|
}
|
|
1359
|
-
var
|
|
1380
|
+
var it = /* @__PURE__ */ new Set([
|
|
1360
1381
|
"INVALID_URN",
|
|
1361
1382
|
"NOT_FOUND",
|
|
1362
1383
|
"GONE",
|
|
@@ -1366,7 +1387,7 @@ var rt = /* @__PURE__ */ new Set([
|
|
|
1366
1387
|
"FORBIDDEN",
|
|
1367
1388
|
"RATE_LIMITED",
|
|
1368
1389
|
"TIMEOUT"
|
|
1369
|
-
]),
|
|
1390
|
+
]), q = {
|
|
1370
1391
|
idle: "PLAYER_STATE_IDLE",
|
|
1371
1392
|
waiting: "PLAYER_STATE_WAITING",
|
|
1372
1393
|
loading: "PLAYER_STATE_LOADING",
|
|
@@ -1376,7 +1397,7 @@ var rt = /* @__PURE__ */ new Set([
|
|
|
1376
1397
|
ended: "PLAYER_STATE_ENDED",
|
|
1377
1398
|
error: "PLAYER_STATE_ERROR"
|
|
1378
1399
|
};
|
|
1379
|
-
function
|
|
1400
|
+
function at(e) {
|
|
1380
1401
|
try {
|
|
1381
1402
|
let t = new URL(e), [n, ...r] = t.hostname.split(".");
|
|
1382
1403
|
return !n || r.length === 0 || n !== "feed" && !n.startsWith("feed-") ? void 0 : (t.hostname = [n.replace(/^feed/, "beacons"), ...r].join("."), t.pathname = "", t.search = "", t.origin);
|
|
@@ -1384,7 +1405,7 @@ function it(e) {
|
|
|
1384
1405
|
return;
|
|
1385
1406
|
}
|
|
1386
1407
|
}
|
|
1387
|
-
var
|
|
1408
|
+
var ot = class {
|
|
1388
1409
|
opts;
|
|
1389
1410
|
url;
|
|
1390
1411
|
now;
|
|
@@ -1451,7 +1472,7 @@ var at = class {
|
|
|
1451
1472
|
constructor(e) {
|
|
1452
1473
|
this.opts = {
|
|
1453
1474
|
...e,
|
|
1454
|
-
heartbeatMs: e.heartbeatMs ??
|
|
1475
|
+
heartbeatMs: e.heartbeatMs ?? $e
|
|
1455
1476
|
}, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`, this.now = e.now ?? (() => Date.now()), this.bornMs = this.now(), this.presentationSinceMs = this.bornMs;
|
|
1456
1477
|
}
|
|
1457
1478
|
start() {
|
|
@@ -1461,16 +1482,16 @@ var at = class {
|
|
|
1461
1482
|
sdkVersion: this.opts.sdkVersion,
|
|
1462
1483
|
...this.opts.environment
|
|
1463
1484
|
};
|
|
1464
|
-
this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(),
|
|
1485
|
+
this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), tt), this.timer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs), typeof window < "u" && window.addEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.addEventListener("visibilitychange", this.onVisibility), this.onVisibility());
|
|
1465
1486
|
}
|
|
1466
1487
|
noteStateChange(e, t, n, r) {
|
|
1467
1488
|
if (this.endedFlag || e === t) return;
|
|
1468
1489
|
let i = this.now();
|
|
1469
|
-
t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(
|
|
1490
|
+
t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(W, i - this.bornMs)), this.firstAudioMs === 0 && !this.isMuted && (this.firstAudioMs = Math.max(W, i - this.bornMs)), this.isMuted && this.mutedSinceMs === null && (this.mutedSinceMs = i)) : this.playingSinceMs !== null && (this.playingTotalMs += i - this.playingSinceMs, this.playingSinceMs = null, this.mutedSinceMs !== null && (this.mutedTotalMs += i - this.mutedSinceMs, this.mutedSinceMs = null)), this.isPlaying = t === "playing", this.syncActive(), t === "waiting" ? this.waitingSinceMs = i : this.waitingSinceMs !== null && (this.waitingTotalMs += i - this.waitingSinceMs, this.waitingSinceMs = null), t === "buffering" && e === "playing" ? (this.isHidden || (this.rebufferCount += 1), this.isBuffering = !0, this.lastStallMs = i, this.timeToFirstStallMs === 0 && (this.timeToFirstStallMs = Math.max(W, Math.round(this.playingTotalMs)))) : e === "buffering" && (this.isBuffering = !1), this.syncBuffering(), t === "paused" && (this.pauseCount += 1);
|
|
1470
1491
|
let a = {
|
|
1471
|
-
fromState:
|
|
1472
|
-
toState:
|
|
1473
|
-
presentation:
|
|
1492
|
+
fromState: q[e] ?? "PLAYER_STATE_UNSPECIFIED",
|
|
1493
|
+
toState: q[t] ?? "PLAYER_STATE_UNSPECIFIED",
|
|
1494
|
+
presentation: nt[this.presentation]
|
|
1474
1495
|
}, o = this.causeOf(e, t, i);
|
|
1475
1496
|
o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
|
|
1476
1497
|
}
|
|
@@ -1509,16 +1530,16 @@ var at = class {
|
|
|
1509
1530
|
}
|
|
1510
1531
|
this.muteChanges += 1;
|
|
1511
1532
|
let r = this.now();
|
|
1512
|
-
n ? this.isPlaying && this.mutedSinceMs === null && (this.mutedSinceMs = r) : (this.mutedSinceMs !== null && (this.mutedTotalMs += r - this.mutedSinceMs, this.mutedSinceMs = null), this.firstAudioMs === 0 && this.isPlaying && (this.firstAudioMs = Math.max(
|
|
1533
|
+
n ? this.isPlaying && this.mutedSinceMs === null && (this.mutedSinceMs = r) : (this.mutedSinceMs !== null && (this.mutedTotalMs += r - this.mutedSinceMs, this.mutedSinceMs = null), this.firstAudioMs === 0 && this.isPlaying && (this.firstAudioMs = Math.max(W, r - this.bornMs))), this.isMuted = n, this.syncActive();
|
|
1513
1534
|
}
|
|
1514
1535
|
notePresentation(e) {
|
|
1515
1536
|
this.endedFlag || e === this.presentation || (this.closePresentation(), this.presentation = e);
|
|
1516
1537
|
}
|
|
1517
1538
|
noteReady() {
|
|
1518
|
-
this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(
|
|
1539
|
+
this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(W, this.now() - this.bornMs));
|
|
1519
1540
|
}
|
|
1520
1541
|
noteFirstBytes() {
|
|
1521
|
-
this.sawFirstBytes = !0, this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(
|
|
1542
|
+
this.sawFirstBytes = !0, this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(W, this.now() - this.bornMs));
|
|
1522
1543
|
}
|
|
1523
1544
|
noteSeek() {
|
|
1524
1545
|
this.endedFlag || (this.seekCount += 1, this.lastSeekMs = this.now());
|
|
@@ -1537,7 +1558,7 @@ var at = class {
|
|
|
1537
1558
|
let r = n ?? "";
|
|
1538
1559
|
if (r !== "") return /^fragLoad/i.test(r) ? this.sawFirstBytes ? "ERROR_STAGE_SEGMENT" : "ERROR_STAGE_FIRST_SEGMENT" : /^(manifest|level|audioTrack|subtitleTrack)/i.test(r) ? "ERROR_STAGE_MANIFEST" : /key|licen[cs]e/i.test(r) ? "ERROR_STAGE_DRM" : /parsing|append|buffer|remux|decrypt|media/i.test(r) ? "ERROR_STAGE_DECODE" : "ERROR_STAGE_OTHER";
|
|
1539
1560
|
if (e === "DRM_CLIENT") return "ERROR_STAGE_DRM";
|
|
1540
|
-
if (
|
|
1561
|
+
if (it.has(e)) return "ERROR_STAGE_RESOLVE";
|
|
1541
1562
|
let i = t ?? "";
|
|
1542
1563
|
return /frag|segment/i.test(i) ? this.sawFirstBytes ? "ERROR_STAGE_SEGMENT" : "ERROR_STAGE_FIRST_SEGMENT" : /manifest|level|playlist/i.test(i) ? "ERROR_STAGE_MANIFEST" : /key|licen[cs]e/i.test(i) ? "ERROR_STAGE_DRM" : /buffer|decode|media|append/i.test(i) ? "ERROR_STAGE_DECODE" : "ERROR_STAGE_OTHER";
|
|
1543
1564
|
}
|
|
@@ -1563,7 +1584,7 @@ var at = class {
|
|
|
1563
1584
|
let s = e.droppedFrames ?? 0;
|
|
1564
1585
|
s > this.lastDropped && (i.droppedFramesDelta = s - this.lastDropped), this.lastDropped = s;
|
|
1565
1586
|
let c = e.renditionSwitches ?? 0;
|
|
1566
|
-
c > this.lastSwitches && (i.renditionSwitchesDelta = c - this.lastSwitches), this.lastSwitches = c, n > this.lastHbPlayingMs && (i.playingMsDelta = Math.round(n - this.lastHbPlayingMs)), this.lastHbPlayingMs = n, r > this.lastHbHiddenMs && (i.hiddenMsDelta = Math.round(r - this.lastHbHiddenMs)), this.lastHbHiddenMs = r, typeof e.latencySeconds == "number" && (i.latencyMs = Math.round(e.latencySeconds * 1e3)), typeof e.targetLatencySeconds == "number" && (i.targetLatencyMs = Math.round(e.targetLatencySeconds * 1e3)), typeof e.bandwidthKbps == "number" && (i.bandwidthEstKbps = e.bandwidthKbps), typeof e.levelHeight == "number" && (i.renditionHeight = e.levelHeight), typeof e.renditionBitrateKbps == "number" && (i.renditionBitrateKbps = e.renditionBitrateKbps), e.atLiveEdge === !0 && (i.atLiveEdge = !0), typeof e.bufferedMs == "number" && e.bufferedMs > 0 && (i.bufferedMs = Math.round(e.bufferedMs)), i.playerState =
|
|
1587
|
+
c > this.lastSwitches && (i.renditionSwitchesDelta = c - this.lastSwitches), this.lastSwitches = c, n > this.lastHbPlayingMs && (i.playingMsDelta = Math.round(n - this.lastHbPlayingMs)), this.lastHbPlayingMs = n, r > this.lastHbHiddenMs && (i.hiddenMsDelta = Math.round(r - this.lastHbHiddenMs)), this.lastHbHiddenMs = r, typeof e.latencySeconds == "number" && (i.latencyMs = Math.round(e.latencySeconds * 1e3)), typeof e.targetLatencySeconds == "number" && (i.targetLatencyMs = Math.round(e.targetLatencySeconds * 1e3)), typeof e.bandwidthKbps == "number" && (i.bandwidthEstKbps = e.bandwidthKbps), typeof e.levelHeight == "number" && (i.renditionHeight = e.levelHeight), typeof e.renditionBitrateKbps == "number" && (i.renditionBitrateKbps = e.renditionBitrateKbps), e.atLiveEdge === !0 && (i.atLiveEdge = !0), typeof e.bufferedMs == "number" && e.bufferedMs > 0 && (i.bufferedMs = Math.round(e.bufferedMs)), i.playerState = q[e.state] ?? "PLAYER_STATE_UNSPECIFIED";
|
|
1567
1588
|
let l = this.mutedTotalMs;
|
|
1568
1589
|
this.mutedSinceMs !== null && (l += t - this.mutedSinceMs), l > this.lastHbMutedMs && (i.mutedMsDelta = Math.round(l - this.lastHbMutedMs)), this.lastHbMutedMs = l;
|
|
1569
1590
|
let u = e.upshifts ?? 0;
|
|
@@ -1578,14 +1599,14 @@ var at = class {
|
|
|
1578
1599
|
if (this.lastRequestCount = m, h > 0) {
|
|
1579
1600
|
i.requestCountDelta = h;
|
|
1580
1601
|
let t = e.requestTimesMs ?? [], n = t.slice(Math.max(0, t.length - h)).sort((e, t) => e - t);
|
|
1581
|
-
n.length > 0 && (i.requestTimeP50Ms =
|
|
1602
|
+
n.length > 0 && (i.requestTimeP50Ms = K(n, 50), i.requestTimeP95Ms = K(n, 95));
|
|
1582
1603
|
}
|
|
1583
1604
|
typeof e.latencySeconds == "number" && typeof e.targetLatencySeconds == "number" && (i.driftMs = Math.round((e.latencySeconds - e.targetLatencySeconds) * 1e3)), typeof e.playbackRate == "number" && e.playbackRate > 0 && (i.playbackRate = e.playbackRate), e.qualityPinned === !0 && (i.qualityPinned = !0), e.cdnHost && (i.cdnHost = e.cdnHost), this.maybeEmitMediaInfo(e), this.enqueue({ heartbeat: i }), this.flush(), a > 0 && this.noteFirstBytes();
|
|
1584
1605
|
}
|
|
1585
1606
|
maybeEmitMediaInfo(e) {
|
|
1586
1607
|
if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
|
|
1587
1608
|
let t = {};
|
|
1588
|
-
e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem =
|
|
1609
|
+
e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = rt[e.keySystem]), e.drmSecurityLevel && (t.drmSecurityLevel = e.drmSecurityLevel), e.ladderHeights?.length && (t.ladderHeights = e.ladderHeights), e.ladderTopBitrateKbps && (t.ladderTopBitrateKbps = e.ladderTopBitrateKbps), e.lowLatency && (t.lowLatency = !0);
|
|
1589
1610
|
let n = JSON.stringify(t);
|
|
1590
1611
|
n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
|
|
1591
1612
|
}
|
|
@@ -1605,7 +1626,7 @@ var at = class {
|
|
|
1605
1626
|
let s = n?.positionMs ?? 0;
|
|
1606
1627
|
s > 0 && (r.exitPositionMs = Math.round(s)), this.activeTotalMs > 0 && (r.activeWatchTimeMs = Math.round(this.activeTotalMs)), this.mutedTotalMs > 0 && (r.mutedTimeMs = Math.round(this.mutedTotalMs));
|
|
1607
1628
|
let c = e !== "END_REASON_ENDED";
|
|
1608
|
-
c && this.lastStallMs > 0 && t - this.lastStallMs <=
|
|
1629
|
+
c && this.lastStallMs > 0 && t - this.lastStallMs <= G && (r.abandonAfterStallMs = Math.max(W, Math.round(t - this.lastStallMs))), c && this.lastErrorMs > 0 && t - this.lastErrorMs <= G && (r.abandonAfterErrorMs = Math.max(W, Math.round(t - this.lastErrorMs))), this.fullscreenTotalMs > 0 && (r.fullscreenTimeMs = Math.round(this.fullscreenTotalMs)), this.pipTotalMs > 0 && (r.pipTimeMs = Math.round(this.pipTotalMs)), this.volumeChanges > 0 && (r.volumeChanges = this.volumeChanges), this.muteChanges > 0 && (r.muteChanges = this.muteChanges);
|
|
1609
1630
|
let l = n?.decodedFrames ?? this.lastDecoded;
|
|
1610
1631
|
l > 0 && (r.decodedFramesTotal = l), this.startupTimeMs === 0 && (r.failedStage = this.lastErrorStage ?? (this.manifestLoadMs === 0 ? "ERROR_STAGE_MANIFEST" : this.firstFragLoadMs === 0 ? "ERROR_STAGE_FIRST_SEGMENT" : "ERROR_STAGE_OTHER")), this.pending.push(this.wrap({ sessionEnd: r })), this.flush(e === "END_REASON_PAGEHIDE");
|
|
1611
1632
|
}
|
|
@@ -1625,7 +1646,7 @@ var at = class {
|
|
|
1625
1646
|
};
|
|
1626
1647
|
}
|
|
1627
1648
|
enqueue(e) {
|
|
1628
|
-
for (this.pending.push(this.wrap(e)); this.pending.length >
|
|
1649
|
+
for (this.pending.push(this.wrap(e)); this.pending.length > et;) {
|
|
1629
1650
|
let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
|
|
1630
1651
|
this.pending.splice(e >= 0 ? e : 0, 1);
|
|
1631
1652
|
}
|
|
@@ -1650,7 +1671,7 @@ var at = class {
|
|
|
1650
1671
|
};
|
|
1651
1672
|
//#endregion
|
|
1652
1673
|
//#region src/core/environment.ts
|
|
1653
|
-
function
|
|
1674
|
+
function st() {
|
|
1654
1675
|
if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
|
|
1655
1676
|
let e;
|
|
1656
1677
|
try {
|
|
@@ -1675,14 +1696,14 @@ function ot() {
|
|
|
1675
1696
|
integration: "INTEGRATION_KIND_IFRAME"
|
|
1676
1697
|
};
|
|
1677
1698
|
}
|
|
1678
|
-
function
|
|
1699
|
+
function ct() {
|
|
1679
1700
|
if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
|
|
1680
1701
|
let e = navigator.userAgentData, t = e?.platform ?? "";
|
|
1681
1702
|
if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
|
|
1682
1703
|
let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
|
|
1683
1704
|
return e?.mobile ?? (typeof navigator.maxTouchPoints == "number" && navigator.maxTouchPoints > 1 && n > 0 && n < 900) ? n >= 600 ? "DEVICE_CLASS_TABLET" : "DEVICE_CLASS_PHONE" : e || n >= 900 || /mac|win|linux|chrome os/i.test(t) ? "DEVICE_CLASS_DESKTOP" : "DEVICE_CLASS_OTHER";
|
|
1684
1705
|
}
|
|
1685
|
-
function
|
|
1706
|
+
function lt() {
|
|
1686
1707
|
let e = typeof navigator < "u" ? navigator.connection : void 0;
|
|
1687
1708
|
if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
|
|
1688
1709
|
let t = { connectionType: e.type ? {
|
|
@@ -1692,17 +1713,17 @@ function ct() {
|
|
|
1692
1713
|
}[e.type] ?? "CONNECTION_TYPE_OTHER" : "CONNECTION_TYPE_UNKNOWN" };
|
|
1693
1714
|
return typeof e.downlink == "number" && e.downlink > 0 && (t.downlinkKbps = Math.round(e.downlink * 1e3)), e.saveData && (t.saveData = !0), t;
|
|
1694
1715
|
}
|
|
1695
|
-
function
|
|
1696
|
-
let t =
|
|
1697
|
-
deviceClass:
|
|
1716
|
+
function ut(e) {
|
|
1717
|
+
let t = st(), n = {
|
|
1718
|
+
deviceClass: ct(),
|
|
1698
1719
|
integration: t.integration,
|
|
1699
|
-
...
|
|
1720
|
+
...lt()
|
|
1700
1721
|
};
|
|
1701
1722
|
t.host && (n.host = t.host), typeof screen < "u" && screen.width > 0 && (n.screenWidth = screen.width, n.screenHeight = screen.height), typeof window < "u" && window.devicePixelRatio > 0 && (n.devicePixelRatio = Math.round(window.devicePixelRatio * 100) / 100);
|
|
1702
1723
|
let r = e.getBoundingClientRect?.();
|
|
1703
1724
|
return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
|
|
1704
1725
|
}
|
|
1705
|
-
async function
|
|
1726
|
+
async function dt() {
|
|
1706
1727
|
let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
|
|
1707
1728
|
if (!e?.getHighEntropyValues) return {};
|
|
1708
1729
|
try {
|
|
@@ -1714,7 +1735,7 @@ async function ut() {
|
|
|
1714
1735
|
}
|
|
1715
1736
|
//#endregion
|
|
1716
1737
|
//#region src/core/version.ts
|
|
1717
|
-
var
|
|
1738
|
+
var ft = "2.2.1", J = {
|
|
1718
1739
|
accent: "#E1B600",
|
|
1719
1740
|
accentText: "#1B1C23",
|
|
1720
1741
|
background: "#0e0f13",
|
|
@@ -1725,7 +1746,7 @@ var dt = "2.2.0", K = {
|
|
|
1725
1746
|
border: "rgba(255, 255, 255, 0.1)",
|
|
1726
1747
|
radius: "8px",
|
|
1727
1748
|
fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
|
|
1728
|
-
},
|
|
1749
|
+
}, Y = {
|
|
1729
1750
|
accent: "--havik-accent",
|
|
1730
1751
|
accentText: "--havik-accent-text",
|
|
1731
1752
|
background: "--havik-bg",
|
|
@@ -1737,25 +1758,25 @@ var dt = "2.2.0", K = {
|
|
|
1737
1758
|
radius: "--havik-radius",
|
|
1738
1759
|
fontFamily: "--havik-font"
|
|
1739
1760
|
};
|
|
1740
|
-
function
|
|
1761
|
+
function X(e, t) {
|
|
1741
1762
|
let n = {
|
|
1742
|
-
...
|
|
1763
|
+
...J,
|
|
1743
1764
|
...t
|
|
1744
1765
|
};
|
|
1745
|
-
for (let t of Object.keys(
|
|
1766
|
+
for (let t of Object.keys(Y)) e.style.setProperty(Y[t], n[t]);
|
|
1746
1767
|
n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
|
|
1747
1768
|
}
|
|
1748
1769
|
//#endregion
|
|
1749
1770
|
//#region src/ui/styles.ts
|
|
1750
|
-
var
|
|
1751
|
-
function
|
|
1752
|
-
if (e.getElementById(
|
|
1771
|
+
var Z = "havik-player-styles", pt = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n.havik-spinner{width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n";
|
|
1772
|
+
function mt(e = document) {
|
|
1773
|
+
if (e.getElementById(Z)) return;
|
|
1753
1774
|
let t = e.createElement("style");
|
|
1754
|
-
t.id =
|
|
1775
|
+
t.id = Z, t.textContent = pt, e.head.appendChild(t);
|
|
1755
1776
|
}
|
|
1756
1777
|
//#endregion
|
|
1757
1778
|
//#region src/ui/icons.ts
|
|
1758
|
-
var
|
|
1779
|
+
var ht = {
|
|
1759
1780
|
play: "<path d=\"M8 5v14l11-7z\"/>",
|
|
1760
1781
|
pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
|
|
1761
1782
|
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\"/>",
|
|
@@ -1768,21 +1789,21 @@ var mt = {
|
|
|
1768
1789
|
liveDot: "<circle cx=\"12\" cy=\"12\" r=\"6\"/>",
|
|
1769
1790
|
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\"/>"
|
|
1770
1791
|
};
|
|
1771
|
-
function
|
|
1772
|
-
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${
|
|
1792
|
+
function Q(e) {
|
|
1793
|
+
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${ht[e]}</svg>`;
|
|
1773
1794
|
}
|
|
1774
1795
|
//#endregion
|
|
1775
1796
|
//#region src/ui/controls.ts
|
|
1776
|
-
var
|
|
1797
|
+
var gt = 3e3, _t = /* @__PURE__ */ new Set([
|
|
1777
1798
|
"GONE",
|
|
1778
1799
|
"NOT_FOUND",
|
|
1779
1800
|
"INVALID_URN",
|
|
1780
1801
|
"UNAUTHORIZED"
|
|
1781
1802
|
]);
|
|
1782
|
-
function
|
|
1783
|
-
return new
|
|
1803
|
+
function vt(e) {
|
|
1804
|
+
return new yt(e);
|
|
1784
1805
|
}
|
|
1785
|
-
var
|
|
1806
|
+
var yt = class {
|
|
1786
1807
|
root;
|
|
1787
1808
|
video;
|
|
1788
1809
|
player;
|
|
@@ -1824,7 +1845,7 @@ var vt = class {
|
|
|
1824
1845
|
scrubbing = !1;
|
|
1825
1846
|
offFns = [];
|
|
1826
1847
|
constructor(e) {
|
|
1827
|
-
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ??
|
|
1848
|
+
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? gt, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, mt(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");
|
|
1828
1849
|
let t = (e, t, n) => {
|
|
1829
1850
|
let r = this.doc.createElement(e);
|
|
1830
1851
|
if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
|
|
@@ -1835,12 +1856,12 @@ var vt = class {
|
|
|
1835
1856
|
"aria-label": r,
|
|
1836
1857
|
title: r
|
|
1837
1858
|
});
|
|
1838
|
-
return i.innerHTML =
|
|
1859
|
+
return i.innerHTML = Q(n), i;
|
|
1839
1860
|
};
|
|
1840
1861
|
this.overlay = t("div", "havik-overlay"), this.center = t("button", "havik-center", {
|
|
1841
1862
|
type: "button",
|
|
1842
1863
|
"aria-label": "Play"
|
|
1843
|
-
}), this.center.innerHTML =
|
|
1864
|
+
}), this.center.innerHTML = Q("play"), this.spinner = t("div", "havik-spinner"), this.spinner.style.display = "none", this.overlay.append(this.center, this.spinner), this.gradient = t("div", "havik-gradient"), this.bar = t("div", "havik-bar"), this.seek = t("div", "havik-seek", {
|
|
1844
1865
|
role: "slider",
|
|
1845
1866
|
"aria-label": "Seek",
|
|
1846
1867
|
"aria-valuemin": "0",
|
|
@@ -1858,7 +1879,7 @@ var vt = class {
|
|
|
1858
1879
|
"aria-label": "Volume"
|
|
1859
1880
|
});
|
|
1860
1881
|
let a = t("div", "havik-vol");
|
|
1861
|
-
a.append(this.muteBtn, this.volSlider), this.live = t("div", "havik-live"), this.live.innerHTML =
|
|
1882
|
+
a.append(this.muteBtn, this.volSlider), this.live = t("div", "havik-live"), this.live.innerHTML = Q("liveDot");
|
|
1862
1883
|
let o = this.doc.createElement("span");
|
|
1863
1884
|
o.textContent = "LIVE", this.liveGo = t("button", "havik-live__go", { type: "button" }), this.liveGo.textContent = "Go live", this.live.append(o, this.liveGo), this.time = t("div", "havik-time"), this.time.textContent = "0:00", this.time.style.display = "none";
|
|
1864
1885
|
let s = t("div", "havik-spacer");
|
|
@@ -1912,12 +1933,12 @@ var vt = class {
|
|
|
1912
1933
|
}
|
|
1913
1934
|
if (e === "preplay") {
|
|
1914
1935
|
let e = this.doc.createElement("button");
|
|
1915
|
-
e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML =
|
|
1936
|
+
e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = Q("play"), e.addEventListener("click", () => void this.player.play()), n.append(e);
|
|
1916
1937
|
} else {
|
|
1917
1938
|
let r = this.doc.createElement("div");
|
|
1918
|
-
if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !
|
|
1939
|
+
if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !_t.has(this.lastError.code)) {
|
|
1919
1940
|
let e = this.doc.createElement("button");
|
|
1920
|
-
e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${
|
|
1941
|
+
e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${Q("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), n.append(e);
|
|
1921
1942
|
}
|
|
1922
1943
|
}
|
|
1923
1944
|
this.root.append(n), this.statusBox = n, this.show();
|
|
@@ -1956,7 +1977,7 @@ var vt = class {
|
|
|
1956
1977
|
let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
|
|
1957
1978
|
this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
|
|
1958
1979
|
}
|
|
1959
|
-
!this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${
|
|
1980
|
+
!this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${bt(this.video.currentTime)} / ${bt(this.video.duration)}`);
|
|
1960
1981
|
}
|
|
1961
1982
|
syncLive() {
|
|
1962
1983
|
let e = this.player.getStats(), t = e.isLive !== !1;
|
|
@@ -2006,17 +2027,17 @@ var vt = class {
|
|
|
2006
2027
|
renderQualityMenu() {
|
|
2007
2028
|
let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
|
|
2008
2029
|
n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
|
|
2009
|
-
for (let n of e) this.menu.append(this.menuItem(
|
|
2030
|
+
for (let n of e) this.menu.append(this.menuItem(xt(n), t === n.index, () => this.player.setQuality(n.index)));
|
|
2010
2031
|
}
|
|
2011
2032
|
renderAudioMenu() {
|
|
2012
2033
|
let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
|
|
2013
2034
|
t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
|
|
2014
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
2035
|
+
for (let t of e) this.menu.append(this.menuItem(St(t), t.default, () => this.player.setAudioTrack(t.id)));
|
|
2015
2036
|
}
|
|
2016
2037
|
renderCcMenu() {
|
|
2017
2038
|
let e = this.player.getTextTracks(), t = this.doc.createElement("div");
|
|
2018
2039
|
t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
|
|
2019
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
2040
|
+
for (let t of e) this.menu.append(this.menuItem(Ct(t), !1, () => this.player.setTextTrack(t.id)));
|
|
2020
2041
|
}
|
|
2021
2042
|
onTracksChanged() {
|
|
2022
2043
|
this.openMenu && this.renderMenu(this.openMenu);
|
|
@@ -2099,7 +2120,7 @@ var vt = class {
|
|
|
2099
2120
|
}
|
|
2100
2121
|
}
|
|
2101
2122
|
setIcon(e, t) {
|
|
2102
|
-
e.innerHTML =
|
|
2123
|
+
e.innerHTML = Q(t);
|
|
2103
2124
|
}
|
|
2104
2125
|
destroy() {
|
|
2105
2126
|
this.hideTimer && clearTimeout(this.hideTimer);
|
|
@@ -2116,32 +2137,32 @@ var vt = class {
|
|
|
2116
2137
|
this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
|
|
2117
2138
|
}
|
|
2118
2139
|
};
|
|
2119
|
-
function
|
|
2140
|
+
function bt(e) {
|
|
2120
2141
|
(!Number.isFinite(e) || e < 0) && (e = 0);
|
|
2121
2142
|
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");
|
|
2122
2143
|
return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
|
|
2123
2144
|
}
|
|
2124
|
-
function
|
|
2145
|
+
function xt(e) {
|
|
2125
2146
|
return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
|
|
2126
2147
|
}
|
|
2127
|
-
function
|
|
2148
|
+
function St(e) {
|
|
2128
2149
|
return e.name || e.lang || `Track ${e.id}`;
|
|
2129
2150
|
}
|
|
2130
|
-
function
|
|
2151
|
+
function Ct(e) {
|
|
2131
2152
|
return e.name || e.lang || `Track ${e.id}`;
|
|
2132
2153
|
}
|
|
2133
2154
|
//#endregion
|
|
2134
2155
|
//#region src/managed/index.ts
|
|
2135
|
-
var
|
|
2136
|
-
async function
|
|
2156
|
+
var wt = 6e3, Tt = 45e3, Et = 2e3, Dt = 3, Ot = 2e3, kt = 48e4;
|
|
2157
|
+
async function At(e) {
|
|
2137
2158
|
if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
|
|
2138
2159
|
if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: baseUrl is required");
|
|
2139
2160
|
if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
|
|
2140
2161
|
if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
|
|
2141
|
-
let t = new
|
|
2162
|
+
let t = new jt(e);
|
|
2142
2163
|
return await t.start(), t;
|
|
2143
2164
|
}
|
|
2144
|
-
var
|
|
2165
|
+
var jt = class {
|
|
2145
2166
|
opts;
|
|
2146
2167
|
abort = new AbortController();
|
|
2147
2168
|
listeners = {};
|
|
@@ -2228,7 +2249,7 @@ var Ot = class {
|
|
|
2228
2249
|
setTimeout(() => {
|
|
2229
2250
|
a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
|
|
2230
2251
|
phase: i ? "unavailable" : "tooEarly",
|
|
2231
|
-
retryInMs:
|
|
2252
|
+
retryInMs: Tt,
|
|
2232
2253
|
attempt: 1,
|
|
2233
2254
|
elapsedMs: 0,
|
|
2234
2255
|
...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
|
|
@@ -2267,13 +2288,13 @@ var Ot = class {
|
|
|
2267
2288
|
!a && !p() && h();
|
|
2268
2289
|
}, e);
|
|
2269
2290
|
};
|
|
2270
|
-
_(
|
|
2291
|
+
_(wt), this.armingSub = E({
|
|
2271
2292
|
eventsBaseUrl: t,
|
|
2272
2293
|
matchUrn: this.opts.matchUrn,
|
|
2273
2294
|
credential: this.opts.credential,
|
|
2274
|
-
onAlive: () => _(
|
|
2295
|
+
onAlive: () => _(Tt),
|
|
2275
2296
|
onState: (e) => {
|
|
2276
|
-
a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() *
|
|
2297
|
+
a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Et);
|
|
2277
2298
|
},
|
|
2278
2299
|
onError: () => h()
|
|
2279
2300
|
});
|
|
@@ -2296,7 +2317,7 @@ var Ot = class {
|
|
|
2296
2317
|
let n = await s(this.opts.credential);
|
|
2297
2318
|
if (this.destroyed || t !== this.loadGen) return;
|
|
2298
2319
|
this.setState("loading");
|
|
2299
|
-
let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a =
|
|
2320
|
+
let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = ke(this.opts.video, (e) => {
|
|
2300
2321
|
this._state !== "ended" && this._state !== "error" && this.setState(e);
|
|
2301
2322
|
});
|
|
2302
2323
|
if (await i.load(), this.destroyed || t !== this.loadGen) {
|
|
@@ -2315,17 +2336,17 @@ var Ot = class {
|
|
|
2315
2336
|
if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
|
|
2316
2337
|
let r = e.analytics?.sid;
|
|
2317
2338
|
if (!r) return;
|
|
2318
|
-
let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ??
|
|
2339
|
+
let i = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? at(this.opts.baseUrl);
|
|
2319
2340
|
if (!i) return;
|
|
2320
|
-
let a = this.opts.video, o = new
|
|
2341
|
+
let a = this.opts.video, o = new ot({
|
|
2321
2342
|
endpoint: i,
|
|
2322
2343
|
sid: r,
|
|
2323
2344
|
apiKey: n,
|
|
2324
2345
|
player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
|
|
2325
|
-
sdkVersion:
|
|
2326
|
-
environment:
|
|
2346
|
+
sdkVersion: ft,
|
|
2347
|
+
environment: ut(a),
|
|
2327
2348
|
sample: () => {
|
|
2328
|
-
let e = this.engine?.getStats() ??
|
|
2349
|
+
let e = this.engine?.getStats() ?? L, t = a.buffered.length ? Math.max(0, a.buffered.end(a.buffered.length - 1) - a.currentTime) : 0;
|
|
2329
2350
|
return {
|
|
2330
2351
|
...e,
|
|
2331
2352
|
bufferedMs: Math.round(t * 1e3),
|
|
@@ -2339,7 +2360,7 @@ var Ot = class {
|
|
|
2339
2360
|
let s = () => o.noteSeek(), c = () => o.noteVolume(a.muted, a.volume);
|
|
2340
2361
|
a.addEventListener("seeking", s), a.addEventListener("volumechange", c), this.detachMediaListeners = () => {
|
|
2341
2362
|
a.removeEventListener("seeking", s), a.removeEventListener("volumechange", c);
|
|
2342
|
-
}, o.start(), o.seedVolume(a.muted, a.volume), o.notePresentation(this.currentPresentation()),
|
|
2363
|
+
}, o.start(), o.seedVolume(a.muted, a.volume), o.notePresentation(this.currentPresentation()), dt().then((e) => o.noteEnvironment(e)), this.opts.autoplay === !1 && o.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
|
|
2343
2364
|
}
|
|
2344
2365
|
currentPresentation() {
|
|
2345
2366
|
let e = this.opts.video;
|
|
@@ -2363,11 +2384,11 @@ var Ot = class {
|
|
|
2363
2384
|
hlsConfig: i,
|
|
2364
2385
|
emit: (e) => this.onEngineEvent(e)
|
|
2365
2386
|
}, o = this.opts.engine ?? "auto";
|
|
2366
|
-
if (o === "native") return new
|
|
2367
|
-
if (o === "hls") return new
|
|
2368
|
-
if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl &&
|
|
2369
|
-
if (
|
|
2370
|
-
if (
|
|
2387
|
+
if (o === "native") return new V(a);
|
|
2388
|
+
if (o === "hls") return new U(a);
|
|
2389
|
+
if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && z(this.opts.video) && B()) return new V(a);
|
|
2390
|
+
if (Me()) return new U(a);
|
|
2391
|
+
if (z(this.opts.video)) return new V(a);
|
|
2371
2392
|
throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
|
|
2372
2393
|
}
|
|
2373
2394
|
onEngineEvent(e) {
|
|
@@ -2410,7 +2431,7 @@ var Ot = class {
|
|
|
2410
2431
|
r = 0, i = !1;
|
|
2411
2432
|
},
|
|
2412
2433
|
onError: () => {
|
|
2413
|
-
this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >=
|
|
2434
|
+
this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= Dt && (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.`))));
|
|
2414
2435
|
}
|
|
2415
2436
|
});
|
|
2416
2437
|
}
|
|
@@ -2432,7 +2453,7 @@ var Ot = class {
|
|
|
2432
2453
|
scheduleRejoin(e) {
|
|
2433
2454
|
!this.rejoinEnabled() || this.rejoinTimer || (this.rejoinTimer = setTimeout(() => {
|
|
2434
2455
|
this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
|
|
2435
|
-
}, Math.random() *
|
|
2456
|
+
}, Math.random() * Et));
|
|
2436
2457
|
}
|
|
2437
2458
|
cancelRejoin() {
|
|
2438
2459
|
this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
|
|
@@ -2456,7 +2477,7 @@ var Ot = class {
|
|
|
2456
2477
|
}
|
|
2457
2478
|
t.controls = !1;
|
|
2458
2479
|
let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
|
|
2459
|
-
r && r.insertBefore(n, t), n.appendChild(t),
|
|
2480
|
+
r && r.insertBefore(n, t), n.appendChild(t), X(n, this.opts.theme), this.container = n, this.controlBar = vt({
|
|
2460
2481
|
root: n,
|
|
2461
2482
|
video: t,
|
|
2462
2483
|
player: this,
|
|
@@ -2486,7 +2507,7 @@ var Ot = class {
|
|
|
2486
2507
|
}
|
|
2487
2508
|
startStats() {
|
|
2488
2509
|
this.stopStats();
|
|
2489
|
-
let e = Math.max(500, this.opts.statsIntervalMs ??
|
|
2510
|
+
let e = Math.max(500, this.opts.statsIntervalMs ?? Ot);
|
|
2490
2511
|
this.statsTimer = setInterval(() => {
|
|
2491
2512
|
if (!this.engine) return;
|
|
2492
2513
|
let e = this.engine.getStats();
|
|
@@ -2498,7 +2519,7 @@ var Ot = class {
|
|
|
2498
2519
|
}
|
|
2499
2520
|
scheduleLicenseRefresh() {
|
|
2500
2521
|
this.stopRefresh();
|
|
2501
|
-
let e = this.opts.licenseRefreshMs ??
|
|
2522
|
+
let e = this.opts.licenseRefreshMs ?? kt;
|
|
2502
2523
|
!e || !this._descriptor?.drmEnabled || (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
|
|
2503
2524
|
}
|
|
2504
2525
|
stopRefresh() {
|
|
@@ -2540,7 +2561,7 @@ var Ot = class {
|
|
|
2540
2561
|
this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
|
|
2541
2562
|
}
|
|
2542
2563
|
getStats() {
|
|
2543
|
-
return this.engine?.getStats() ??
|
|
2564
|
+
return this.engine?.getStats() ?? L;
|
|
2544
2565
|
}
|
|
2545
2566
|
getQualityLevels() {
|
|
2546
2567
|
return this.engine?.getQualityLevels() ?? [];
|
|
@@ -2650,7 +2671,7 @@ function $(e) {
|
|
|
2650
2671
|
}
|
|
2651
2672
|
//#endregion
|
|
2652
2673
|
//#region src/embed/host.ts
|
|
2653
|
-
function
|
|
2674
|
+
function Mt(e) {
|
|
2654
2675
|
if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
|
|
2655
2676
|
if (!e.src) throw Error("mountEmbed: opts.src is required");
|
|
2656
2677
|
if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
|
|
@@ -2709,8 +2730,8 @@ function kt(e) {
|
|
|
2709
2730
|
}
|
|
2710
2731
|
//#endregion
|
|
2711
2732
|
//#region src/embed/page.ts
|
|
2712
|
-
var
|
|
2713
|
-
function
|
|
2733
|
+
var Nt = /^[A-Za-z0-9:_.-]{1,128}$/;
|
|
2734
|
+
function Pt() {
|
|
2714
2735
|
let e = window.HAVIK_EMBED_CONFIG;
|
|
2715
2736
|
if (e) return {
|
|
2716
2737
|
baseUrl: e.baseUrl ?? "",
|
|
@@ -2732,9 +2753,9 @@ function jt() {
|
|
|
2732
2753
|
waitForLive: !0
|
|
2733
2754
|
};
|
|
2734
2755
|
}
|
|
2735
|
-
async function
|
|
2736
|
-
let e =
|
|
2737
|
-
|
|
2756
|
+
async function Ft() {
|
|
2757
|
+
let e = Pt();
|
|
2758
|
+
Lt();
|
|
2738
2759
|
let t = document.createElement("video");
|
|
2739
2760
|
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);
|
|
2740
2761
|
let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
|
|
@@ -2789,12 +2810,12 @@ async function Mt() {
|
|
|
2789
2810
|
code: e.code,
|
|
2790
2811
|
message: e.message
|
|
2791
2812
|
})), e.on("autoplayblocked", () => {
|
|
2792
|
-
i({ type: "oddin:autoplayblocked" }),
|
|
2813
|
+
i({ type: "oddin:autoplayblocked" }), It(t, e);
|
|
2793
2814
|
});
|
|
2794
2815
|
}, s = async (r) => {
|
|
2795
2816
|
a?.destroy(), a = null;
|
|
2796
2817
|
try {
|
|
2797
|
-
let n = await
|
|
2818
|
+
let n = await At({
|
|
2798
2819
|
video: t,
|
|
2799
2820
|
baseUrl: e.baseUrl,
|
|
2800
2821
|
matchUrn: r,
|
|
@@ -2855,7 +2876,7 @@ async function Mt() {
|
|
|
2855
2876
|
a?.retry();
|
|
2856
2877
|
break;
|
|
2857
2878
|
case "oddin:load":
|
|
2858
|
-
typeof e.matchUrn == "string" &&
|
|
2879
|
+
typeof e.matchUrn == "string" && Nt.test(e.matchUrn) && s(e.matchUrn);
|
|
2859
2880
|
break;
|
|
2860
2881
|
case "oddin:destroy": c();
|
|
2861
2882
|
}
|
|
@@ -2867,18 +2888,18 @@ async function Mt() {
|
|
|
2867
2888
|
protocol: 1
|
|
2868
2889
|
});
|
|
2869
2890
|
}
|
|
2870
|
-
function
|
|
2891
|
+
function It(e, t) {
|
|
2871
2892
|
if (document.getElementById("havik-tap")) return;
|
|
2872
2893
|
let n = document.createElement("button");
|
|
2873
2894
|
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", () => {
|
|
2874
2895
|
n.remove(), t.play();
|
|
2875
2896
|
}), document.body.appendChild(n);
|
|
2876
2897
|
}
|
|
2877
|
-
function
|
|
2898
|
+
function Lt() {
|
|
2878
2899
|
let e = document.createElement("style");
|
|
2879
2900
|
e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
|
|
2880
2901
|
}
|
|
2881
2902
|
//#endregion
|
|
2882
|
-
export {
|
|
2903
|
+
export { J as ODDIN_THEME, n as PlaybackError, X as applyTheme, Ft as bootEmbed, ge as clearDeviceId, At as createPlayer, D as deriveEventsBaseUrl, Ee as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ue as fetchCatalog, fe as fetchTournament, me as flattenMatches, A as getDeviceId, Oe as isLowLatencyManifest, $ as isOddinMessage, ve as licenseRequestHeaders, Mt as mountEmbed, v as pollToLive, b as resolveStream, E as subscribeLiveState, C as videoFullscreenActive, he as watchStatus };
|
|
2883
2904
|
|
|
2884
2905
|
//# sourceMappingURL=index.js.map
|