@oddin-gg/havik-player 2.13.1 → 2.15.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 +4 -3
- package/dist/havik-player.global.js.map +1 -1
- package/dist/index.cjs +5 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +181 -144
- package/dist/index.js.map +1 -1
- package/docs/API.md +26 -1
- package/embed/index.html +1 -1
- package/examples/vanilla.html +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -174,7 +174,7 @@ function ue(e, t) {
|
|
|
174
174
|
t?.addEventListener("abort", a, { once: !0 });
|
|
175
175
|
});
|
|
176
176
|
}
|
|
177
|
-
async function
|
|
177
|
+
async function v(e, t, r) {
|
|
178
178
|
let i = ae(t), a = Date.now(), s = i.timeoutMs == null ? Infinity : a + i.timeoutMs, c = 0, l;
|
|
179
179
|
for (;;) {
|
|
180
180
|
if (r?.aborted) throw new n("ABORTED", 0, "wait-for-live aborted");
|
|
@@ -200,21 +200,21 @@ async function de(e, t, r) {
|
|
|
200
200
|
}
|
|
201
201
|
//#endregion
|
|
202
202
|
//#region src/core/resolve.ts
|
|
203
|
-
async function
|
|
203
|
+
async function y(e) {
|
|
204
204
|
if (!e.matchUrn || typeof e.matchUrn != "string") throw new n("INVALID_URN", 0, "resolveStream: a non-empty matchUrn is required");
|
|
205
205
|
let t = await p(d(e.baseUrl, `/v1/playback/${encodeURIComponent(e.matchUrn)}`), {
|
|
206
206
|
credential: e.credential,
|
|
207
207
|
signal: e.signal,
|
|
208
208
|
apiHeaders: e.apiHeaders
|
|
209
209
|
});
|
|
210
|
-
if (!t.ok) throw await
|
|
211
|
-
return
|
|
210
|
+
if (!t.ok) throw await de(t);
|
|
211
|
+
return me(await t.json(), e.matchUrn);
|
|
212
212
|
}
|
|
213
|
-
function
|
|
214
|
-
let t = () =>
|
|
215
|
-
return e.waitForLive ?
|
|
213
|
+
function b(e) {
|
|
214
|
+
let t = () => y(e);
|
|
215
|
+
return e.waitForLive ? v(t, e.waitForLive, e.signal) : t();
|
|
216
216
|
}
|
|
217
|
-
async function
|
|
217
|
+
async function de(e) {
|
|
218
218
|
let t = f(e.headers.get("retry-after")), i = e.headers.get("x-request-id") ?? e.headers.get("x-amzn-requestid") ?? void 0, a = e.statusText || `HTTP ${e.status}`, o, s;
|
|
219
219
|
try {
|
|
220
220
|
let t = await e.json();
|
|
@@ -230,7 +230,7 @@ async function fe(e) {
|
|
|
230
230
|
liveStartsAtMs: s
|
|
231
231
|
});
|
|
232
232
|
}
|
|
233
|
-
var
|
|
233
|
+
var fe = /* @__PURE__ */ new Set([
|
|
234
234
|
"matchUrn",
|
|
235
235
|
"protocol",
|
|
236
236
|
"drmEnabled",
|
|
@@ -241,7 +241,7 @@ var pe = /* @__PURE__ */ new Set([
|
|
|
241
241
|
"expiresAt",
|
|
242
242
|
"analytics"
|
|
243
243
|
]);
|
|
244
|
-
function
|
|
244
|
+
function x(e, t) {
|
|
245
245
|
let r;
|
|
246
246
|
try {
|
|
247
247
|
r = new URL(t);
|
|
@@ -251,7 +251,7 @@ function b(e, t) {
|
|
|
251
251
|
if (r.protocol !== "https:" && r.protocol !== "http:") throw new n("INTERNAL", 0, `playback ${e} has a disallowed scheme: ${r.protocol}`);
|
|
252
252
|
return t;
|
|
253
253
|
}
|
|
254
|
-
function
|
|
254
|
+
function pe(e) {
|
|
255
255
|
if (e !== void 0) {
|
|
256
256
|
if (typeof e == "string" && !e.endsWith("/")) try {
|
|
257
257
|
let t = new URL(e);
|
|
@@ -260,41 +260,41 @@ function me(e) {
|
|
|
260
260
|
g(`server analytics.endpoint ${JSON.stringify(e)} ignored: not a bare https origin.`);
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
|
-
function
|
|
263
|
+
function me(e, t) {
|
|
264
264
|
if (!e.manifestUrl) throw new n("INTERNAL", 0, "playback response is missing manifestUrl");
|
|
265
265
|
let r = e.drmEnabled ?? !!e.drm, i = {
|
|
266
266
|
matchUrn: e.matchUrn ?? t,
|
|
267
267
|
protocol: "hls",
|
|
268
268
|
drmEnabled: r,
|
|
269
|
-
manifestUrl:
|
|
269
|
+
manifestUrl: x("manifestUrl", e.manifestUrl),
|
|
270
270
|
serverTime: e.serverTime ?? ""
|
|
271
271
|
};
|
|
272
272
|
if (e.liveStartsAt && (i.liveStartsAt = e.liveStartsAt), typeof e.analytics?.sid == "string" && e.analytics.sid) {
|
|
273
273
|
i.analytics = { sid: e.analytics.sid };
|
|
274
|
-
let t =
|
|
274
|
+
let t = pe(e.analytics.endpoint);
|
|
275
275
|
t && (i.analytics.endpoint = t);
|
|
276
276
|
}
|
|
277
277
|
if (r && e.drm) {
|
|
278
278
|
let t = {};
|
|
279
|
-
e.drm.widevine?.licenseUrl && (t.widevine = { licenseUrl:
|
|
280
|
-
licenseUrl:
|
|
281
|
-
certificateUrl:
|
|
279
|
+
e.drm.widevine?.licenseUrl && (t.widevine = { licenseUrl: x("widevine licenseUrl", e.drm.widevine.licenseUrl) }), e.drm.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && (t.fairplay = {
|
|
280
|
+
licenseUrl: x("fairplay licenseUrl", e.drm.fairplay.licenseUrl),
|
|
281
|
+
certificateUrl: x("fairplay certificateUrl", e.drm.fairplay.certificateUrl)
|
|
282
282
|
}), (t.widevine || t.fairplay) && (i.drm = t);
|
|
283
283
|
}
|
|
284
284
|
let a = {};
|
|
285
|
-
for (let [t, n] of Object.entries(e))
|
|
285
|
+
for (let [t, n] of Object.entries(e)) fe.has(t) || (a[t] = n);
|
|
286
286
|
return Object.keys(a).length > 0 && (i.extensions = a), i;
|
|
287
287
|
}
|
|
288
288
|
//#endregion
|
|
289
289
|
//#region src/core/fullscreen.ts
|
|
290
|
-
function
|
|
290
|
+
function he(e) {
|
|
291
291
|
return e.ownerDocument ?? document;
|
|
292
292
|
}
|
|
293
|
-
function
|
|
293
|
+
function ge(e) {
|
|
294
294
|
return e.fullscreenElement ?? e.webkitFullscreenElement ?? null;
|
|
295
295
|
}
|
|
296
296
|
function S(e, t) {
|
|
297
|
-
let n =
|
|
297
|
+
let n = ge(he(e));
|
|
298
298
|
return n != null && (n === e || t != null && n === t) || e.webkitDisplayingFullscreen === !0;
|
|
299
299
|
}
|
|
300
300
|
async function C(e, t) {
|
|
@@ -310,7 +310,7 @@ async function w(e, t) {
|
|
|
310
310
|
n.webkitExitFullscreen?.();
|
|
311
311
|
return;
|
|
312
312
|
}
|
|
313
|
-
let r =
|
|
313
|
+
let r = he(e), i = ge(r);
|
|
314
314
|
i != null && (i === e || t != null && i === t) && (typeof r.exitFullscreen == "function" ? await r.exitFullscreen() : r.webkitExitFullscreen?.());
|
|
315
315
|
}
|
|
316
316
|
//#endregion
|
|
@@ -671,14 +671,14 @@ function I(e) {
|
|
|
671
671
|
function L(e) {
|
|
672
672
|
return (e ?? "").trim().toLowerCase();
|
|
673
673
|
}
|
|
674
|
-
var
|
|
674
|
+
var R = {
|
|
675
675
|
hevc: "hvc1.1.6.L93.90",
|
|
676
676
|
dv: "dvh1.05.06",
|
|
677
677
|
av1: "av01.0.05M.08",
|
|
678
678
|
vp9: "vp09.00.10.08"
|
|
679
|
-
},
|
|
680
|
-
function
|
|
681
|
-
let t = L(e), n =
|
|
679
|
+
}, z = /* @__PURE__ */ new Map(), B = /* @__PURE__ */ new Map();
|
|
680
|
+
function V(e) {
|
|
681
|
+
let t = L(e), n = z.get(t);
|
|
682
682
|
if (n) return n;
|
|
683
683
|
let r = {
|
|
684
684
|
initDataTypes: ["cenc"],
|
|
@@ -696,23 +696,23 @@ function Ne(e) {
|
|
|
696
696
|
} catch {
|
|
697
697
|
return !1;
|
|
698
698
|
}
|
|
699
|
-
})().then((e) => (
|
|
700
|
-
return
|
|
699
|
+
})().then((e) => (B.set(t, e), e));
|
|
700
|
+
return z.set(t, i), i;
|
|
701
701
|
}
|
|
702
|
-
function
|
|
703
|
-
if (j()) for (let e of Object.values(
|
|
702
|
+
function je() {
|
|
703
|
+
if (j()) for (let e of Object.values(R)) V(e);
|
|
704
704
|
}
|
|
705
|
-
function
|
|
705
|
+
function H(e) {
|
|
706
706
|
let t = I(e);
|
|
707
|
-
if (t) return t === "avc" ? !0 :
|
|
707
|
+
if (t) return t === "avc" ? !0 : B.get(L(e)) ?? B.get(L(R[t]));
|
|
708
708
|
}
|
|
709
709
|
//#endregion
|
|
710
710
|
//#region src/core/drm/support.ts
|
|
711
|
-
var
|
|
711
|
+
var Me = 5e3, U = "video/mp4; codecs=\"avc1.42E01E\"", Ne = "audio/mp4; codecs=\"mp4a.40.2\"", Pe = {
|
|
712
712
|
widevine: ["com.widevine.alpha"],
|
|
713
713
|
fairplay: ["com.apple.fps"]
|
|
714
714
|
};
|
|
715
|
-
function
|
|
715
|
+
function Fe(e) {
|
|
716
716
|
return e === "fairplay" ? [{
|
|
717
717
|
initDataTypes: [
|
|
718
718
|
"sinf",
|
|
@@ -720,30 +720,30 @@ function Be(e) {
|
|
|
720
720
|
"cenc"
|
|
721
721
|
],
|
|
722
722
|
videoCapabilities: [{
|
|
723
|
-
contentType:
|
|
723
|
+
contentType: U,
|
|
724
724
|
robustness: "",
|
|
725
725
|
encryptionScheme: "cbcs"
|
|
726
726
|
}],
|
|
727
727
|
audioCapabilities: [{
|
|
728
|
-
contentType:
|
|
728
|
+
contentType: Ne,
|
|
729
729
|
robustness: "",
|
|
730
730
|
encryptionScheme: "cbcs"
|
|
731
731
|
}]
|
|
732
732
|
}] : N("com.widevine.alpha", [{
|
|
733
733
|
initDataTypes: ["cenc"],
|
|
734
734
|
videoCapabilities: [{
|
|
735
|
-
contentType:
|
|
735
|
+
contentType: U,
|
|
736
736
|
robustness: F.video,
|
|
737
737
|
encryptionScheme: F.scheme
|
|
738
738
|
}],
|
|
739
739
|
audioCapabilities: [{
|
|
740
|
-
contentType:
|
|
740
|
+
contentType: Ne,
|
|
741
741
|
robustness: F.audio,
|
|
742
742
|
encryptionScheme: F.scheme
|
|
743
743
|
}]
|
|
744
744
|
}]);
|
|
745
745
|
}
|
|
746
|
-
function
|
|
746
|
+
function Ie(e, t) {
|
|
747
747
|
return new Promise((n, r) => {
|
|
748
748
|
let i = setTimeout(() => r(/* @__PURE__ */ Error("probe-timeout")), t);
|
|
749
749
|
e.then((e) => {
|
|
@@ -753,8 +753,8 @@ function Ve(e, t) {
|
|
|
753
753
|
});
|
|
754
754
|
});
|
|
755
755
|
}
|
|
756
|
-
async function
|
|
757
|
-
let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ??
|
|
756
|
+
async function Le(e = {}) {
|
|
757
|
+
let t = e.systems ?? ["widevine", "fairplay"], n = e.timeoutMs ?? Me, r = {};
|
|
758
758
|
if (typeof window < "u" && window.isSecureContext === !1) return {
|
|
759
759
|
verdict: "insecure-context",
|
|
760
760
|
detail: r
|
|
@@ -766,7 +766,7 @@ async function He(e = {}) {
|
|
|
766
766
|
};
|
|
767
767
|
let a = !1, o = !1, s = Date.now() + n, c = () => Math.max(1, s - Date.now());
|
|
768
768
|
for (let e of t) {
|
|
769
|
-
let t =
|
|
769
|
+
let t = Pe[e];
|
|
770
770
|
if (!t) {
|
|
771
771
|
r[e] = "unknown system";
|
|
772
772
|
continue;
|
|
@@ -777,7 +777,7 @@ async function He(e = {}) {
|
|
|
777
777
|
break;
|
|
778
778
|
}
|
|
779
779
|
try {
|
|
780
|
-
return await
|
|
780
|
+
return await Ie((await Ie(i(n, Fe(e)), c())).createMediaKeys(), c()), r[e] = `ok (${n})`, {
|
|
781
781
|
verdict: "supported",
|
|
782
782
|
system: e,
|
|
783
783
|
detail: r
|
|
@@ -801,49 +801,49 @@ async function He(e = {}) {
|
|
|
801
801
|
}
|
|
802
802
|
//#endregion
|
|
803
803
|
//#region src/core/manifest.ts
|
|
804
|
-
var
|
|
805
|
-
function
|
|
806
|
-
return
|
|
804
|
+
var Re = /^#EXT-X-(PART-INF|PART|PRELOAD-HINT)[:\s]/m;
|
|
805
|
+
function ze(e) {
|
|
806
|
+
return Re.test(e);
|
|
807
807
|
}
|
|
808
808
|
//#endregion
|
|
809
809
|
//#region src/core/env.ts
|
|
810
|
-
var
|
|
810
|
+
var Be = {
|
|
811
811
|
integration: "https://feed-dev.oddin-video.gg",
|
|
812
812
|
production: "https://feed.oddin-video.gg"
|
|
813
813
|
};
|
|
814
|
-
function
|
|
815
|
-
let t = Object.prototype.hasOwnProperty.call(
|
|
814
|
+
function Ve(e) {
|
|
815
|
+
let t = Object.prototype.hasOwnProperty.call(Be, e) ? Be[e] : void 0;
|
|
816
816
|
if (!t) throw new n("INTERNAL", 0, `unknown env ${JSON.stringify(e)} — expected 'integration' or 'production'`);
|
|
817
817
|
return t;
|
|
818
818
|
}
|
|
819
819
|
//#endregion
|
|
820
820
|
//#region src/core/stats.ts
|
|
821
|
-
var
|
|
822
|
-
function
|
|
821
|
+
var He = { droppedFrames: 0 };
|
|
822
|
+
function Ue(e) {
|
|
823
823
|
try {
|
|
824
824
|
return new URL(e).host;
|
|
825
825
|
} catch {
|
|
826
826
|
return "";
|
|
827
827
|
}
|
|
828
828
|
}
|
|
829
|
-
var
|
|
830
|
-
function
|
|
829
|
+
var We = /\.(m3u8|ts|m4s|mp4|cmfv|cmfa)(\?|$)/i;
|
|
830
|
+
function Ge(e) {
|
|
831
831
|
if (typeof performance > "u" || typeof performance.getEntriesByType != "function") return "";
|
|
832
832
|
let t = "", n = -1, r = performance.getEntriesByType("resource");
|
|
833
|
-
for (let i of r) if (!(i.startTime < e) && (i.initiatorType === "video" || i.initiatorType === "audio" ||
|
|
834
|
-
let e =
|
|
833
|
+
for (let i of r) if (!(i.startTime < e) && (i.initiatorType === "video" || i.initiatorType === "audio" || We.test(i.name)) && i.startTime >= n) {
|
|
834
|
+
let e = Ue(i.name);
|
|
835
835
|
e && (t = e, n = i.startTime);
|
|
836
836
|
}
|
|
837
837
|
return t;
|
|
838
838
|
}
|
|
839
|
-
function
|
|
839
|
+
function W(e) {
|
|
840
840
|
let t = e.getVideoPlaybackQuality?.();
|
|
841
841
|
return {
|
|
842
842
|
dropped: t?.droppedVideoFrames ?? 0,
|
|
843
843
|
totalFrames: t?.totalVideoFrames ?? 0
|
|
844
844
|
};
|
|
845
845
|
}
|
|
846
|
-
function
|
|
846
|
+
function Ke(e, t) {
|
|
847
847
|
let n = [
|
|
848
848
|
["playing", () => t("playing")],
|
|
849
849
|
["waiting", () => t("buffering")],
|
|
@@ -860,7 +860,7 @@ function qe(e, t) {
|
|
|
860
860
|
}
|
|
861
861
|
//#endregion
|
|
862
862
|
//#region src/core/engine/native.ts
|
|
863
|
-
function
|
|
863
|
+
function qe(e) {
|
|
864
864
|
try {
|
|
865
865
|
let t = new TextDecoder("utf-8").decode(new Uint8Array(e));
|
|
866
866
|
return /^skd:\/\/[0-9a-fA-F-]{36}:([0-9a-fA-F]{32})$/.exec(t)?.[1];
|
|
@@ -868,10 +868,10 @@ function Je(e) {
|
|
|
868
868
|
return;
|
|
869
869
|
}
|
|
870
870
|
}
|
|
871
|
-
function
|
|
871
|
+
function G(e) {
|
|
872
872
|
return !!(e ?? (typeof document < "u" ? document.createElement("video") : void 0))?.canPlayType("application/vnd.apple.mpegurl");
|
|
873
873
|
}
|
|
874
|
-
function
|
|
874
|
+
function Je() {
|
|
875
875
|
if (typeof window > "u") return !1;
|
|
876
876
|
let e = "WebKitMediaKeys" in window || typeof HTMLMediaElement < "u" && "webkitSetMediaKeys" in HTMLMediaElement.prototype, t = typeof navigator < "u" && /Apple/i.test(navigator.vendor ?? "");
|
|
877
877
|
return e || t;
|
|
@@ -934,7 +934,7 @@ var K = class {
|
|
|
934
934
|
});
|
|
935
935
|
this.onEncrypted = (n) => {
|
|
936
936
|
let i = n;
|
|
937
|
-
i.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV =
|
|
937
|
+
i.initData && !this.fairplayArmed && (this.fairplayArmed = !0, this.fairplayKeyIV = qe(i.initData), (async () => {
|
|
938
938
|
try {
|
|
939
939
|
let n = await (await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
|
|
940
940
|
initDataTypes: [i.initDataType],
|
|
@@ -1029,11 +1029,11 @@ var K = class {
|
|
|
1029
1029
|
e.seekable.length > 0 && (e.currentTime = e.seekable.end(e.seekable.length - 1));
|
|
1030
1030
|
}
|
|
1031
1031
|
getStats() {
|
|
1032
|
-
let e = this.deps.video, t =
|
|
1032
|
+
let e = this.deps.video, t = W(e), n = {
|
|
1033
1033
|
droppedFrames: t.dropped,
|
|
1034
1034
|
decodedFrames: t.totalFrames
|
|
1035
1035
|
};
|
|
1036
|
-
return this.cdnHost ||=
|
|
1036
|
+
return this.cdnHost ||= Ge(this.builtAt), this.cdnHost && (n.cdnHost = this.cdnHost), this.deps.descriptor.drmEnabled && (n.keySystem = "fairplay"), e.duration === Infinity ? (n.isLive = !0, e.seekable.length > 0 && (n.atLiveEdge = e.currentTime >= e.seekable.end(e.seekable.length - 1) - 2)) : Number.isFinite(e.duration) && e.duration > 0 && (n.isLive = !1), n;
|
|
1037
1037
|
}
|
|
1038
1038
|
stop() {
|
|
1039
1039
|
try {
|
|
@@ -1301,7 +1301,7 @@ var kt = class {
|
|
|
1301
1301
|
this.deps = e, this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl, this.loadStarted = e.deferLoad !== !0;
|
|
1302
1302
|
}
|
|
1303
1303
|
async load() {
|
|
1304
|
-
let { descriptor: e, snapToLiveOnRefocus: t, video: r, emit: i } = this.deps, a =
|
|
1304
|
+
let { descriptor: e, snapToLiveOnRefocus: t, video: r, emit: i } = this.deps, a = G(r) && Je(), o = !!e.drm?.widevine?.licenseUrl && !a;
|
|
1305
1305
|
if (e.drmEnabled && !o) {
|
|
1306
1306
|
i({
|
|
1307
1307
|
type: "error",
|
|
@@ -1310,7 +1310,7 @@ var kt = class {
|
|
|
1310
1310
|
});
|
|
1311
1311
|
return;
|
|
1312
1312
|
}
|
|
1313
|
-
e.drmEnabled && o && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && (Ae(r),
|
|
1313
|
+
e.drmEnabled && o && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && (Ae(r), je()), this.startHls(), t !== !1 && typeof document < "u" && (this.onVisibility = () => {
|
|
1314
1314
|
let e = this.hls;
|
|
1315
1315
|
if (document.visibilityState !== "visible" || !e) return;
|
|
1316
1316
|
let t = e.liveSyncPosition;
|
|
@@ -1430,7 +1430,7 @@ var kt = class {
|
|
|
1430
1430
|
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 > Et && this.requestTimesMs.shift());
|
|
1431
1431
|
let a = t?.frag?.url;
|
|
1432
1432
|
if (a) {
|
|
1433
|
-
let e =
|
|
1433
|
+
let e = Ue(a);
|
|
1434
1434
|
e && (this.cdnHost = e);
|
|
1435
1435
|
}
|
|
1436
1436
|
}), f.on(e.Events.LEVEL_LOADED, (e, t) => {
|
|
@@ -1526,9 +1526,9 @@ var kt = class {
|
|
|
1526
1526
|
this.ladderHeights = t.length ? [...new Set(t)].sort((e, t) => e - t) : void 0, this.ladderTopBitrateKbps = e.levels.length ? Math.round(Math.max(...e.levels.map((e) => e.bitrate)) / 1e3) : 0;
|
|
1527
1527
|
}
|
|
1528
1528
|
dropLevelsWidevineCannotPlay(e) {
|
|
1529
|
-
this.removeLevelsWhere(e, (e) =>
|
|
1529
|
+
this.removeLevelsWhere(e, (e) => H(e) !== !0);
|
|
1530
1530
|
let t = new Set(e.levels.map((e) => e.videoCodec).filter((e) => !!e && I(e) !== "avc" && !!I(e)));
|
|
1531
|
-
for (let n of t)
|
|
1531
|
+
for (let n of t) V(n).then((t) => {
|
|
1532
1532
|
t || this.hls !== e || this.removeLevelsWhere(e, (e) => e === n) && (this.measureLadder(e), this.applySizeCap());
|
|
1533
1533
|
});
|
|
1534
1534
|
}
|
|
@@ -1539,7 +1539,7 @@ var kt = class {
|
|
|
1539
1539
|
a && I(a) && t(a) && (r.push(n), i.add(a));
|
|
1540
1540
|
}), !r.length) return !1;
|
|
1541
1541
|
let a = [...i].join(", ");
|
|
1542
|
-
if (!n.some((e, t) => r.includes(t) ? !1 : !I(e.videoCodec) ||
|
|
1542
|
+
if (!n.some((e, t) => r.includes(t) ? !1 : !I(e.videoCodec) || H(e.videoCodec) === !0)) return console.warn(`havik-player: every rendition is ${a}, which Widevine may not play on this browser; keeping them`), !1;
|
|
1543
1543
|
for (let t = r.length - 1; t >= 0; --t) e.removeLevel(r[t]);
|
|
1544
1544
|
return console.info(`havik-player: skipped ${r.length} rendition(s) Widevine cannot play on this browser (${a})`), !0;
|
|
1545
1545
|
}
|
|
@@ -1585,7 +1585,7 @@ var kt = class {
|
|
|
1585
1585
|
e.widevine && (this.licenseUrl = e.widevine);
|
|
1586
1586
|
}
|
|
1587
1587
|
getStats() {
|
|
1588
|
-
let e = this.hls, t =
|
|
1588
|
+
let e = this.hls, t = W(this.deps.video), n = {
|
|
1589
1589
|
droppedFrames: t.dropped,
|
|
1590
1590
|
decodedFrames: t.totalFrames,
|
|
1591
1591
|
lowLatency: this.detectedLowLatency,
|
|
@@ -1646,7 +1646,7 @@ var kt = class {
|
|
|
1646
1646
|
this.resetStarvationWindow();
|
|
1647
1647
|
return;
|
|
1648
1648
|
}
|
|
1649
|
-
let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < ft, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > mt, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } =
|
|
1649
|
+
let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < ft, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > mt, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = W(t), c = o - s, l = c !== this.starvedFrames;
|
|
1650
1650
|
if (this.starvedFrames = c, !a || l) {
|
|
1651
1651
|
(t.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA || l) && this.clearKick(), this.resetStarvationWindow();
|
|
1652
1652
|
return;
|
|
@@ -2260,7 +2260,7 @@ async function $t() {
|
|
|
2260
2260
|
}
|
|
2261
2261
|
//#endregion
|
|
2262
2262
|
//#region src/core/version.ts
|
|
2263
|
-
var en = "2.
|
|
2263
|
+
var en = "2.15.0", tn = {
|
|
2264
2264
|
accent: "#E1B600",
|
|
2265
2265
|
accentText: "#1B1C23",
|
|
2266
2266
|
background: "#0e0f13",
|
|
@@ -2293,15 +2293,16 @@ function rn(e, t) {
|
|
|
2293
2293
|
}
|
|
2294
2294
|
//#endregion
|
|
2295
2295
|
//#region src/ui/styles.ts
|
|
2296
|
-
var an = "havik-player-styles", on = "\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/* Absolutely centred, NOT a flex sibling of .havik-center: the hidden center\n button (.havik-gone) still occupies its 74px in the row, which pushed an\n in-flow spinner 37px right of centre. */\n.havik-spinner{position:absolute;inset:0;margin:auto;width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n";
|
|
2297
|
-
function
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2296
|
+
var an = "havik-player-styles", on = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer;\n touch-action:manipulation}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n/* Absolutely centred, NOT a flex sibling of .havik-center: the hidden center\n button (.havik-gone) still occupies its 74px in the row, which pushed an\n in-flow spinner 37px right of centre. */\n.havik-spinner{position:absolute;inset:0;margin:auto;width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n", sn = /* @__PURE__ */ new WeakMap();
|
|
2297
|
+
function cn(e = document, t) {
|
|
2298
|
+
let n = e.querySelector(`style#${an}`);
|
|
2299
|
+
if (n && (!t || sn.get(n) === t)) return;
|
|
2300
|
+
let r = e.createElement("style");
|
|
2301
|
+
r.id = an, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = on, sn.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
|
|
2301
2302
|
}
|
|
2302
2303
|
//#endregion
|
|
2303
2304
|
//#region src/ui/icons.ts
|
|
2304
|
-
var
|
|
2305
|
+
var ln = {
|
|
2305
2306
|
play: "<path d=\"M8 5v14l11-7z\"/>",
|
|
2306
2307
|
pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
|
|
2307
2308
|
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\"/>",
|
|
@@ -2315,20 +2316,20 @@ var cn = {
|
|
|
2315
2316
|
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\"/>"
|
|
2316
2317
|
};
|
|
2317
2318
|
function Z(e) {
|
|
2318
|
-
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${
|
|
2319
|
+
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${ln[e]}</svg>`;
|
|
2319
2320
|
}
|
|
2320
2321
|
//#endregion
|
|
2321
2322
|
//#region src/ui/controls.ts
|
|
2322
|
-
var
|
|
2323
|
+
var un = 3e3, dn = 250, fn = /* @__PURE__ */ new Set([
|
|
2323
2324
|
"GONE",
|
|
2324
2325
|
"NOT_FOUND",
|
|
2325
2326
|
"INVALID_URN",
|
|
2326
2327
|
"UNAUTHORIZED"
|
|
2327
2328
|
]);
|
|
2328
|
-
function
|
|
2329
|
-
return new
|
|
2329
|
+
function pn(e) {
|
|
2330
|
+
return new mn(e);
|
|
2330
2331
|
}
|
|
2331
|
-
var
|
|
2332
|
+
var mn = class {
|
|
2332
2333
|
root;
|
|
2333
2334
|
video;
|
|
2334
2335
|
player;
|
|
@@ -2366,11 +2367,15 @@ var fn = class {
|
|
|
2366
2367
|
interruptedMessage;
|
|
2367
2368
|
errorMessage;
|
|
2368
2369
|
hideTimer;
|
|
2370
|
+
tapTimer;
|
|
2371
|
+
surfacePointerType = "";
|
|
2372
|
+
surfaceWasHidden = !1;
|
|
2373
|
+
pointerClosedMenu = !1;
|
|
2369
2374
|
openMenu = null;
|
|
2370
2375
|
scrubbing = !1;
|
|
2371
2376
|
offFns = [];
|
|
2372
2377
|
constructor(e) {
|
|
2373
|
-
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ??
|
|
2378
|
+
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? un, 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, cn(this.doc, e.cspNonce), 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");
|
|
2374
2379
|
let t = (e, t, n) => {
|
|
2375
2380
|
let r = this.doc.createElement(e);
|
|
2376
2381
|
if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
|
|
@@ -2419,18 +2424,48 @@ var fn = class {
|
|
|
2419
2424
|
}
|
|
2420
2425
|
wire() {
|
|
2421
2426
|
let e = this.video;
|
|
2422
|
-
this.on(this.playBtn, "click", () => this.togglePlay()), this.on(this.center, "click", () => this.togglePlay()), this.on(this.overlay, "
|
|
2423
|
-
e.
|
|
2427
|
+
this.on(this.playBtn, "click", () => this.togglePlay()), this.on(this.center, "click", () => this.togglePlay()), this.on(this.overlay, "pointerdown", (e) => {
|
|
2428
|
+
this.surfacePointerType = e.pointerType ?? "", this.surfaceWasHidden = this.root.classList.contains("havik-hide");
|
|
2429
|
+
}), this.on(this.overlay, "click", (e) => {
|
|
2430
|
+
e.target === this.overlay && this.onSurfaceClick();
|
|
2424
2431
|
}), this.on(this.muteBtn, "click", () => this.player.setMuted(!e.muted)), this.on(this.volSlider, "input", () => {
|
|
2425
2432
|
let t = Number(this.volSlider.value);
|
|
2426
2433
|
this.setVolumeFill(t), this.player.setVolume(t), t > 0 && e.muted && this.player.setMuted(!1);
|
|
2427
2434
|
}), this.on(this.liveGo, "click", () => this.player.seekToLive()), this.on(this.pipBtn, "click", () => void this.togglePip()), this.on(this.fsBtn, "click", () => void this.toggleFullscreen()), this.on(this.ccBtn, "click", () => this.toggleMenu("cc")), this.on(this.audioBtn, "click", () => this.toggleMenu("audio")), this.on(this.qualityBtn, "click", () => this.toggleMenu("quality")), this.on(this.seek, "pointerdown", (e) => this.onSeekDown(e)), this.on(this.seek, "keydown", (e) => this.onSeekKey(e)), this.on(e, "play", () => this.syncPlayPause()), this.on(e, "pause", () => this.syncPlayPause()), this.on(e, "volumechange", () => this.syncVolume()), this.on(e, "timeupdate", () => this.syncProgress()), this.on(e, "progress", () => this.syncProgress()), this.on(e, "durationchange", () => this.syncProgress()), this.offFns.push(this.player.on("statechange", () => this.syncState())), this.offFns.push(this.player.on("ended", () => this.syncState())), this.offFns.push(this.player.on("ready", () => this.refreshTracks())), this.offFns.push(this.player.on("qualitychange", () => this.onTracksChanged())), this.offFns.push(this.player.on("audiotrackchange", () => this.onTracksChanged())), this.offFns.push(this.player.on("texttrackchange", () => this.onTracksChanged())), this.offFns.push(this.player.on("pipchange", (e) => this.syncPip(e))), this.offFns.push(this.player.on("stats", () => this.syncLive())), this.offFns.push(this.player.on("error", (e) => {
|
|
2428
2435
|
this.lastError = e, this.statusKind = null, this.syncState();
|
|
2429
|
-
}))
|
|
2436
|
+
}));
|
|
2437
|
+
let t = (e) => e.pointerType !== "touch";
|
|
2438
|
+
this.on(this.root, "pointermove", (e) => {
|
|
2439
|
+
t(e) && this.poke();
|
|
2440
|
+
}), this.on(this.root, "pointerleave", (e) => {
|
|
2441
|
+
t(e) && this.scheduleHide(0);
|
|
2442
|
+
}), this.on(this.root, "focusin", () => this.poke()), this.on(this.root, "keydown", (e) => this.onKey(e)), this.on(this.doc, "pointerdown", (e) => this.onDocPointer(e), { capture: !0 }), this.on(this.doc, "fullscreenchange", () => this.syncFullscreen()), this.on(this.doc, "webkitfullscreenchange", () => this.syncFullscreen()), this.on(e, "webkitbeginfullscreen", () => this.syncFullscreen()), this.on(e, "webkitendfullscreen", () => this.syncFullscreen()), this.syncState();
|
|
2430
2443
|
}
|
|
2431
2444
|
togglePlay() {
|
|
2432
2445
|
this.video.paused || this.video.ended ? this.player.play() : this.player.pause();
|
|
2433
2446
|
}
|
|
2447
|
+
onSurfaceClick() {
|
|
2448
|
+
let e = this.surfacePointerType === "touch";
|
|
2449
|
+
if (this.surfacePointerType = "", !e) {
|
|
2450
|
+
this.tapTimer && clearTimeout(this.tapTimer), this.tapTimer = void 0, this.togglePlay();
|
|
2451
|
+
return;
|
|
2452
|
+
}
|
|
2453
|
+
if (this.pointerClosedMenu) {
|
|
2454
|
+
this.tapTimer && clearTimeout(this.tapTimer), this.tapTimer = void 0, this.poke();
|
|
2455
|
+
return;
|
|
2456
|
+
}
|
|
2457
|
+
if (this.tapTimer) {
|
|
2458
|
+
clearTimeout(this.tapTimer), this.tapTimer = void 0, this.togglePlay();
|
|
2459
|
+
return;
|
|
2460
|
+
}
|
|
2461
|
+
let t = this.surfaceWasHidden;
|
|
2462
|
+
this.tapTimer = setTimeout(() => {
|
|
2463
|
+
this.tapTimer = void 0, this.toggleBar(t);
|
|
2464
|
+
}, dn);
|
|
2465
|
+
}
|
|
2466
|
+
toggleBar(e) {
|
|
2467
|
+
e ? this.poke() : this.hideIfIdle();
|
|
2468
|
+
}
|
|
2434
2469
|
syncPlayPause() {
|
|
2435
2470
|
let e = !this.video.paused && !this.video.ended;
|
|
2436
2471
|
this.setIcon(this.playBtn, e ? "pause" : "play"), this.playBtn.setAttribute("aria-label", e ? "Pause" : "Play"), this.setIcon(this.center, e ? "pause" : "play"), this.center.classList.toggle("havik-gone", e || !!this.statusBox), e ? this.scheduleHide() : this.show();
|
|
@@ -2461,7 +2496,7 @@ var fn = class {
|
|
|
2461
2496
|
e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = Z("play"), e.addEventListener("click", () => void this.player.play()), n.append(e);
|
|
2462
2497
|
} else {
|
|
2463
2498
|
let r = this.doc.createElement("div");
|
|
2464
|
-
if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !
|
|
2499
|
+
if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !fn.has(this.lastError.code)) {
|
|
2465
2500
|
let e = this.doc.createElement("button");
|
|
2466
2501
|
e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${Z("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), n.append(e);
|
|
2467
2502
|
}
|
|
@@ -2502,7 +2537,7 @@ var fn = class {
|
|
|
2502
2537
|
let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
|
|
2503
2538
|
this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
|
|
2504
2539
|
}
|
|
2505
|
-
!this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${
|
|
2540
|
+
!this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${hn(this.video.currentTime)} / ${hn(this.video.duration)}`);
|
|
2506
2541
|
}
|
|
2507
2542
|
syncLive() {
|
|
2508
2543
|
let e = this.player.getStats(), t = e.isLive !== !1;
|
|
@@ -2552,17 +2587,17 @@ var fn = class {
|
|
|
2552
2587
|
renderQualityMenu() {
|
|
2553
2588
|
let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
|
|
2554
2589
|
n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
|
|
2555
|
-
for (let n of e) this.menu.append(this.menuItem(
|
|
2590
|
+
for (let n of e) this.menu.append(this.menuItem(gn(n), t === n.index, () => this.player.setQuality(n.index)));
|
|
2556
2591
|
}
|
|
2557
2592
|
renderAudioMenu() {
|
|
2558
2593
|
let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
|
|
2559
2594
|
t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
|
|
2560
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
2595
|
+
for (let t of e) this.menu.append(this.menuItem(_n(t), t.default, () => this.player.setAudioTrack(t.id)));
|
|
2561
2596
|
}
|
|
2562
2597
|
renderCcMenu() {
|
|
2563
2598
|
let e = this.player.getTextTracks(), t = this.doc.createElement("div");
|
|
2564
2599
|
t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
|
|
2565
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
2600
|
+
for (let t of e) this.menu.append(this.menuItem(vn(t), !1, () => this.player.setTextTrack(t.id)));
|
|
2566
2601
|
}
|
|
2567
2602
|
onTracksChanged() {
|
|
2568
2603
|
this.openMenu && this.renderMenu(this.openMenu);
|
|
@@ -2596,16 +2631,17 @@ var fn = class {
|
|
|
2596
2631
|
this.root.classList.remove("havik-hide");
|
|
2597
2632
|
}
|
|
2598
2633
|
scheduleHide(e = this.autoHideMs) {
|
|
2599
|
-
this.hideTimer && clearTimeout(this.hideTimer), this.hideTimer = setTimeout(() =>
|
|
2600
|
-
|
|
2601
|
-
|
|
2634
|
+
this.hideTimer && clearTimeout(this.hideTimer), this.hideTimer = setTimeout(() => this.hideIfIdle(), e);
|
|
2635
|
+
}
|
|
2636
|
+
hideIfIdle() {
|
|
2637
|
+
!this.video.paused && !this.video.ended && !this.openMenu && !this.statusBox && this.root.classList.add("havik-hide");
|
|
2602
2638
|
}
|
|
2603
2639
|
onDocPointer(e) {
|
|
2604
|
-
this.openMenu && !this.menu.contains(e.target) && ([
|
|
2640
|
+
this.pointerClosedMenu = !1, this.openMenu && !this.menu.contains(e.target) && ([
|
|
2605
2641
|
this.qualityBtn,
|
|
2606
2642
|
this.audioBtn,
|
|
2607
2643
|
this.ccBtn
|
|
2608
|
-
].some((t) => t.contains(e.target)) || this.closeMenu());
|
|
2644
|
+
].some((t) => t.contains(e.target)) || (this.closeMenu(), this.pointerClosedMenu = !0));
|
|
2609
2645
|
}
|
|
2610
2646
|
onKey(e) {
|
|
2611
2647
|
let t = e.target;
|
|
@@ -2648,7 +2684,7 @@ var fn = class {
|
|
|
2648
2684
|
e.innerHTML = Z(t);
|
|
2649
2685
|
}
|
|
2650
2686
|
destroy() {
|
|
2651
|
-
this.hideTimer && clearTimeout(this.hideTimer);
|
|
2687
|
+
this.hideTimer && clearTimeout(this.hideTimer), this.tapTimer && clearTimeout(this.tapTimer);
|
|
2652
2688
|
for (let e of this.offFns) try {
|
|
2653
2689
|
e();
|
|
2654
2690
|
} catch {}
|
|
@@ -2662,41 +2698,41 @@ var fn = class {
|
|
|
2662
2698
|
this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
|
|
2663
2699
|
}
|
|
2664
2700
|
};
|
|
2665
|
-
function
|
|
2701
|
+
function hn(e) {
|
|
2666
2702
|
(!Number.isFinite(e) || e < 0) && (e = 0);
|
|
2667
2703
|
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");
|
|
2668
2704
|
return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
|
|
2669
2705
|
}
|
|
2670
|
-
function
|
|
2706
|
+
function gn(e) {
|
|
2671
2707
|
return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
|
|
2672
2708
|
}
|
|
2673
|
-
function
|
|
2709
|
+
function _n(e) {
|
|
2674
2710
|
return e.name || e.lang || `Track ${e.id}`;
|
|
2675
2711
|
}
|
|
2676
|
-
function
|
|
2712
|
+
function vn(e) {
|
|
2677
2713
|
return e.name || e.lang || `Track ${e.id}`;
|
|
2678
2714
|
}
|
|
2679
2715
|
//#endregion
|
|
2680
2716
|
//#region src/managed/index.ts
|
|
2681
|
-
var
|
|
2682
|
-
function
|
|
2717
|
+
var yn = 6e3, bn = 45e3, Q = 2e3, xn = 3, Sn = 2e3, Cn = 48e4;
|
|
2718
|
+
function wn(e) {
|
|
2683
2719
|
if (e.env !== void 0 && e.baseUrl !== void 0) throw new n("INTERNAL", 0, `createPlayer: pass env OR baseUrl, not both (got env=${JSON.stringify(e.env)} and baseUrl=${JSON.stringify(e.baseUrl)})`);
|
|
2684
|
-
if (e.env !== void 0) return
|
|
2720
|
+
if (e.env !== void 0) return Ve(e.env);
|
|
2685
2721
|
if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
|
|
2686
2722
|
return e.baseUrl;
|
|
2687
2723
|
}
|
|
2688
|
-
async function
|
|
2724
|
+
async function Tn(e) {
|
|
2689
2725
|
if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
|
|
2690
|
-
let t =
|
|
2726
|
+
let t = wn(e);
|
|
2691
2727
|
if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
|
|
2692
2728
|
if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
|
|
2693
|
-
let r = new
|
|
2729
|
+
let r = new En({
|
|
2694
2730
|
...e,
|
|
2695
2731
|
baseUrl: t
|
|
2696
2732
|
});
|
|
2697
2733
|
return await r.start(), r;
|
|
2698
2734
|
}
|
|
2699
|
-
var
|
|
2735
|
+
var En = class {
|
|
2700
2736
|
opts;
|
|
2701
2737
|
abort = new AbortController();
|
|
2702
2738
|
listeners = {};
|
|
@@ -2740,7 +2776,7 @@ var wn = class {
|
|
|
2740
2776
|
this.opts.muted && (this.opts.video.muted = !0), this.opts.poster && (this.opts.video.poster = this.opts.poster), this.mountControls(), this.attachPipListeners(), this.setState("loading");
|
|
2741
2777
|
let e = ++this.loadGen, t = this.resolveOptions();
|
|
2742
2778
|
try {
|
|
2743
|
-
let n = await
|
|
2779
|
+
let n = await y(t);
|
|
2744
2780
|
if (await this.onResolved(n, e), e === this.loadGen && this._state === "error" && this._lastError) {
|
|
2745
2781
|
let e = this._lastError;
|
|
2746
2782
|
throw this.destroy(), e;
|
|
@@ -2767,7 +2803,7 @@ var wn = class {
|
|
|
2767
2803
|
r ? this.armWithSse(e, r, t, n) : this.beginArmingByPolling(e);
|
|
2768
2804
|
}
|
|
2769
2805
|
beginArmingByPolling(e) {
|
|
2770
|
-
|
|
2806
|
+
b({
|
|
2771
2807
|
...this.resolveOptions(),
|
|
2772
2808
|
signal: this.abort.signal,
|
|
2773
2809
|
waitForLive: this.buildWaitConfig(e)
|
|
@@ -2784,7 +2820,7 @@ var wn = class {
|
|
|
2784
2820
|
setTimeout(() => {
|
|
2785
2821
|
a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
|
|
2786
2822
|
phase: i ? "unavailable" : "tooEarly",
|
|
2787
|
-
retryInMs:
|
|
2823
|
+
retryInMs: bn,
|
|
2788
2824
|
attempt: 1,
|
|
2789
2825
|
elapsedMs: 0,
|
|
2790
2826
|
...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
|
|
@@ -2801,7 +2837,7 @@ var wn = class {
|
|
|
2801
2837
|
}
|
|
2802
2838
|
}
|
|
2803
2839
|
}, h = () => {
|
|
2804
|
-
a || s || p() || (s = !0,
|
|
2840
|
+
a || s || p() || (s = !0, b({
|
|
2805
2841
|
...this.resolveOptions(),
|
|
2806
2842
|
signal: c.signal,
|
|
2807
2843
|
waitForLive: this.buildWaitConfig(e)
|
|
@@ -2809,7 +2845,7 @@ var wn = class {
|
|
|
2809
2845
|
a || p() || c.signal.aborted || (a = !0, this.closeArming(), this.fail(e instanceof n ? e : new n("INTERNAL", 0, String(e))));
|
|
2810
2846
|
}));
|
|
2811
2847
|
}, g = () => {
|
|
2812
|
-
a || p() ||
|
|
2848
|
+
a || p() || y(this.resolveOptions()).then((e) => m(e)).catch((e) => {
|
|
2813
2849
|
if (!(a || p())) {
|
|
2814
2850
|
if (e instanceof n && o(e.code)) {
|
|
2815
2851
|
h();
|
|
@@ -2823,13 +2859,13 @@ var wn = class {
|
|
|
2823
2859
|
!a && !p() && h();
|
|
2824
2860
|
}, e);
|
|
2825
2861
|
};
|
|
2826
|
-
_(
|
|
2862
|
+
_(yn), this.armingSub = T({
|
|
2827
2863
|
eventsBaseUrl: t,
|
|
2828
2864
|
matchUrn: this.opts.matchUrn,
|
|
2829
2865
|
credential: this.opts.credential,
|
|
2830
|
-
onAlive: () => _(
|
|
2866
|
+
onAlive: () => _(bn),
|
|
2831
2867
|
onState: (e) => {
|
|
2832
|
-
a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() *
|
|
2868
|
+
a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Q);
|
|
2833
2869
|
},
|
|
2834
2870
|
onError: () => h()
|
|
2835
2871
|
});
|
|
@@ -2852,7 +2888,7 @@ var wn = class {
|
|
|
2852
2888
|
let n = await s(this.opts.credential);
|
|
2853
2889
|
if (this.destroyed || t !== this.loadGen) return;
|
|
2854
2890
|
this.setState("loading");
|
|
2855
|
-
let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a =
|
|
2891
|
+
let r = this.opts.autoplay === !1 && !this.playRequested, i = this.createEngine(e, n, r), a = Ke(this.opts.video, (e) => {
|
|
2856
2892
|
this._state !== "ended" && this._state !== "error" && this.setState(e);
|
|
2857
2893
|
});
|
|
2858
2894
|
if (await i.load(), this.destroyed || t !== this.loadGen) {
|
|
@@ -2893,7 +2929,7 @@ var wn = class {
|
|
|
2893
2929
|
this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, e));
|
|
2894
2930
|
}, 0),
|
|
2895
2931
|
sample: () => {
|
|
2896
|
-
let e = this.engine?.getStats() ??
|
|
2932
|
+
let e = this.engine?.getStats() ?? He, t = c.buffered.length ? Math.max(0, c.buffered.end(c.buffered.length - 1) - c.currentTime) : 0;
|
|
2897
2933
|
return {
|
|
2898
2934
|
...e,
|
|
2899
2935
|
bufferedMs: Math.round(t * 1e3),
|
|
@@ -2934,9 +2970,9 @@ var wn = class {
|
|
|
2934
2970
|
}, o = this.opts.engine ?? "auto";
|
|
2935
2971
|
if (o === "native") return new K(a);
|
|
2936
2972
|
if (o === "hls") return new kt(a);
|
|
2937
|
-
if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl &&
|
|
2973
|
+
if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && G(this.opts.video) && Je()) return new K(a);
|
|
2938
2974
|
if (at()) return new kt(a);
|
|
2939
|
-
if (
|
|
2975
|
+
if (G(this.opts.video)) return new K(a);
|
|
2940
2976
|
throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
|
|
2941
2977
|
}
|
|
2942
2978
|
onEngineEvent(e) {
|
|
@@ -2988,7 +3024,7 @@ var wn = class {
|
|
|
2988
3024
|
r = 0, i = !1;
|
|
2989
3025
|
},
|
|
2990
3026
|
onError: () => {
|
|
2991
|
-
this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >=
|
|
3027
|
+
this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= xn && (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.`))));
|
|
2992
3028
|
}
|
|
2993
3029
|
});
|
|
2994
3030
|
}
|
|
@@ -3010,7 +3046,7 @@ var wn = class {
|
|
|
3010
3046
|
scheduleRejoin(e) {
|
|
3011
3047
|
this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
|
|
3012
3048
|
this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
|
|
3013
|
-
}, Math.random() *
|
|
3049
|
+
}, Math.random() * Q));
|
|
3014
3050
|
}
|
|
3015
3051
|
cancelRejoin() {
|
|
3016
3052
|
this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
|
|
@@ -3034,7 +3070,7 @@ var wn = class {
|
|
|
3034
3070
|
}
|
|
3035
3071
|
t.controls = !1;
|
|
3036
3072
|
let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
|
|
3037
|
-
r && r.insertBefore(n, t), n.appendChild(t), rn(n, this.opts.theme), this.container = n, this.controlBar =
|
|
3073
|
+
r && r.insertBefore(n, t), n.appendChild(t), rn(n, this.opts.theme), this.container = n, this.controlBar = pn({
|
|
3038
3074
|
root: n,
|
|
3039
3075
|
video: t,
|
|
3040
3076
|
player: this,
|
|
@@ -3042,7 +3078,8 @@ var wn = class {
|
|
|
3042
3078
|
statusOverlays: this.opts.statusOverlays,
|
|
3043
3079
|
endedMessage: this.opts.endedMessage,
|
|
3044
3080
|
interruptedMessage: this.opts.interruptedMessage,
|
|
3045
|
-
errorMessage: this.opts.errorMessage
|
|
3081
|
+
errorMessage: this.opts.errorMessage,
|
|
3082
|
+
cspNonce: this.opts.cspNonce
|
|
3046
3083
|
});
|
|
3047
3084
|
}
|
|
3048
3085
|
unmountControls() {
|
|
@@ -3064,7 +3101,7 @@ var wn = class {
|
|
|
3064
3101
|
}
|
|
3065
3102
|
startStats() {
|
|
3066
3103
|
this.stopStats();
|
|
3067
|
-
let e = Math.max(500, this.opts.statsIntervalMs ??
|
|
3104
|
+
let e = Math.max(500, this.opts.statsIntervalMs ?? Sn);
|
|
3068
3105
|
this.statsTimer = setInterval(() => {
|
|
3069
3106
|
if (!this.engine) return;
|
|
3070
3107
|
let e = this.engine.getStats();
|
|
@@ -3076,7 +3113,7 @@ var wn = class {
|
|
|
3076
3113
|
}
|
|
3077
3114
|
scheduleLicenseRefresh() {
|
|
3078
3115
|
this.stopRefresh();
|
|
3079
|
-
let e = this.opts.licenseRefreshMs ??
|
|
3116
|
+
let e = this.opts.licenseRefreshMs ?? Cn;
|
|
3080
3117
|
e && this._descriptor?.drmEnabled && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
|
|
3081
3118
|
}
|
|
3082
3119
|
stopRefresh() {
|
|
@@ -3085,7 +3122,7 @@ var wn = class {
|
|
|
3085
3122
|
async refreshLicense() {
|
|
3086
3123
|
let e = this.engine;
|
|
3087
3124
|
if (!this.destroyed && e) try {
|
|
3088
|
-
let t = await
|
|
3125
|
+
let t = await y(this.resolveOptions());
|
|
3089
3126
|
if (this.destroyed || this.engine !== e) return;
|
|
3090
3127
|
let n = t.drm?.widevine?.licenseUrl, r = t.drm?.fairplay?.licenseUrl;
|
|
3091
3128
|
(n || r) && (e.setLicenseUrls({
|
|
@@ -3121,7 +3158,7 @@ var wn = class {
|
|
|
3121
3158
|
this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
|
|
3122
3159
|
}
|
|
3123
3160
|
getStats() {
|
|
3124
|
-
return this.engine?.getStats() ??
|
|
3161
|
+
return this.engine?.getStats() ?? He;
|
|
3125
3162
|
}
|
|
3126
3163
|
getQualityLevels() {
|
|
3127
3164
|
return this.engine?.getQualityLevels() ?? [];
|
|
@@ -3173,7 +3210,7 @@ var wn = class {
|
|
|
3173
3210
|
let e = ++this.loadGen;
|
|
3174
3211
|
this.teardownPlayback();
|
|
3175
3212
|
try {
|
|
3176
|
-
let t = await
|
|
3213
|
+
let t = await b({
|
|
3177
3214
|
...this.resolveOptions(),
|
|
3178
3215
|
waitForLive: this.opts.waitForLive ? this.buildWaitConfig(e) : void 0
|
|
3179
3216
|
});
|
|
@@ -3231,7 +3268,7 @@ function $(e) {
|
|
|
3231
3268
|
}
|
|
3232
3269
|
//#endregion
|
|
3233
3270
|
//#region src/embed/host.ts
|
|
3234
|
-
function
|
|
3271
|
+
function Dn(e) {
|
|
3235
3272
|
if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
|
|
3236
3273
|
if (!e.src) throw Error("mountEmbed: opts.src is required");
|
|
3237
3274
|
if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
|
|
@@ -3290,15 +3327,15 @@ function Tn(e) {
|
|
|
3290
3327
|
}
|
|
3291
3328
|
//#endregion
|
|
3292
3329
|
//#region src/embed/page.ts
|
|
3293
|
-
var
|
|
3294
|
-
function
|
|
3330
|
+
var On = /^[A-Za-z0-9:_.-]{1,128}$/;
|
|
3331
|
+
function kn(e) {
|
|
3295
3332
|
if (typeof e == "boolean") return e;
|
|
3296
3333
|
if (typeof e == "object" && e) {
|
|
3297
3334
|
let t = e.endpoint;
|
|
3298
3335
|
return typeof t == "string" ? { endpoint: t } : {};
|
|
3299
3336
|
}
|
|
3300
3337
|
}
|
|
3301
|
-
function
|
|
3338
|
+
function An() {
|
|
3302
3339
|
let e = window.HAVIK_EMBED_CONFIG;
|
|
3303
3340
|
if (e) return {
|
|
3304
3341
|
baseUrl: e.baseUrl ?? "",
|
|
@@ -3308,7 +3345,7 @@ function On() {
|
|
|
3308
3345
|
autoplay: e.autoplay ?? !0,
|
|
3309
3346
|
muted: e.muted ?? !0,
|
|
3310
3347
|
waitForLive: e.waitForLive ?? !0,
|
|
3311
|
-
analytics:
|
|
3348
|
+
analytics: kn(e.analytics)
|
|
3312
3349
|
};
|
|
3313
3350
|
let t = new URLSearchParams(location.search);
|
|
3314
3351
|
return {
|
|
@@ -3321,9 +3358,9 @@ function On() {
|
|
|
3321
3358
|
waitForLive: !0
|
|
3322
3359
|
};
|
|
3323
3360
|
}
|
|
3324
|
-
async function
|
|
3325
|
-
let e =
|
|
3326
|
-
|
|
3361
|
+
async function jn() {
|
|
3362
|
+
let e = An();
|
|
3363
|
+
Nn();
|
|
3327
3364
|
let t = document.createElement("video");
|
|
3328
3365
|
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);
|
|
3329
3366
|
let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
|
|
@@ -3378,12 +3415,12 @@ async function kn() {
|
|
|
3378
3415
|
code: e.code,
|
|
3379
3416
|
message: e.message
|
|
3380
3417
|
})), e.on("autoplayblocked", () => {
|
|
3381
|
-
i({ type: "oddin:autoplayblocked" }),
|
|
3418
|
+
i({ type: "oddin:autoplayblocked" }), Mn(t, e);
|
|
3382
3419
|
});
|
|
3383
3420
|
}, s = async (r) => {
|
|
3384
3421
|
a?.destroy(), a = null;
|
|
3385
3422
|
try {
|
|
3386
|
-
let n = await
|
|
3423
|
+
let n = await Tn({
|
|
3387
3424
|
video: t,
|
|
3388
3425
|
baseUrl: e.baseUrl,
|
|
3389
3426
|
matchUrn: r,
|
|
@@ -3445,7 +3482,7 @@ async function kn() {
|
|
|
3445
3482
|
a?.retry();
|
|
3446
3483
|
break;
|
|
3447
3484
|
case "oddin:load":
|
|
3448
|
-
typeof e.matchUrn == "string" &&
|
|
3485
|
+
typeof e.matchUrn == "string" && On.test(e.matchUrn) && s(e.matchUrn);
|
|
3449
3486
|
break;
|
|
3450
3487
|
case "oddin:destroy": c();
|
|
3451
3488
|
}
|
|
@@ -3457,18 +3494,18 @@ async function kn() {
|
|
|
3457
3494
|
protocol: 1
|
|
3458
3495
|
});
|
|
3459
3496
|
}
|
|
3460
|
-
function
|
|
3497
|
+
function Mn(e, t) {
|
|
3461
3498
|
if (document.getElementById("havik-tap")) return;
|
|
3462
3499
|
let n = document.createElement("button");
|
|
3463
3500
|
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", () => {
|
|
3464
3501
|
n.remove(), t.play();
|
|
3465
3502
|
}), document.body.appendChild(n);
|
|
3466
3503
|
}
|
|
3467
|
-
function
|
|
3504
|
+
function Nn() {
|
|
3468
3505
|
let e = document.createElement("style");
|
|
3469
3506
|
e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
|
|
3470
3507
|
}
|
|
3471
3508
|
//#endregion
|
|
3472
|
-
export { tn as ODDIN_THEME, n as PlaybackError, rn as applyTheme,
|
|
3509
|
+
export { tn as ODDIN_THEME, n as PlaybackError, rn as applyTheme, Ve as baseUrlForEnv, jn as bootEmbed, Te as clearDeviceId, Tn as createPlayer, E as deriveEventsBaseUrl, Le as detectDrmSupport, C as enterVideoFullscreen, w as exitVideoFullscreen, _e as fetchCatalog, ye as fetchTournament, xe as flattenMatches, k as getDeviceId, ze as isLowLatencyManifest, $ as isOddinMessage, De as licenseRequestHeaders, Dn as mountEmbed, v as pollToLive, b as resolveStream, T as subscribeLiveState, S as videoFullscreenActive, Ce as watchStatus };
|
|
3473
3510
|
|
|
3474
3511
|
//# sourceMappingURL=index.js.map
|