@oddin-gg/havik-player 1.8.4 → 1.9.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/dist/havik-player.global.js +13 -13
- 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 +35 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +333 -106
- 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
|
@@ -171,7 +171,7 @@ async function v(e) {
|
|
|
171
171
|
signal: e.signal
|
|
172
172
|
});
|
|
173
173
|
if (!n.ok) throw await ae(n);
|
|
174
|
-
return
|
|
174
|
+
return se(await n.json(), e.matchUrn);
|
|
175
175
|
}
|
|
176
176
|
function y(e) {
|
|
177
177
|
let t = () => v(e);
|
|
@@ -201,7 +201,8 @@ var oe = /* @__PURE__ */ new Set([
|
|
|
201
201
|
"drm",
|
|
202
202
|
"serverTime",
|
|
203
203
|
"liveStartsAt",
|
|
204
|
-
"expiresAt"
|
|
204
|
+
"expiresAt",
|
|
205
|
+
"analytics"
|
|
205
206
|
]);
|
|
206
207
|
function b(e, n) {
|
|
207
208
|
let r;
|
|
@@ -213,7 +214,7 @@ function b(e, n) {
|
|
|
213
214
|
if (r.protocol !== "https:" && r.protocol !== "http:") throw new t("INTERNAL", 0, `playback ${e} has a disallowed scheme: ${r.protocol}`);
|
|
214
215
|
return n;
|
|
215
216
|
}
|
|
216
|
-
function
|
|
217
|
+
function se(e, n) {
|
|
217
218
|
if (!e.manifestUrl) throw new t("INTERNAL", 0, "playback response is missing manifestUrl");
|
|
218
219
|
let r = e.drmEnabled ?? !!e.drm, i = {
|
|
219
220
|
matchUrn: e.matchUrn ?? n,
|
|
@@ -222,7 +223,7 @@ function x(e, n) {
|
|
|
222
223
|
manifestUrl: b("manifestUrl", e.manifestUrl),
|
|
223
224
|
serverTime: e.serverTime ?? ""
|
|
224
225
|
};
|
|
225
|
-
if (e.liveStartsAt && (i.liveStartsAt = e.liveStartsAt), r && e.drm) {
|
|
226
|
+
if (e.liveStartsAt && (i.liveStartsAt = e.liveStartsAt), typeof e.analytics?.sid == "string" && e.analytics.sid && (i.analytics = { sid: e.analytics.sid }), r && e.drm) {
|
|
226
227
|
let t = {};
|
|
227
228
|
e.drm.widevine?.licenseUrl && (t.widevine = { licenseUrl: b("widevine licenseUrl", e.drm.widevine.licenseUrl) }), e.drm.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && (t.fairplay = {
|
|
228
229
|
licenseUrl: b("fairplay licenseUrl", e.drm.fairplay.licenseUrl),
|
|
@@ -235,7 +236,7 @@ function x(e, n) {
|
|
|
235
236
|
}
|
|
236
237
|
//#endregion
|
|
237
238
|
//#region src/core/catalog.ts
|
|
238
|
-
async function
|
|
239
|
+
async function x(e) {
|
|
239
240
|
let r = new URLSearchParams();
|
|
240
241
|
e.status && r.set("status", Array.isArray(e.status) ? e.status.join(",") : e.status), e.sport && r.set("sport", e.sport);
|
|
241
242
|
let i = r.toString(), a = o(e.baseUrl, "/v1/catalog" + (i ? `?${i}` : "")), s = new Headers();
|
|
@@ -252,12 +253,12 @@ async function S(e) {
|
|
|
252
253
|
};
|
|
253
254
|
if (!l.ok) throw new t(n(l.status), l.status, `catalog request failed (HTTP ${l.status})`);
|
|
254
255
|
return {
|
|
255
|
-
tournaments:
|
|
256
|
+
tournaments: S(await l.json()),
|
|
256
257
|
etag: l.headers.get("etag") ?? void 0,
|
|
257
258
|
notModified: !1
|
|
258
259
|
};
|
|
259
260
|
}
|
|
260
|
-
function
|
|
261
|
+
function S(e) {
|
|
261
262
|
return (e.tournaments ?? []).map((e) => {
|
|
262
263
|
let t = e.urn ?? "", n = e.name ?? "", r = e.sport ?? "";
|
|
263
264
|
return {
|
|
@@ -480,11 +481,11 @@ function me(e, t, n = {}) {
|
|
|
480
481
|
}
|
|
481
482
|
//#endregion
|
|
482
483
|
//#region src/core/drm/support.ts
|
|
483
|
-
var he = 5e3, O = "video/mp4; codecs=\"avc1.42E01E\"", k = "audio/mp4; codecs=\"mp4a.40.2\"",
|
|
484
|
+
var he = 5e3, O = "video/mp4; codecs=\"avc1.42E01E\"", k = "audio/mp4; codecs=\"mp4a.40.2\"", ge = {
|
|
484
485
|
widevine: ["com.widevine.alpha"],
|
|
485
486
|
fairplay: ["com.apple.fps"]
|
|
486
487
|
};
|
|
487
|
-
function
|
|
488
|
+
function _e(e) {
|
|
488
489
|
return e === "fairplay" ? [{
|
|
489
490
|
initDataTypes: [
|
|
490
491
|
"sinf",
|
|
@@ -515,7 +516,7 @@ function j(e) {
|
|
|
515
516
|
}]
|
|
516
517
|
}];
|
|
517
518
|
}
|
|
518
|
-
function
|
|
519
|
+
function A(e, t) {
|
|
519
520
|
return new Promise((n, r) => {
|
|
520
521
|
let i = setTimeout(() => r(/* @__PURE__ */ Error("probe-timeout")), t);
|
|
521
522
|
e.then((e) => {
|
|
@@ -525,7 +526,7 @@ function M(e, t) {
|
|
|
525
526
|
});
|
|
526
527
|
});
|
|
527
528
|
}
|
|
528
|
-
async function
|
|
529
|
+
async function j(e = {}) {
|
|
529
530
|
let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? he, r = {};
|
|
530
531
|
if (typeof window < "u" && window.isSecureContext === !1) return {
|
|
531
532
|
verdict: "insecure-context",
|
|
@@ -538,7 +539,7 @@ async function N(e = {}) {
|
|
|
538
539
|
};
|
|
539
540
|
let a = !1, o = !1, s = Date.now() + n, c = () => Math.max(1, s - Date.now());
|
|
540
541
|
for (let e of t) {
|
|
541
|
-
let t =
|
|
542
|
+
let t = ge[e];
|
|
542
543
|
if (!t) {
|
|
543
544
|
r[e] = "unknown system";
|
|
544
545
|
continue;
|
|
@@ -549,7 +550,7 @@ async function N(e = {}) {
|
|
|
549
550
|
break;
|
|
550
551
|
}
|
|
551
552
|
try {
|
|
552
|
-
return await
|
|
553
|
+
return await A((await A(i(n, _e(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
|
|
553
554
|
verdict: "supported",
|
|
554
555
|
system: e,
|
|
555
556
|
detail: r
|
|
@@ -573,17 +574,17 @@ async function N(e = {}) {
|
|
|
573
574
|
}
|
|
574
575
|
//#endregion
|
|
575
576
|
//#region src/core/manifest.ts
|
|
576
|
-
var
|
|
577
|
-
function
|
|
578
|
-
return
|
|
577
|
+
var M = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
|
|
578
|
+
function N(e) {
|
|
579
|
+
return M.test(e);
|
|
579
580
|
}
|
|
580
581
|
//#endregion
|
|
581
582
|
//#region src/core/stats.ts
|
|
582
|
-
var
|
|
583
|
-
function
|
|
583
|
+
var P = { droppedFrames: 0 };
|
|
584
|
+
function F(e) {
|
|
584
585
|
return e.getVideoPlaybackQuality?.()?.droppedVideoFrames ?? 0;
|
|
585
586
|
}
|
|
586
|
-
function
|
|
587
|
+
function I(e, t) {
|
|
587
588
|
let n = [
|
|
588
589
|
["playing", () => t("playing")],
|
|
589
590
|
["waiting", () => t("buffering")],
|
|
@@ -600,7 +601,7 @@ function R(e, t) {
|
|
|
600
601
|
}
|
|
601
602
|
//#endregion
|
|
602
603
|
//#region src/core/engine/native.ts
|
|
603
|
-
function
|
|
604
|
+
function ve(e) {
|
|
604
605
|
try {
|
|
605
606
|
let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
|
|
606
607
|
return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
|
|
@@ -608,15 +609,15 @@ function ge(e) {
|
|
|
608
609
|
return;
|
|
609
610
|
}
|
|
610
611
|
}
|
|
611
|
-
function
|
|
612
|
+
function L(e) {
|
|
612
613
|
return !!(e ?? (typeof document < "u" ? document.createElement("video") : void 0))?.canPlayType("application/vnd.apple.mpegurl");
|
|
613
614
|
}
|
|
614
|
-
function
|
|
615
|
+
function R() {
|
|
615
616
|
if (typeof window > "u") return !1;
|
|
616
617
|
let e = "WebKitMediaKeys" in window || typeof HTMLMediaElement < "u" && "webkitSetMediaKeys" in HTMLMediaElement.prototype, t = typeof navigator < "u" && /Apple/i.test(navigator.vendor ?? "");
|
|
617
618
|
return e || t;
|
|
618
619
|
}
|
|
619
|
-
var
|
|
620
|
+
var z = class {
|
|
620
621
|
name = "native";
|
|
621
622
|
deps;
|
|
622
623
|
onLoaded;
|
|
@@ -646,7 +647,7 @@ var V = class {
|
|
|
646
647
|
type: "error",
|
|
647
648
|
error: new t("INTERNAL", 0, "native HLS playback error"),
|
|
648
649
|
fatal: !0
|
|
649
|
-
}), e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = n.manifestUrl, e.load();
|
|
650
|
+
}), e.addEventListener("loadedmetadata", this.onLoaded, { once: !0 }), e.addEventListener("error", this.onError), e.src = ye(n.manifestUrl, n.analytics?.sid), e.load();
|
|
650
651
|
}
|
|
651
652
|
armFairPlay() {
|
|
652
653
|
let { video: e, descriptor: n, credential: r, deviceId: i, userId: a, emit: o } = this.deps, s = n.drm?.fairplay;
|
|
@@ -659,7 +660,7 @@ var V = class {
|
|
|
659
660
|
});
|
|
660
661
|
this.onEncrypted = (t) => {
|
|
661
662
|
let o = t;
|
|
662
|
-
!o.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV =
|
|
663
|
+
!o.initData || this.fairplayArmed || (this.fairplayArmed = !0, this.fairplayKeyIV = ve(o.initData), (async () => {
|
|
663
664
|
try {
|
|
664
665
|
let t = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
|
|
665
666
|
initDataTypes: [o.initDataType],
|
|
@@ -751,7 +752,7 @@ var V = class {
|
|
|
751
752
|
e.seekable.length > 0 && (e.currentTime = e.seekable.end(e.seekable.length - 1));
|
|
752
753
|
}
|
|
753
754
|
getStats() {
|
|
754
|
-
let e = this.deps.video, t = { droppedFrames:
|
|
755
|
+
let e = this.deps.video, t = { droppedFrames: F(e) };
|
|
755
756
|
return e.duration === Infinity && (t.isLive = !0, e.seekable.length > 0 && (t.atLiveEdge = e.currentTime >= e.seekable.end(e.seekable.length - 1) - 2)), t;
|
|
756
757
|
}
|
|
757
758
|
stop() {
|
|
@@ -771,31 +772,38 @@ var V = class {
|
|
|
771
772
|
} catch {}
|
|
772
773
|
}
|
|
773
774
|
};
|
|
775
|
+
function ye(e, t) {
|
|
776
|
+
return t ? `${e}${e.includes("?") ? "&" : "?"}CMCD=${encodeURIComponent(`sid="${t}"`)}` : e;
|
|
777
|
+
}
|
|
774
778
|
//#endregion
|
|
775
779
|
//#region src/core/engine/hls.ts
|
|
776
|
-
function
|
|
780
|
+
function be() {
|
|
777
781
|
return e.isSupported();
|
|
778
782
|
}
|
|
779
|
-
var
|
|
780
|
-
function
|
|
783
|
+
var xe = 3, Se = 5, Ce = 3, we = 3e4, Te = 12e3, Ee = 3e3, B = 6e3, De = 4e3, Oe = 2, ke = 5, Ae = 2, je = 9, Me = .5;
|
|
784
|
+
function Ne(e) {
|
|
781
785
|
if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
|
|
782
786
|
delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
|
|
783
787
|
return;
|
|
784
788
|
}
|
|
785
|
-
e.liveSyncDuration ??=
|
|
786
|
-
let t = Math.max(12, e.liveSyncDuration +
|
|
787
|
-
(e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t),
|
|
789
|
+
e.liveSyncDuration ??= Ae;
|
|
790
|
+
let t = Math.max(12, e.liveSyncDuration + je);
|
|
791
|
+
(e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Pe(e, e.liveSyncDuration);
|
|
788
792
|
}
|
|
789
|
-
function
|
|
793
|
+
function Pe(e, t) {
|
|
790
794
|
let n = (e, t, n) => Math.min(n, Math.max(t, e));
|
|
791
795
|
e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
|
|
792
796
|
}
|
|
793
|
-
var
|
|
797
|
+
var V = class {
|
|
794
798
|
name = "hls";
|
|
795
799
|
hls = null;
|
|
796
800
|
deps;
|
|
797
801
|
licenseUrl;
|
|
798
802
|
detectedLowLatency = !1;
|
|
803
|
+
bytesLoaded = 0;
|
|
804
|
+
playlistBytesLoaded = 0;
|
|
805
|
+
renditionSwitches = 0;
|
|
806
|
+
licenseTimeMs = 0;
|
|
799
807
|
mediaRecoveries = 0;
|
|
800
808
|
networkRecoveries = 0;
|
|
801
809
|
licenseRecoveries = 0;
|
|
@@ -819,7 +827,7 @@ var U = class {
|
|
|
819
827
|
this.deps = e, this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl;
|
|
820
828
|
}
|
|
821
829
|
async load() {
|
|
822
|
-
let { descriptor: e, snapToLiveOnRefocus: n, video: r, emit: i } = this.deps, a =
|
|
830
|
+
let { descriptor: e, snapToLiveOnRefocus: n, video: r, emit: i } = this.deps, a = L(r) && R(), o = !!e.drm?.widevine?.licenseUrl && !a;
|
|
823
831
|
if (e.drmEnabled && !o) {
|
|
824
832
|
i({
|
|
825
833
|
type: "error",
|
|
@@ -843,7 +851,7 @@ var U = class {
|
|
|
843
851
|
let t = e.liveSyncPosition;
|
|
844
852
|
if (typeof t != "number" || !Number.isFinite(t)) return;
|
|
845
853
|
let { video: n } = this.deps;
|
|
846
|
-
n.currentTime <= t +
|
|
854
|
+
n.currentTime <= t + Me || (n.currentTime = t);
|
|
847
855
|
}
|
|
848
856
|
startHls() {
|
|
849
857
|
let { video: t, descriptor: n, credential: r, deviceId: i, userId: a, lowLatency: o, debug: s, hlsConfig: c, emit: l } = this.deps, u = n.drmEnabled && !!(n.drm?.widevine?.licenseUrl || n.drm?.fairplay?.licenseUrl), d = {
|
|
@@ -866,33 +874,48 @@ var U = class {
|
|
|
866
874
|
},
|
|
867
875
|
licenseXhrSetup: (e, t) => {
|
|
868
876
|
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);
|
|
877
|
+
let o = performance.now();
|
|
878
|
+
e.addEventListener("loadend", () => {
|
|
879
|
+
this.licenseTimeMs = Math.max(1, Math.round(performance.now() - o));
|
|
880
|
+
});
|
|
869
881
|
}
|
|
870
882
|
} : {},
|
|
883
|
+
...n.analytics?.sid ? { cmcd: {
|
|
884
|
+
sessionId: n.analytics.sid,
|
|
885
|
+
contentId: n.matchUrn,
|
|
886
|
+
useHeaders: !1
|
|
887
|
+
} } : {},
|
|
871
888
|
...c
|
|
872
889
|
};
|
|
873
|
-
|
|
890
|
+
Ne(d);
|
|
874
891
|
let f = new e(d);
|
|
875
892
|
this.hls = f, f.on(e.Events.MANIFEST_PARSED, () => {
|
|
876
893
|
this.mediaRecoveries = 0, this.networkRecoveries = 0, l({ type: "ready" });
|
|
877
894
|
}), f.on(e.Events.FRAG_BUFFERED, (e, t) => {
|
|
878
895
|
t?.frag?.type === "main" && (this.mediaRecoveries > 0 || this.networkRecoveries > 0 || this.licenseRecoveries > 0 || this.starvationKicks > 0) && !this.recoveryResetTimer && (this.recoveryResetTimer = setTimeout(() => {
|
|
879
896
|
this.mediaRecoveries = 0, this.networkRecoveries = 0, this.licenseRecoveries = 0, this.starvationKicks = 0, this.recoveryResetTimer = null;
|
|
880
|
-
},
|
|
897
|
+
}, we));
|
|
881
898
|
}), f.on(e.Events.FRAG_LOADING, (e, t) => {
|
|
882
899
|
t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
|
|
900
|
+
}), f.on(e.Events.FRAG_LOADED, (e, t) => {
|
|
901
|
+
let n = t?.frag?.stats?.loaded;
|
|
902
|
+
typeof n == "number" && (this.bytesLoaded += n);
|
|
883
903
|
}), f.on(e.Events.LEVEL_LOADED, (e, t) => {
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
904
|
+
let n = t?.stats?.loaded;
|
|
905
|
+
if (typeof n == "number" && (this.playlistBytesLoaded += n), this.detectedLowLatency) return;
|
|
906
|
+
let r = t.details;
|
|
907
|
+
(r?.partList?.length || (r?.partTarget ?? 0) > 0) && (this.detectedLowLatency = !0, l({
|
|
887
908
|
type: "lowLatencyDetected",
|
|
888
909
|
value: !0
|
|
889
910
|
}));
|
|
890
911
|
}), f.on(e.Events.LEVEL_LOADED, (e, t) => this.trackEnd(t)), this.endWatchdog = setInterval(() => {
|
|
891
912
|
this.checkEndStale(), this.checkStarvation();
|
|
892
|
-
},
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
913
|
+
}, Ee), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
|
|
914
|
+
this.renditionSwitches += 1, l({
|
|
915
|
+
type: "qualitychange",
|
|
916
|
+
index: t.level
|
|
917
|
+
});
|
|
918
|
+
}), f.on(e.Events.AUDIO_TRACK_SWITCHED, (e, t) => l({
|
|
896
919
|
type: "audiotrackchange",
|
|
897
920
|
id: t.id
|
|
898
921
|
})), f.on(e.Events.SUBTITLE_TRACK_SWITCH, (e, t) => l({
|
|
@@ -958,13 +981,17 @@ var U = class {
|
|
|
958
981
|
}
|
|
959
982
|
getStats() {
|
|
960
983
|
let e = this.hls, t = {
|
|
961
|
-
droppedFrames:
|
|
962
|
-
lowLatency: this.detectedLowLatency
|
|
984
|
+
droppedFrames: F(this.deps.video),
|
|
985
|
+
lowLatency: this.detectedLowLatency,
|
|
986
|
+
bytesLoaded: this.bytesLoaded,
|
|
987
|
+
playlistBytesLoaded: this.playlistBytesLoaded,
|
|
988
|
+
renditionSwitches: this.renditionSwitches,
|
|
989
|
+
licenseTimeMs: this.licenseTimeMs
|
|
963
990
|
};
|
|
964
991
|
if (e) {
|
|
965
992
|
typeof e.latency == "number" && (t.latencySeconds = e.latency), typeof e.targetLatency == "number" && e.targetLatency > 0 && (t.targetLatencySeconds = e.targetLatency), typeof e.bandwidthEstimate == "number" && (t.bandwidthKbps = Math.round(e.bandwidthEstimate / 1e3));
|
|
966
993
|
let n = e.currentLevel >= 0 ? e.levels[e.currentLevel] : void 0;
|
|
967
|
-
n && (t.levelHeight = n.height), typeof n?.details?.live == "boolean" && (t.isLive = n.details.live, typeof e.liveSyncPosition == "number" && (t.atLiveEdge = this.deps.video.currentTime >= e.liveSyncPosition - 1.5));
|
|
994
|
+
n && (t.levelHeight = n.height), n && typeof n.bitrate == "number" && (t.renditionBitrateKbps = Math.round(n.bitrate / 1e3)), typeof n?.details?.live == "boolean" && (t.isLive = n.details.live, typeof e.liveSyncPosition == "number" && (t.atLiveEdge = this.deps.video.currentTime >= e.liveSyncPosition - 1.5));
|
|
968
995
|
}
|
|
969
996
|
return t;
|
|
970
997
|
}
|
|
@@ -987,7 +1014,7 @@ var U = class {
|
|
|
987
1014
|
return;
|
|
988
1015
|
}
|
|
989
1016
|
let e = Date.now() - this.lastAdvanceMs;
|
|
990
|
-
!this.chaseSuspended && e >
|
|
1017
|
+
!this.chaseSuspended && e > B && this.suspendChasing(), e > Te && this.emitEnded("stalled");
|
|
991
1018
|
}
|
|
992
1019
|
checkStarvation() {
|
|
993
1020
|
let e = this.hls, { video: t } = this.deps;
|
|
@@ -996,12 +1023,12 @@ var U = class {
|
|
|
996
1023
|
this.starvedTicks = 0;
|
|
997
1024
|
return;
|
|
998
1025
|
}
|
|
999
|
-
let n = this.lastAdvanceMs !== 0 && Date.now() - this.lastAdvanceMs <
|
|
1026
|
+
let n = this.lastAdvanceMs !== 0 && Date.now() - this.lastAdvanceMs < B, r = this.lastFragLoadingMs !== 0 && Date.now() - this.lastFragLoadingMs > De;
|
|
1000
1027
|
if (!(t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused && n && r)) {
|
|
1001
1028
|
this.starvedTicks = 0;
|
|
1002
1029
|
return;
|
|
1003
1030
|
}
|
|
1004
|
-
if (!(++this.starvedTicks <
|
|
1031
|
+
if (!(++this.starvedTicks < Oe) && (this.starvedTicks = 0, !(this.starvationKicks >= ke))) {
|
|
1005
1032
|
this.starvationKicks += 1, console.warn("havik-player: video starved while the live playlist advances (wedged hls.js fragment pipeline) — restarting load at the live edge");
|
|
1006
1033
|
try {
|
|
1007
1034
|
e.stopLoad(), e.startLoad(-1);
|
|
@@ -1050,7 +1077,7 @@ var U = class {
|
|
|
1050
1077
|
scheduleLicenseRetry(t) {
|
|
1051
1078
|
if (!(t.details === e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED || t.details === e.ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED)) return !1;
|
|
1052
1079
|
let n = t.response?.code ?? 0;
|
|
1053
|
-
if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >=
|
|
1080
|
+
if (!(n === 0 || n === 408 || n === 429 || n >= 500) || this.licenseRecoveries >= Ce) return !1;
|
|
1054
1081
|
this.licenseRecoveries += 1;
|
|
1055
1082
|
let r = Math.min(8e3, 500 * 2 ** (this.licenseRecoveries - 1));
|
|
1056
1083
|
return this.licenseRetryTimer = setTimeout(() => {
|
|
@@ -1078,7 +1105,7 @@ var U = class {
|
|
|
1078
1105
|
return;
|
|
1079
1106
|
}
|
|
1080
1107
|
if (t.type === e.ErrorTypes.NETWORK_ERROR) {
|
|
1081
|
-
if (this.networkRecoveries <
|
|
1108
|
+
if (this.networkRecoveries < Se) {
|
|
1082
1109
|
this.networkRecoveries += 1;
|
|
1083
1110
|
let e = Math.min(8e3, 1e3 * 2 ** (this.networkRecoveries - 1));
|
|
1084
1111
|
this.networkRetryTimer = setTimeout(() => {
|
|
@@ -1094,7 +1121,7 @@ var U = class {
|
|
|
1094
1121
|
return;
|
|
1095
1122
|
}
|
|
1096
1123
|
if (t.type === e.ErrorTypes.MEDIA_ERROR) {
|
|
1097
|
-
if (this.mediaRecoveries <
|
|
1124
|
+
if (this.mediaRecoveries < xe) {
|
|
1098
1125
|
this.mediaRecoveries += 1, n.recoverMediaError();
|
|
1099
1126
|
return;
|
|
1100
1127
|
}
|
|
@@ -1116,7 +1143,172 @@ var U = class {
|
|
|
1116
1143
|
let r = e.response?.code ?? 0, i = e.details ?? e.type, a = e.reason ?? e.error?.message ?? "";
|
|
1117
1144
|
return new t(r === 401 ? "UNAUTHORIZED" : r === 403 || n === "DRM" ? "FORBIDDEN" : "INTERNAL", r, `${n} error: ${i}${a ? ` (${a})` : ""}`);
|
|
1118
1145
|
}
|
|
1119
|
-
},
|
|
1146
|
+
}, Fe = 15e3, Ie = 100, H = 1, U = {
|
|
1147
|
+
idle: "PLAYER_STATE_IDLE",
|
|
1148
|
+
waiting: "PLAYER_STATE_WAITING",
|
|
1149
|
+
loading: "PLAYER_STATE_LOADING",
|
|
1150
|
+
playing: "PLAYER_STATE_PLAYING",
|
|
1151
|
+
buffering: "PLAYER_STATE_BUFFERING",
|
|
1152
|
+
paused: "PLAYER_STATE_PAUSED",
|
|
1153
|
+
ended: "PLAYER_STATE_ENDED",
|
|
1154
|
+
error: "PLAYER_STATE_ERROR"
|
|
1155
|
+
};
|
|
1156
|
+
function Le(e) {
|
|
1157
|
+
try {
|
|
1158
|
+
let t = new URL(e), [n, ...r] = t.hostname.split(".");
|
|
1159
|
+
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);
|
|
1160
|
+
} catch {
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
var Re = class {
|
|
1165
|
+
opts;
|
|
1166
|
+
url;
|
|
1167
|
+
now;
|
|
1168
|
+
seq = 0;
|
|
1169
|
+
pending = [];
|
|
1170
|
+
timer = null;
|
|
1171
|
+
endedFlag = !1;
|
|
1172
|
+
lastBytes = 0;
|
|
1173
|
+
lastPlaylistBytes = 0;
|
|
1174
|
+
lastDropped = 0;
|
|
1175
|
+
lastSwitches = 0;
|
|
1176
|
+
playingSinceMs = null;
|
|
1177
|
+
hiddenSinceMs = null;
|
|
1178
|
+
playingTotalMs = 0;
|
|
1179
|
+
hiddenTotalMs = 0;
|
|
1180
|
+
lastHbPlayingMs = 0;
|
|
1181
|
+
lastHbHiddenMs = 0;
|
|
1182
|
+
rebufferCount = 0;
|
|
1183
|
+
rebufferTotalMs = 0;
|
|
1184
|
+
bufferingSinceMs = null;
|
|
1185
|
+
errorCount = 0;
|
|
1186
|
+
seekCount = 0;
|
|
1187
|
+
pauseCount = 0;
|
|
1188
|
+
bornMs;
|
|
1189
|
+
startupTimeMs = 0;
|
|
1190
|
+
manifestLoadMs = 0;
|
|
1191
|
+
firstFragLoadMs = 0;
|
|
1192
|
+
onPagehide = () => this.end("END_REASON_PAGEHIDE");
|
|
1193
|
+
onVisibility = () => {
|
|
1194
|
+
typeof document > "u" || (document.visibilityState === "hidden" ? this.hiddenSinceMs === null && (this.hiddenSinceMs = this.now()) : this.hiddenSinceMs !== null && (this.hiddenTotalMs += this.now() - this.hiddenSinceMs, this.hiddenSinceMs = null));
|
|
1195
|
+
};
|
|
1196
|
+
constructor(e) {
|
|
1197
|
+
this.opts = {
|
|
1198
|
+
...e,
|
|
1199
|
+
heartbeatMs: e.heartbeatMs ?? Fe
|
|
1200
|
+
}, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`, this.now = e.now ?? (() => Date.now()), this.bornMs = this.now();
|
|
1201
|
+
}
|
|
1202
|
+
start() {
|
|
1203
|
+
this.endedFlag || (this.enqueue({ sessionStart: {
|
|
1204
|
+
player: this.opts.player,
|
|
1205
|
+
sdkVersion: this.opts.sdkVersion
|
|
1206
|
+
} }), this.flush(), 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()));
|
|
1207
|
+
}
|
|
1208
|
+
noteStateChange(e, t, n, r) {
|
|
1209
|
+
if (this.endedFlag || e === t) return;
|
|
1210
|
+
let i = this.now();
|
|
1211
|
+
t === "playing" ? (this.playingSinceMs === null && (this.playingSinceMs = i), this.startupTimeMs === 0 && (this.startupTimeMs = Math.max(H, i - this.bornMs))) : this.playingSinceMs !== null && (this.playingTotalMs += i - this.playingSinceMs, this.playingSinceMs = null), t === "buffering" && e === "playing" ? (this.rebufferCount += 1, this.bufferingSinceMs = i) : e === "buffering" && this.bufferingSinceMs !== null && (this.rebufferTotalMs += i - this.bufferingSinceMs, this.bufferingSinceMs = null), t === "paused" && (this.pauseCount += 1);
|
|
1212
|
+
let a = {
|
|
1213
|
+
fromState: U[e] ?? "PLAYER_STATE_UNSPECIFIED",
|
|
1214
|
+
toState: U[t] ?? "PLAYER_STATE_UNSPECIFIED"
|
|
1215
|
+
};
|
|
1216
|
+
n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
|
|
1217
|
+
}
|
|
1218
|
+
noteReady() {
|
|
1219
|
+
this.manifestLoadMs === 0 && (this.manifestLoadMs = Math.max(H, this.now() - this.bornMs));
|
|
1220
|
+
}
|
|
1221
|
+
noteFirstBytes() {
|
|
1222
|
+
this.firstFragLoadMs === 0 && (this.firstFragLoadMs = Math.max(H, this.now() - this.bornMs));
|
|
1223
|
+
}
|
|
1224
|
+
noteSeek() {
|
|
1225
|
+
this.endedFlag || (this.seekCount += 1);
|
|
1226
|
+
}
|
|
1227
|
+
noteError(e, t, n, r, i) {
|
|
1228
|
+
if (this.endedFlag) return;
|
|
1229
|
+
this.errorCount += 1;
|
|
1230
|
+
let a = { code: e };
|
|
1231
|
+
t && (a.detail = t), n && (a.fatal = !0), r > 0 && (a.httpStatus = r), i > 0 && (a.positionMs = Math.round(i)), this.enqueue({ error: a }), this.flush();
|
|
1232
|
+
}
|
|
1233
|
+
noteEnded() {
|
|
1234
|
+
this.end("END_REASON_ENDED");
|
|
1235
|
+
}
|
|
1236
|
+
noteDestroyed() {
|
|
1237
|
+
this.end("END_REASON_STOPPED");
|
|
1238
|
+
}
|
|
1239
|
+
noteFatal() {
|
|
1240
|
+
this.end("END_REASON_FATAL_ERROR");
|
|
1241
|
+
}
|
|
1242
|
+
heartbeat() {
|
|
1243
|
+
if (this.endedFlag) return;
|
|
1244
|
+
let e = this.opts.sample(), t = this.now(), n = this.playingTotalMs;
|
|
1245
|
+
this.playingSinceMs !== null && (n += t - this.playingSinceMs);
|
|
1246
|
+
let r = this.hiddenTotalMs;
|
|
1247
|
+
this.hiddenSinceMs !== null && (r += t - this.hiddenSinceMs);
|
|
1248
|
+
let i = {}, a = e.bytesLoaded ?? 0;
|
|
1249
|
+
a > this.lastBytes && (i.bytesDownloadedDelta = a - this.lastBytes), this.lastBytes = a;
|
|
1250
|
+
let o = e.playlistBytesLoaded ?? 0;
|
|
1251
|
+
o > this.lastPlaylistBytes && (i.playlistBytesDelta = o - this.lastPlaylistBytes), this.lastPlaylistBytes = o;
|
|
1252
|
+
let s = e.droppedFrames ?? 0;
|
|
1253
|
+
s > this.lastDropped && (i.droppedFramesDelta = s - this.lastDropped), this.lastDropped = s;
|
|
1254
|
+
let c = e.renditionSwitches ?? 0;
|
|
1255
|
+
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);
|
|
1256
|
+
let l = this.bufferedMsFrom(e);
|
|
1257
|
+
l > 0 && (i.bufferedMs = l), i.playerState = U[e.state] ?? "PLAYER_STATE_UNSPECIFIED", this.enqueue({ heartbeat: i }), this.flush(), a > 0 && this.noteFirstBytes();
|
|
1258
|
+
}
|
|
1259
|
+
bufferedMsFrom(e) {
|
|
1260
|
+
let t = e;
|
|
1261
|
+
return typeof t.bufferedMs == "number" ? Math.round(t.bufferedMs) : 0;
|
|
1262
|
+
}
|
|
1263
|
+
end(e) {
|
|
1264
|
+
if (this.endedFlag) return;
|
|
1265
|
+
this.endedFlag = !0;
|
|
1266
|
+
let t = this.now();
|
|
1267
|
+
this.playingSinceMs !== null && (this.playingTotalMs += t - this.playingSinceMs, this.playingSinceMs = null), this.hiddenSinceMs !== null && (this.hiddenTotalMs += t - this.hiddenSinceMs, this.hiddenSinceMs = null), this.bufferingSinceMs !== null && (this.rebufferTotalMs += t - this.bufferingSinceMs, this.bufferingSinceMs = null), this.timer &&= (clearInterval(this.timer), null), typeof window < "u" && window.removeEventListener("pagehide", this.onPagehide), typeof document < "u" && document.removeEventListener("visibilitychange", this.onVisibility);
|
|
1268
|
+
let n = this.safeSample(), r = { reason: e }, i = n?.bytesLoaded ?? this.lastBytes;
|
|
1269
|
+
i > 0 && (r.bytesDownloadedTotal = i), this.playingTotalMs > 0 && (r.watchTimeMs = Math.round(this.playingTotalMs)), this.rebufferCount > 0 && (r.rebufferCount = this.rebufferCount), this.rebufferTotalMs > 0 && (r.rebufferTimeMs = Math.round(this.rebufferTotalMs)), this.startupTimeMs > 0 && (r.startupTimeMs = Math.round(this.startupTimeMs)), this.manifestLoadMs > 0 && (r.manifestLoadMs = Math.round(this.manifestLoadMs)), this.firstFragLoadMs > 0 && (r.firstFragLoadMs = Math.round(this.firstFragLoadMs));
|
|
1270
|
+
let a = n?.licenseTimeMs ?? 0;
|
|
1271
|
+
a > 0 && (r.licenseTimeMs = Math.round(a));
|
|
1272
|
+
let o = n?.droppedFrames ?? this.lastDropped;
|
|
1273
|
+
o > 0 && (r.droppedFramesTotal = o), this.errorCount > 0 && (r.errorCount = this.errorCount), this.hiddenTotalMs > 0 && (r.hiddenTimeMs = Math.round(this.hiddenTotalMs)), this.seekCount > 0 && (r.seekCount = this.seekCount), this.pauseCount > 0 && (r.pauseCount = this.pauseCount), this.pending.push(this.wrap({ sessionEnd: r })), this.flush(e === "END_REASON_PAGEHIDE");
|
|
1274
|
+
}
|
|
1275
|
+
safeSample() {
|
|
1276
|
+
try {
|
|
1277
|
+
return this.opts.sample();
|
|
1278
|
+
} catch {
|
|
1279
|
+
return null;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
wrap(e) {
|
|
1283
|
+
return this.seq += 1, {
|
|
1284
|
+
sid: this.opts.sid,
|
|
1285
|
+
clientTime: new Date(this.now()).toISOString(),
|
|
1286
|
+
seq: this.seq,
|
|
1287
|
+
...e
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
enqueue(e) {
|
|
1291
|
+
for (this.pending.push(this.wrap(e)); this.pending.length > Ie;) {
|
|
1292
|
+
let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
|
|
1293
|
+
this.pending.splice(e >= 0 ? e : 0, 1);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
flush(e = !1) {
|
|
1297
|
+
if (this.pending.length === 0) return;
|
|
1298
|
+
let t = JSON.stringify({ beacons: this.pending });
|
|
1299
|
+
if (this.pending = [], e) {
|
|
1300
|
+
let e = this.opts.sendBeacon ?? (typeof navigator < "u" && navigator.sendBeacon ? navigator.sendBeacon.bind(navigator) : void 0);
|
|
1301
|
+
if (e && e(this.url, t)) return;
|
|
1302
|
+
}
|
|
1303
|
+
let n = this.opts.fetchFn ?? (typeof fetch < "u" ? fetch : void 0);
|
|
1304
|
+
n && n(this.url, {
|
|
1305
|
+
method: "POST",
|
|
1306
|
+
headers: { "content-type": "application/json" },
|
|
1307
|
+
body: t,
|
|
1308
|
+
keepalive: !0
|
|
1309
|
+
}).catch(() => {});
|
|
1310
|
+
}
|
|
1311
|
+
}, ze = "1.9.0", W = {
|
|
1120
1312
|
accent: "#E1B600",
|
|
1121
1313
|
accentText: "#1B1C23",
|
|
1122
1314
|
background: "#0e0f13",
|
|
@@ -1149,15 +1341,15 @@ function K(e, t) {
|
|
|
1149
1341
|
}
|
|
1150
1342
|
//#endregion
|
|
1151
1343
|
//#region src/ui/styles.ts
|
|
1152
|
-
var q = "havik-player-styles",
|
|
1153
|
-
function
|
|
1344
|
+
var q = "havik-player-styles", Be = "\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";
|
|
1345
|
+
function Ve(e = document) {
|
|
1154
1346
|
if (e.getElementById(q)) return;
|
|
1155
1347
|
let t = e.createElement("style");
|
|
1156
|
-
t.id = q, t.textContent =
|
|
1348
|
+
t.id = q, t.textContent = Be, e.head.appendChild(t);
|
|
1157
1349
|
}
|
|
1158
1350
|
//#endregion
|
|
1159
1351
|
//#region src/ui/icons.ts
|
|
1160
|
-
var
|
|
1352
|
+
var He = {
|
|
1161
1353
|
play: "<path d=\"M8 5v14l11-7z\"/>",
|
|
1162
1354
|
pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
|
|
1163
1355
|
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\"/>",
|
|
@@ -1171,20 +1363,20 @@ var Pe = {
|
|
|
1171
1363
|
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\"/>"
|
|
1172
1364
|
};
|
|
1173
1365
|
function J(e) {
|
|
1174
|
-
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${
|
|
1366
|
+
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${He[e]}</svg>`;
|
|
1175
1367
|
}
|
|
1176
1368
|
//#endregion
|
|
1177
1369
|
//#region src/ui/controls.ts
|
|
1178
|
-
var
|
|
1370
|
+
var Ue = 3e3, We = /* @__PURE__ */ new Set([
|
|
1179
1371
|
"GONE",
|
|
1180
1372
|
"NOT_FOUND",
|
|
1181
1373
|
"INVALID_URN",
|
|
1182
1374
|
"UNAUTHORIZED"
|
|
1183
1375
|
]);
|
|
1184
|
-
function
|
|
1185
|
-
return new
|
|
1376
|
+
function Ge(e) {
|
|
1377
|
+
return new Ke(e);
|
|
1186
1378
|
}
|
|
1187
|
-
var
|
|
1379
|
+
var Ke = class {
|
|
1188
1380
|
root;
|
|
1189
1381
|
video;
|
|
1190
1382
|
player;
|
|
@@ -1224,7 +1416,7 @@ var Re = class {
|
|
|
1224
1416
|
scrubbing = !1;
|
|
1225
1417
|
offFns = [];
|
|
1226
1418
|
constructor(e) {
|
|
1227
|
-
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ??
|
|
1419
|
+
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Ue, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.errorMessage = e.errorMessage, Ve(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");
|
|
1228
1420
|
let t = (e, t, n) => {
|
|
1229
1421
|
let r = this.doc.createElement(e);
|
|
1230
1422
|
if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
|
|
@@ -1311,7 +1503,7 @@ var Re = class {
|
|
|
1311
1503
|
e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = J("play"), e.addEventListener("click", () => void this.player.play()), t.append(e);
|
|
1312
1504
|
} else {
|
|
1313
1505
|
let n = this.doc.createElement("div");
|
|
1314
|
-
if (n.className = "havik-status__msg", n.textContent = e === "ended" ? this.endedMessage : e === "waiting" ? "Starting soon…" : this.errorMessage ?? this.lastError?.message ?? "Playback unavailable", t.append(n), e === "error" && this.lastError && !
|
|
1506
|
+
if (n.className = "havik-status__msg", n.textContent = e === "ended" ? this.endedMessage : e === "waiting" ? "Starting soon…" : this.errorMessage ?? this.lastError?.message ?? "Playback unavailable", t.append(n), e === "error" && this.lastError && !We.has(this.lastError.code)) {
|
|
1315
1507
|
let e = this.doc.createElement("button");
|
|
1316
1508
|
e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${J("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), t.append(e);
|
|
1317
1509
|
}
|
|
@@ -1402,17 +1594,17 @@ var Re = class {
|
|
|
1402
1594
|
renderQualityMenu() {
|
|
1403
1595
|
let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
|
|
1404
1596
|
n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
|
|
1405
|
-
for (let n of e) this.menu.append(this.menuItem(
|
|
1597
|
+
for (let n of e) this.menu.append(this.menuItem(qe(n), t === n.index, () => this.player.setQuality(n.index)));
|
|
1406
1598
|
}
|
|
1407
1599
|
renderAudioMenu() {
|
|
1408
1600
|
let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
|
|
1409
1601
|
t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
|
|
1410
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
1602
|
+
for (let t of e) this.menu.append(this.menuItem(Je(t), t.default, () => this.player.setAudioTrack(t.id)));
|
|
1411
1603
|
}
|
|
1412
1604
|
renderCcMenu() {
|
|
1413
1605
|
let e = this.player.getTextTracks(), t = this.doc.createElement("div");
|
|
1414
1606
|
t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
|
|
1415
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
1607
|
+
for (let t of e) this.menu.append(this.menuItem(Ye(t), !1, () => this.player.setTextTrack(t.id)));
|
|
1416
1608
|
}
|
|
1417
1609
|
onTracksChanged() {
|
|
1418
1610
|
this.openMenu && this.renderMenu(this.openMenu);
|
|
@@ -1518,27 +1710,27 @@ function Y(e) {
|
|
|
1518
1710
|
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");
|
|
1519
1711
|
return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
|
|
1520
1712
|
}
|
|
1521
|
-
function
|
|
1713
|
+
function qe(e) {
|
|
1522
1714
|
return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
|
|
1523
1715
|
}
|
|
1524
|
-
function
|
|
1716
|
+
function Je(e) {
|
|
1525
1717
|
return e.name || e.lang || `Track ${e.id}`;
|
|
1526
1718
|
}
|
|
1527
|
-
function
|
|
1719
|
+
function Ye(e) {
|
|
1528
1720
|
return e.name || e.lang || `Track ${e.id}`;
|
|
1529
1721
|
}
|
|
1530
1722
|
//#endregion
|
|
1531
1723
|
//#region src/managed/index.ts
|
|
1532
|
-
var
|
|
1724
|
+
var Xe = 6e3, X = 45e3, Z = 2e3, Ze = 3, Qe = 2e3, $e = 480 * 1e3;
|
|
1533
1725
|
async function Q(e) {
|
|
1534
1726
|
if (!e.video || typeof e.video.play != "function") throw new t("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
|
|
1535
1727
|
if (!e.baseUrl) throw new t("INTERNAL", 0, "createPlayer: baseUrl is required");
|
|
1536
1728
|
if (!e.matchUrn) throw new t("INVALID_URN", 0, "createPlayer: matchUrn is required");
|
|
1537
1729
|
if (!e.credential) throw new t("INTERNAL", 0, "createPlayer: credential is required");
|
|
1538
|
-
let n = new
|
|
1730
|
+
let n = new et(e);
|
|
1539
1731
|
return await n.start(), n;
|
|
1540
1732
|
}
|
|
1541
|
-
var
|
|
1733
|
+
var et = class {
|
|
1542
1734
|
opts;
|
|
1543
1735
|
abort = new AbortController();
|
|
1544
1736
|
listeners = {};
|
|
@@ -1559,6 +1751,8 @@ var Ke = class {
|
|
|
1559
1751
|
armSseTimer = null;
|
|
1560
1752
|
armFallbackCtrl = null;
|
|
1561
1753
|
_lastError = null;
|
|
1754
|
+
analyticsSession = null;
|
|
1755
|
+
detachSeekListener;
|
|
1562
1756
|
constructor(e) {
|
|
1563
1757
|
this.opts = e;
|
|
1564
1758
|
}
|
|
@@ -1648,13 +1842,13 @@ var Ke = class {
|
|
|
1648
1842
|
!a && !f() && m();
|
|
1649
1843
|
}, e);
|
|
1650
1844
|
};
|
|
1651
|
-
g(
|
|
1845
|
+
g(Xe), this.armingSub = C({
|
|
1652
1846
|
eventsBaseUrl: n,
|
|
1653
1847
|
matchUrn: this.opts.matchUrn,
|
|
1654
1848
|
credential: this.opts.credential,
|
|
1655
1849
|
onAlive: () => g(X),
|
|
1656
1850
|
onState: (e) => {
|
|
1657
|
-
a || f() || (e.state === "live" || e.state === "gone") && setTimeout(h, Math.random() *
|
|
1851
|
+
a || f() || (e.state === "live" || e.state === "gone") && setTimeout(h, Math.random() * Z);
|
|
1658
1852
|
},
|
|
1659
1853
|
onError: () => m()
|
|
1660
1854
|
});
|
|
@@ -1677,7 +1871,7 @@ var Ke = class {
|
|
|
1677
1871
|
let n = await a(this.opts.credential);
|
|
1678
1872
|
if (this.destroyed || t !== this.loadGen) return;
|
|
1679
1873
|
this.setState("loading");
|
|
1680
|
-
let r = this.createEngine(e, n), i =
|
|
1874
|
+
let r = this.createEngine(e, n), i = I(this.opts.video, (e) => {
|
|
1681
1875
|
this._state === "ended" || this._state === "error" || this.setState(e);
|
|
1682
1876
|
});
|
|
1683
1877
|
if (await r.load(), this.destroyed || t !== this.loadGen) {
|
|
@@ -1688,7 +1882,32 @@ var Ke = class {
|
|
|
1688
1882
|
i(), r.destroy();
|
|
1689
1883
|
return;
|
|
1690
1884
|
}
|
|
1691
|
-
this.engine = r, this.detachMedia = i, this.opts.maxBitrate != null && r.setMaxBitrate(this.opts.maxBitrate), this.startStats(), this.scheduleLicenseRefresh(), this.startLiveState(t);
|
|
1885
|
+
this.engine = r, this.detachMedia = i, this.opts.maxBitrate != null && r.setMaxBitrate(this.opts.maxBitrate), this.startStats(), this.scheduleLicenseRefresh(), this.startLiveState(t), this.startAnalytics(e, r);
|
|
1886
|
+
}
|
|
1887
|
+
startAnalytics(e, t) {
|
|
1888
|
+
if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachSeekListener?.(), this.opts.analytics === !1) return;
|
|
1889
|
+
let n = e.analytics?.sid;
|
|
1890
|
+
if (!n) return;
|
|
1891
|
+
let r = (typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0) ?? Le(this.opts.baseUrl);
|
|
1892
|
+
if (!r) return;
|
|
1893
|
+
let i = this.opts.video, a = new Re({
|
|
1894
|
+
endpoint: r,
|
|
1895
|
+
sid: n,
|
|
1896
|
+
player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
|
|
1897
|
+
sdkVersion: ze,
|
|
1898
|
+
sample: () => {
|
|
1899
|
+
let e = this.engine?.getStats() ?? P, t = i.buffered.length ? Math.max(0, i.buffered.end(i.buffered.length - 1) - i.currentTime) : 0;
|
|
1900
|
+
return {
|
|
1901
|
+
...e,
|
|
1902
|
+
bufferedMs: Math.round(t * 1e3),
|
|
1903
|
+
state: this._state,
|
|
1904
|
+
positionMs: Math.round(i.currentTime * 1e3)
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
});
|
|
1908
|
+
this.analyticsSession = a;
|
|
1909
|
+
let o = () => a.noteSeek();
|
|
1910
|
+
i.addEventListener("seeking", o), this.detachSeekListener = () => i.removeEventListener("seeking", o), a.start();
|
|
1692
1911
|
}
|
|
1693
1912
|
createEngine(e, n) {
|
|
1694
1913
|
let r = { ...this.opts.hlsConfig };
|
|
@@ -1705,17 +1924,17 @@ var Ke = class {
|
|
|
1705
1924
|
hlsConfig: r,
|
|
1706
1925
|
emit: (e) => this.onEngineEvent(e)
|
|
1707
1926
|
}, a = this.opts.engine ?? "auto";
|
|
1708
|
-
if (a === "native") return new
|
|
1709
|
-
if (a === "hls") return new
|
|
1710
|
-
if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl &&
|
|
1711
|
-
if (
|
|
1712
|
-
if (
|
|
1927
|
+
if (a === "native") return new z(i);
|
|
1928
|
+
if (a === "hls") return new V(i);
|
|
1929
|
+
if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && L(this.opts.video) && R()) return new z(i);
|
|
1930
|
+
if (be()) return new V(i);
|
|
1931
|
+
if (L(this.opts.video)) return new z(i);
|
|
1713
1932
|
throw new t("INTERNAL", 0, "no supported HLS playback engine in this browser");
|
|
1714
1933
|
}
|
|
1715
1934
|
onEngineEvent(e) {
|
|
1716
1935
|
if (!this.destroyed) switch (e.type) {
|
|
1717
1936
|
case "ready":
|
|
1718
|
-
this.emit("ready"), this.opts.autoplay !== !1 && this.play();
|
|
1937
|
+
this.analyticsSession?.noteReady(), this.emit("ready"), this.opts.autoplay !== !1 && this.play();
|
|
1719
1938
|
break;
|
|
1720
1939
|
case "lowLatencyDetected":
|
|
1721
1940
|
this._descriptor && (this._descriptor.isLowLatency = e.value);
|
|
@@ -1733,7 +1952,7 @@ var Ke = class {
|
|
|
1733
1952
|
this.onStreamEnded();
|
|
1734
1953
|
break;
|
|
1735
1954
|
case "error":
|
|
1736
|
-
e.fatal ? this.fail(e.error) : this.emit("warning", e.error);
|
|
1955
|
+
this.analyticsSession?.noteError(e.error.code, e.error.message, e.fatal, e.error.httpStatus ?? 0, Math.round(this.opts.video.currentTime * 1e3)), e.fatal ? this.fail(e.error) : this.emit("warning", e.error);
|
|
1737
1956
|
break;
|
|
1738
1957
|
}
|
|
1739
1958
|
}
|
|
@@ -1754,7 +1973,7 @@ var Ke = class {
|
|
|
1754
1973
|
r = 0, i = !1;
|
|
1755
1974
|
},
|
|
1756
1975
|
onError: () => {
|
|
1757
|
-
this.destroyed || e !== this.loadGen || i || (r += 1, r >=
|
|
1976
|
+
this.destroyed || e !== this.loadGen || i || (r += 1, r >= Ze && (i = !0, this.emit("warning", new t("NETWORK", 0, `live-state events unreachable at ${n} after ${r} attempts — end-of-stream detection degraded to the HLS staleness watchdog. Check the events host, the api-key's allowed origins, and CSP connect-src.`))));
|
|
1758
1977
|
}
|
|
1759
1978
|
});
|
|
1760
1979
|
}
|
|
@@ -1776,7 +1995,7 @@ var Ke = class {
|
|
|
1776
1995
|
}
|
|
1777
1996
|
t.controls = !1;
|
|
1778
1997
|
let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
|
|
1779
|
-
r && r.insertBefore(n, t), n.appendChild(t), K(n, this.opts.theme), this.container = n, this.controlBar =
|
|
1998
|
+
r && r.insertBefore(n, t), n.appendChild(t), K(n, this.opts.theme), this.container = n, this.controlBar = Ge({
|
|
1780
1999
|
root: n,
|
|
1781
2000
|
video: t,
|
|
1782
2001
|
player: this,
|
|
@@ -1799,9 +2018,11 @@ var Ke = class {
|
|
|
1799
2018
|
}
|
|
1800
2019
|
startStats() {
|
|
1801
2020
|
this.stopStats();
|
|
1802
|
-
let e = Math.max(500, this.opts.statsIntervalMs ??
|
|
2021
|
+
let e = Math.max(500, this.opts.statsIntervalMs ?? Qe);
|
|
1803
2022
|
this.statsTimer = setInterval(() => {
|
|
1804
|
-
|
|
2023
|
+
if (!this.engine) return;
|
|
2024
|
+
let e = this.engine.getStats();
|
|
2025
|
+
(e.bytesLoaded ?? 0) > 0 && this.analyticsSession?.noteFirstBytes(), this.emit("stats", e);
|
|
1805
2026
|
}, e);
|
|
1806
2027
|
}
|
|
1807
2028
|
stopStats() {
|
|
@@ -1809,7 +2030,7 @@ var Ke = class {
|
|
|
1809
2030
|
}
|
|
1810
2031
|
scheduleLicenseRefresh() {
|
|
1811
2032
|
this.stopRefresh();
|
|
1812
|
-
let e = this.opts.licenseRefreshMs ??
|
|
2033
|
+
let e = this.opts.licenseRefreshMs ?? $e;
|
|
1813
2034
|
!e || !this._descriptor?.drmEnabled || (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
|
|
1814
2035
|
}
|
|
1815
2036
|
stopRefresh() {
|
|
@@ -1846,7 +2067,7 @@ var Ke = class {
|
|
|
1846
2067
|
this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
|
|
1847
2068
|
}
|
|
1848
2069
|
getStats() {
|
|
1849
|
-
return this.engine?.getStats() ??
|
|
2070
|
+
return this.engine?.getStats() ?? P;
|
|
1850
2071
|
}
|
|
1851
2072
|
getQualityLevels() {
|
|
1852
2073
|
return this.engine?.getQualityLevels() ?? [];
|
|
@@ -1907,16 +2128,22 @@ var Ke = class {
|
|
|
1907
2128
|
return n.add(t), () => n.delete(t);
|
|
1908
2129
|
}
|
|
1909
2130
|
destroy() {
|
|
1910
|
-
this.destroyed || (this.destroyed = !0, this.loadGen += 1, this.abort.abort(), this.stopStats(), this.stopRefresh(), this.unmountControls(), this.closeArming(), this.closeLiveState(), this.detachMedia?.(), this.detachPip?.(), this.engine?.destroy(), this.engine = null, this.setState("idle"), this.clearListeners());
|
|
2131
|
+
this.destroyed || (this.destroyed = !0, this.loadGen += 1, this.abort.abort(), this.stopStats(), this.stopRefresh(), this.unmountControls(), this.closeArming(), this.closeLiveState(), this.detachMedia?.(), this.detachPip?.(), this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachSeekListener?.(), this.engine?.destroy(), this.engine = null, this.setState("idle"), this.clearListeners());
|
|
1911
2132
|
}
|
|
1912
2133
|
clearListeners() {
|
|
1913
2134
|
for (let e of Object.keys(this.listeners)) this.listeners[e]?.clear();
|
|
1914
2135
|
}
|
|
1915
2136
|
fail(e) {
|
|
1916
|
-
this._lastError = e, this.setState("error"), this.emit("error", e);
|
|
2137
|
+
this._lastError = e, this.setState("error"), this.analyticsSession?.noteFatal(), this.emit("error", e);
|
|
1917
2138
|
}
|
|
1918
2139
|
setState(e) {
|
|
1919
|
-
if (e
|
|
2140
|
+
if (e === this._state) return;
|
|
2141
|
+
let t = this._state;
|
|
2142
|
+
if (this._state = e, this.analyticsSession) {
|
|
2143
|
+
let n = this.opts.video;
|
|
2144
|
+
this.analyticsSession.noteStateChange(t, e, Math.round(n.currentTime * 1e3), this.engine?.getStats().levelHeight), e === "ended" && this.analyticsSession.noteEnded();
|
|
2145
|
+
}
|
|
2146
|
+
switch (this.emit("statechange", e), e) {
|
|
1920
2147
|
case "playing":
|
|
1921
2148
|
this.emit("playing");
|
|
1922
2149
|
break;
|
|
@@ -1947,7 +2174,7 @@ function $(e) {
|
|
|
1947
2174
|
}
|
|
1948
2175
|
//#endregion
|
|
1949
2176
|
//#region src/embed/host.ts
|
|
1950
|
-
function
|
|
2177
|
+
function tt(e) {
|
|
1951
2178
|
if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
|
|
1952
2179
|
if (!e.src) throw Error("mountEmbed: opts.src is required");
|
|
1953
2180
|
if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
|
|
@@ -2006,8 +2233,8 @@ function qe(e) {
|
|
|
2006
2233
|
}
|
|
2007
2234
|
//#endregion
|
|
2008
2235
|
//#region src/embed/page.ts
|
|
2009
|
-
var
|
|
2010
|
-
function
|
|
2236
|
+
var nt = /^[A-Za-z0-9:_.-]{1,128}$/;
|
|
2237
|
+
function rt() {
|
|
2011
2238
|
let e = window.HAVIK_EMBED_CONFIG;
|
|
2012
2239
|
if (e) return {
|
|
2013
2240
|
baseUrl: e.baseUrl ?? "",
|
|
@@ -2029,9 +2256,9 @@ function Ye() {
|
|
|
2029
2256
|
waitForLive: !0
|
|
2030
2257
|
};
|
|
2031
2258
|
}
|
|
2032
|
-
async function
|
|
2033
|
-
let e =
|
|
2034
|
-
|
|
2259
|
+
async function it() {
|
|
2260
|
+
let e = rt();
|
|
2261
|
+
ot();
|
|
2035
2262
|
let n = document.createElement("video");
|
|
2036
2263
|
n.setAttribute("playsinline", ""), n.muted = e.muted ?? !0, n.style.cssText = "width:100%;height:100%;object-fit:contain;background:#000;aspect-ratio:auto", document.body.appendChild(n);
|
|
2037
2264
|
let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
|
|
@@ -2086,7 +2313,7 @@ async function Xe() {
|
|
|
2086
2313
|
code: e.code,
|
|
2087
2314
|
message: e.message
|
|
2088
2315
|
})), e.on("autoplayblocked", () => {
|
|
2089
|
-
i({ type: "oddin:autoplayblocked" }),
|
|
2316
|
+
i({ type: "oddin:autoplayblocked" }), at(n, e);
|
|
2090
2317
|
});
|
|
2091
2318
|
}, s = async (r) => {
|
|
2092
2319
|
a?.destroy(), a = null;
|
|
@@ -2152,7 +2379,7 @@ async function Xe() {
|
|
|
2152
2379
|
a?.retry();
|
|
2153
2380
|
break;
|
|
2154
2381
|
case "oddin:load":
|
|
2155
|
-
typeof e.matchUrn == "string" &&
|
|
2382
|
+
typeof e.matchUrn == "string" && nt.test(e.matchUrn) && s(e.matchUrn);
|
|
2156
2383
|
break;
|
|
2157
2384
|
case "oddin:destroy":
|
|
2158
2385
|
c();
|
|
@@ -2166,18 +2393,18 @@ async function Xe() {
|
|
|
2166
2393
|
protocol: 1
|
|
2167
2394
|
});
|
|
2168
2395
|
}
|
|
2169
|
-
function
|
|
2396
|
+
function at(e, t) {
|
|
2170
2397
|
if (document.getElementById("havik-tap")) return;
|
|
2171
2398
|
let n = document.createElement("button");
|
|
2172
2399
|
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", () => {
|
|
2173
2400
|
n.remove(), t.play();
|
|
2174
2401
|
}), document.body.appendChild(n);
|
|
2175
2402
|
}
|
|
2176
|
-
function
|
|
2403
|
+
function ot() {
|
|
2177
2404
|
let e = document.createElement("style");
|
|
2178
2405
|
e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
|
|
2179
2406
|
}
|
|
2180
2407
|
//#endregion
|
|
2181
|
-
export { W as ODDIN_THEME, t as PlaybackError, K as applyTheme,
|
|
2408
|
+
export { W as ODDIN_THEME, t as PlaybackError, K as applyTheme, it as bootEmbed, fe as clearDeviceId, Q as createPlayer, w as deriveEventsBaseUrl, j as detectDrmSupport, x as fetchCatalog, ce as fetchTournament, ue as flattenMatches, D as getDeviceId, N as isLowLatencyManifest, $ as isOddinMessage, me as licenseRequestHeaders, tt as mountEmbed, _ as pollToLive, y as resolveStream, C as subscribeLiveState, de as watchStatus };
|
|
2182
2409
|
|
|
2183
2410
|
//# sourceMappingURL=index.js.map
|