@oddin-gg/havik-player 2.14.0 → 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.js +169 -134
- package/dist/index.js.map +1 -1
- package/docs/API.md +4 -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,7 +2293,7 @@ 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", sn = /* @__PURE__ */ new WeakMap();
|
|
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
2297
|
function cn(e = document, t) {
|
|
2298
2298
|
let n = e.querySelector(`style#${an}`);
|
|
2299
2299
|
if (n && (!t || sn.get(n) === t)) return;
|
|
@@ -2320,16 +2320,16 @@ function Z(e) {
|
|
|
2320
2320
|
}
|
|
2321
2321
|
//#endregion
|
|
2322
2322
|
//#region src/ui/controls.ts
|
|
2323
|
-
var un = 3e3, dn = /* @__PURE__ */ new Set([
|
|
2323
|
+
var un = 3e3, dn = 250, fn = /* @__PURE__ */ new Set([
|
|
2324
2324
|
"GONE",
|
|
2325
2325
|
"NOT_FOUND",
|
|
2326
2326
|
"INVALID_URN",
|
|
2327
2327
|
"UNAUTHORIZED"
|
|
2328
2328
|
]);
|
|
2329
|
-
function
|
|
2330
|
-
return new
|
|
2329
|
+
function pn(e) {
|
|
2330
|
+
return new mn(e);
|
|
2331
2331
|
}
|
|
2332
|
-
var
|
|
2332
|
+
var mn = class {
|
|
2333
2333
|
root;
|
|
2334
2334
|
video;
|
|
2335
2335
|
player;
|
|
@@ -2367,6 +2367,10 @@ var pn = class {
|
|
|
2367
2367
|
interruptedMessage;
|
|
2368
2368
|
errorMessage;
|
|
2369
2369
|
hideTimer;
|
|
2370
|
+
tapTimer;
|
|
2371
|
+
surfacePointerType = "";
|
|
2372
|
+
surfaceWasHidden = !1;
|
|
2373
|
+
pointerClosedMenu = !1;
|
|
2370
2374
|
openMenu = null;
|
|
2371
2375
|
scrubbing = !1;
|
|
2372
2376
|
offFns = [];
|
|
@@ -2420,18 +2424,48 @@ var pn = class {
|
|
|
2420
2424
|
}
|
|
2421
2425
|
wire() {
|
|
2422
2426
|
let e = this.video;
|
|
2423
|
-
this.on(this.playBtn, "click", () => this.togglePlay()), this.on(this.center, "click", () => this.togglePlay()), this.on(this.overlay, "
|
|
2424
|
-
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();
|
|
2425
2431
|
}), this.on(this.muteBtn, "click", () => this.player.setMuted(!e.muted)), this.on(this.volSlider, "input", () => {
|
|
2426
2432
|
let t = Number(this.volSlider.value);
|
|
2427
2433
|
this.setVolumeFill(t), this.player.setVolume(t), t > 0 && e.muted && this.player.setMuted(!1);
|
|
2428
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) => {
|
|
2429
2435
|
this.lastError = e, this.statusKind = null, this.syncState();
|
|
2430
|
-
}))
|
|
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();
|
|
2431
2443
|
}
|
|
2432
2444
|
togglePlay() {
|
|
2433
2445
|
this.video.paused || this.video.ended ? this.player.play() : this.player.pause();
|
|
2434
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
|
+
}
|
|
2435
2469
|
syncPlayPause() {
|
|
2436
2470
|
let e = !this.video.paused && !this.video.ended;
|
|
2437
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();
|
|
@@ -2462,7 +2496,7 @@ var pn = class {
|
|
|
2462
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);
|
|
2463
2497
|
} else {
|
|
2464
2498
|
let r = this.doc.createElement("div");
|
|
2465
|
-
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)) {
|
|
2466
2500
|
let e = this.doc.createElement("button");
|
|
2467
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);
|
|
2468
2502
|
}
|
|
@@ -2503,7 +2537,7 @@ var pn = class {
|
|
|
2503
2537
|
let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
|
|
2504
2538
|
this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
|
|
2505
2539
|
}
|
|
2506
|
-
!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)}`);
|
|
2507
2541
|
}
|
|
2508
2542
|
syncLive() {
|
|
2509
2543
|
let e = this.player.getStats(), t = e.isLive !== !1;
|
|
@@ -2553,17 +2587,17 @@ var pn = class {
|
|
|
2553
2587
|
renderQualityMenu() {
|
|
2554
2588
|
let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
|
|
2555
2589
|
n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
|
|
2556
|
-
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)));
|
|
2557
2591
|
}
|
|
2558
2592
|
renderAudioMenu() {
|
|
2559
2593
|
let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
|
|
2560
2594
|
t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
|
|
2561
|
-
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)));
|
|
2562
2596
|
}
|
|
2563
2597
|
renderCcMenu() {
|
|
2564
2598
|
let e = this.player.getTextTracks(), t = this.doc.createElement("div");
|
|
2565
2599
|
t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
|
|
2566
|
-
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)));
|
|
2567
2601
|
}
|
|
2568
2602
|
onTracksChanged() {
|
|
2569
2603
|
this.openMenu && this.renderMenu(this.openMenu);
|
|
@@ -2597,16 +2631,17 @@ var pn = class {
|
|
|
2597
2631
|
this.root.classList.remove("havik-hide");
|
|
2598
2632
|
}
|
|
2599
2633
|
scheduleHide(e = this.autoHideMs) {
|
|
2600
|
-
this.hideTimer && clearTimeout(this.hideTimer), this.hideTimer = setTimeout(() =>
|
|
2601
|
-
|
|
2602
|
-
|
|
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");
|
|
2603
2638
|
}
|
|
2604
2639
|
onDocPointer(e) {
|
|
2605
|
-
this.openMenu && !this.menu.contains(e.target) && ([
|
|
2640
|
+
this.pointerClosedMenu = !1, this.openMenu && !this.menu.contains(e.target) && ([
|
|
2606
2641
|
this.qualityBtn,
|
|
2607
2642
|
this.audioBtn,
|
|
2608
2643
|
this.ccBtn
|
|
2609
|
-
].some((t) => t.contains(e.target)) || this.closeMenu());
|
|
2644
|
+
].some((t) => t.contains(e.target)) || (this.closeMenu(), this.pointerClosedMenu = !0));
|
|
2610
2645
|
}
|
|
2611
2646
|
onKey(e) {
|
|
2612
2647
|
let t = e.target;
|
|
@@ -2649,7 +2684,7 @@ var pn = class {
|
|
|
2649
2684
|
e.innerHTML = Z(t);
|
|
2650
2685
|
}
|
|
2651
2686
|
destroy() {
|
|
2652
|
-
this.hideTimer && clearTimeout(this.hideTimer);
|
|
2687
|
+
this.hideTimer && clearTimeout(this.hideTimer), this.tapTimer && clearTimeout(this.tapTimer);
|
|
2653
2688
|
for (let e of this.offFns) try {
|
|
2654
2689
|
e();
|
|
2655
2690
|
} catch {}
|
|
@@ -2663,41 +2698,41 @@ var pn = class {
|
|
|
2663
2698
|
this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
|
|
2664
2699
|
}
|
|
2665
2700
|
};
|
|
2666
|
-
function
|
|
2701
|
+
function hn(e) {
|
|
2667
2702
|
(!Number.isFinite(e) || e < 0) && (e = 0);
|
|
2668
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");
|
|
2669
2704
|
return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
|
|
2670
2705
|
}
|
|
2671
|
-
function
|
|
2706
|
+
function gn(e) {
|
|
2672
2707
|
return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
|
|
2673
2708
|
}
|
|
2674
|
-
function
|
|
2709
|
+
function _n(e) {
|
|
2675
2710
|
return e.name || e.lang || `Track ${e.id}`;
|
|
2676
2711
|
}
|
|
2677
|
-
function
|
|
2712
|
+
function vn(e) {
|
|
2678
2713
|
return e.name || e.lang || `Track ${e.id}`;
|
|
2679
2714
|
}
|
|
2680
2715
|
//#endregion
|
|
2681
2716
|
//#region src/managed/index.ts
|
|
2682
|
-
var
|
|
2683
|
-
function
|
|
2717
|
+
var yn = 6e3, bn = 45e3, Q = 2e3, xn = 3, Sn = 2e3, Cn = 48e4;
|
|
2718
|
+
function wn(e) {
|
|
2684
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)})`);
|
|
2685
|
-
if (e.env !== void 0) return
|
|
2720
|
+
if (e.env !== void 0) return Ve(e.env);
|
|
2686
2721
|
if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
|
|
2687
2722
|
return e.baseUrl;
|
|
2688
2723
|
}
|
|
2689
|
-
async function
|
|
2724
|
+
async function Tn(e) {
|
|
2690
2725
|
if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
|
|
2691
|
-
let t =
|
|
2726
|
+
let t = wn(e);
|
|
2692
2727
|
if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
|
|
2693
2728
|
if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
|
|
2694
|
-
let r = new
|
|
2729
|
+
let r = new En({
|
|
2695
2730
|
...e,
|
|
2696
2731
|
baseUrl: t
|
|
2697
2732
|
});
|
|
2698
2733
|
return await r.start(), r;
|
|
2699
2734
|
}
|
|
2700
|
-
var
|
|
2735
|
+
var En = class {
|
|
2701
2736
|
opts;
|
|
2702
2737
|
abort = new AbortController();
|
|
2703
2738
|
listeners = {};
|
|
@@ -2741,7 +2776,7 @@ var Tn = class {
|
|
|
2741
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");
|
|
2742
2777
|
let e = ++this.loadGen, t = this.resolveOptions();
|
|
2743
2778
|
try {
|
|
2744
|
-
let n = await
|
|
2779
|
+
let n = await y(t);
|
|
2745
2780
|
if (await this.onResolved(n, e), e === this.loadGen && this._state === "error" && this._lastError) {
|
|
2746
2781
|
let e = this._lastError;
|
|
2747
2782
|
throw this.destroy(), e;
|
|
@@ -2768,7 +2803,7 @@ var Tn = class {
|
|
|
2768
2803
|
r ? this.armWithSse(e, r, t, n) : this.beginArmingByPolling(e);
|
|
2769
2804
|
}
|
|
2770
2805
|
beginArmingByPolling(e) {
|
|
2771
|
-
|
|
2806
|
+
b({
|
|
2772
2807
|
...this.resolveOptions(),
|
|
2773
2808
|
signal: this.abort.signal,
|
|
2774
2809
|
waitForLive: this.buildWaitConfig(e)
|
|
@@ -2785,7 +2820,7 @@ var Tn = class {
|
|
|
2785
2820
|
setTimeout(() => {
|
|
2786
2821
|
a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
|
|
2787
2822
|
phase: i ? "unavailable" : "tooEarly",
|
|
2788
|
-
retryInMs:
|
|
2823
|
+
retryInMs: bn,
|
|
2789
2824
|
attempt: 1,
|
|
2790
2825
|
elapsedMs: 0,
|
|
2791
2826
|
...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
|
|
@@ -2802,7 +2837,7 @@ var Tn = class {
|
|
|
2802
2837
|
}
|
|
2803
2838
|
}
|
|
2804
2839
|
}, h = () => {
|
|
2805
|
-
a || s || p() || (s = !0,
|
|
2840
|
+
a || s || p() || (s = !0, b({
|
|
2806
2841
|
...this.resolveOptions(),
|
|
2807
2842
|
signal: c.signal,
|
|
2808
2843
|
waitForLive: this.buildWaitConfig(e)
|
|
@@ -2810,7 +2845,7 @@ var Tn = class {
|
|
|
2810
2845
|
a || p() || c.signal.aborted || (a = !0, this.closeArming(), this.fail(e instanceof n ? e : new n("INTERNAL", 0, String(e))));
|
|
2811
2846
|
}));
|
|
2812
2847
|
}, g = () => {
|
|
2813
|
-
a || p() ||
|
|
2848
|
+
a || p() || y(this.resolveOptions()).then((e) => m(e)).catch((e) => {
|
|
2814
2849
|
if (!(a || p())) {
|
|
2815
2850
|
if (e instanceof n && o(e.code)) {
|
|
2816
2851
|
h();
|
|
@@ -2824,11 +2859,11 @@ var Tn = class {
|
|
|
2824
2859
|
!a && !p() && h();
|
|
2825
2860
|
}, e);
|
|
2826
2861
|
};
|
|
2827
|
-
_(
|
|
2862
|
+
_(yn), this.armingSub = T({
|
|
2828
2863
|
eventsBaseUrl: t,
|
|
2829
2864
|
matchUrn: this.opts.matchUrn,
|
|
2830
2865
|
credential: this.opts.credential,
|
|
2831
|
-
onAlive: () => _(
|
|
2866
|
+
onAlive: () => _(bn),
|
|
2832
2867
|
onState: (e) => {
|
|
2833
2868
|
a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Q);
|
|
2834
2869
|
},
|
|
@@ -2853,7 +2888,7 @@ var Tn = class {
|
|
|
2853
2888
|
let n = await s(this.opts.credential);
|
|
2854
2889
|
if (this.destroyed || t !== this.loadGen) return;
|
|
2855
2890
|
this.setState("loading");
|
|
2856
|
-
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) => {
|
|
2857
2892
|
this._state !== "ended" && this._state !== "error" && this.setState(e);
|
|
2858
2893
|
});
|
|
2859
2894
|
if (await i.load(), this.destroyed || t !== this.loadGen) {
|
|
@@ -2894,7 +2929,7 @@ var Tn = class {
|
|
|
2894
2929
|
this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, e));
|
|
2895
2930
|
}, 0),
|
|
2896
2931
|
sample: () => {
|
|
2897
|
-
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;
|
|
2898
2933
|
return {
|
|
2899
2934
|
...e,
|
|
2900
2935
|
bufferedMs: Math.round(t * 1e3),
|
|
@@ -2935,9 +2970,9 @@ var Tn = class {
|
|
|
2935
2970
|
}, o = this.opts.engine ?? "auto";
|
|
2936
2971
|
if (o === "native") return new K(a);
|
|
2937
2972
|
if (o === "hls") return new kt(a);
|
|
2938
|
-
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);
|
|
2939
2974
|
if (at()) return new kt(a);
|
|
2940
|
-
if (
|
|
2975
|
+
if (G(this.opts.video)) return new K(a);
|
|
2941
2976
|
throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
|
|
2942
2977
|
}
|
|
2943
2978
|
onEngineEvent(e) {
|
|
@@ -2989,7 +3024,7 @@ var Tn = class {
|
|
|
2989
3024
|
r = 0, i = !1;
|
|
2990
3025
|
},
|
|
2991
3026
|
onError: () => {
|
|
2992
|
-
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.`))));
|
|
2993
3028
|
}
|
|
2994
3029
|
});
|
|
2995
3030
|
}
|
|
@@ -3035,7 +3070,7 @@ var Tn = class {
|
|
|
3035
3070
|
}
|
|
3036
3071
|
t.controls = !1;
|
|
3037
3072
|
let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
|
|
3038
|
-
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({
|
|
3039
3074
|
root: n,
|
|
3040
3075
|
video: t,
|
|
3041
3076
|
player: this,
|
|
@@ -3066,7 +3101,7 @@ var Tn = class {
|
|
|
3066
3101
|
}
|
|
3067
3102
|
startStats() {
|
|
3068
3103
|
this.stopStats();
|
|
3069
|
-
let e = Math.max(500, this.opts.statsIntervalMs ??
|
|
3104
|
+
let e = Math.max(500, this.opts.statsIntervalMs ?? Sn);
|
|
3070
3105
|
this.statsTimer = setInterval(() => {
|
|
3071
3106
|
if (!this.engine) return;
|
|
3072
3107
|
let e = this.engine.getStats();
|
|
@@ -3078,7 +3113,7 @@ var Tn = class {
|
|
|
3078
3113
|
}
|
|
3079
3114
|
scheduleLicenseRefresh() {
|
|
3080
3115
|
this.stopRefresh();
|
|
3081
|
-
let e = this.opts.licenseRefreshMs ??
|
|
3116
|
+
let e = this.opts.licenseRefreshMs ?? Cn;
|
|
3082
3117
|
e && this._descriptor?.drmEnabled && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
|
|
3083
3118
|
}
|
|
3084
3119
|
stopRefresh() {
|
|
@@ -3087,7 +3122,7 @@ var Tn = class {
|
|
|
3087
3122
|
async refreshLicense() {
|
|
3088
3123
|
let e = this.engine;
|
|
3089
3124
|
if (!this.destroyed && e) try {
|
|
3090
|
-
let t = await
|
|
3125
|
+
let t = await y(this.resolveOptions());
|
|
3091
3126
|
if (this.destroyed || this.engine !== e) return;
|
|
3092
3127
|
let n = t.drm?.widevine?.licenseUrl, r = t.drm?.fairplay?.licenseUrl;
|
|
3093
3128
|
(n || r) && (e.setLicenseUrls({
|
|
@@ -3123,7 +3158,7 @@ var Tn = class {
|
|
|
3123
3158
|
this.destroyed || (this.opts.video.volume = Math.min(1, Math.max(0, e)));
|
|
3124
3159
|
}
|
|
3125
3160
|
getStats() {
|
|
3126
|
-
return this.engine?.getStats() ??
|
|
3161
|
+
return this.engine?.getStats() ?? He;
|
|
3127
3162
|
}
|
|
3128
3163
|
getQualityLevels() {
|
|
3129
3164
|
return this.engine?.getQualityLevels() ?? [];
|
|
@@ -3175,7 +3210,7 @@ var Tn = class {
|
|
|
3175
3210
|
let e = ++this.loadGen;
|
|
3176
3211
|
this.teardownPlayback();
|
|
3177
3212
|
try {
|
|
3178
|
-
let t = await
|
|
3213
|
+
let t = await b({
|
|
3179
3214
|
...this.resolveOptions(),
|
|
3180
3215
|
waitForLive: this.opts.waitForLive ? this.buildWaitConfig(e) : void 0
|
|
3181
3216
|
});
|
|
@@ -3233,7 +3268,7 @@ function $(e) {
|
|
|
3233
3268
|
}
|
|
3234
3269
|
//#endregion
|
|
3235
3270
|
//#region src/embed/host.ts
|
|
3236
|
-
function
|
|
3271
|
+
function Dn(e) {
|
|
3237
3272
|
if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
|
|
3238
3273
|
if (!e.src) throw Error("mountEmbed: opts.src is required");
|
|
3239
3274
|
if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
|
|
@@ -3292,15 +3327,15 @@ function En(e) {
|
|
|
3292
3327
|
}
|
|
3293
3328
|
//#endregion
|
|
3294
3329
|
//#region src/embed/page.ts
|
|
3295
|
-
var
|
|
3296
|
-
function
|
|
3330
|
+
var On = /^[A-Za-z0-9:_.-]{1,128}$/;
|
|
3331
|
+
function kn(e) {
|
|
3297
3332
|
if (typeof e == "boolean") return e;
|
|
3298
3333
|
if (typeof e == "object" && e) {
|
|
3299
3334
|
let t = e.endpoint;
|
|
3300
3335
|
return typeof t == "string" ? { endpoint: t } : {};
|
|
3301
3336
|
}
|
|
3302
3337
|
}
|
|
3303
|
-
function
|
|
3338
|
+
function An() {
|
|
3304
3339
|
let e = window.HAVIK_EMBED_CONFIG;
|
|
3305
3340
|
if (e) return {
|
|
3306
3341
|
baseUrl: e.baseUrl ?? "",
|
|
@@ -3310,7 +3345,7 @@ function kn() {
|
|
|
3310
3345
|
autoplay: e.autoplay ?? !0,
|
|
3311
3346
|
muted: e.muted ?? !0,
|
|
3312
3347
|
waitForLive: e.waitForLive ?? !0,
|
|
3313
|
-
analytics:
|
|
3348
|
+
analytics: kn(e.analytics)
|
|
3314
3349
|
};
|
|
3315
3350
|
let t = new URLSearchParams(location.search);
|
|
3316
3351
|
return {
|
|
@@ -3323,9 +3358,9 @@ function kn() {
|
|
|
3323
3358
|
waitForLive: !0
|
|
3324
3359
|
};
|
|
3325
3360
|
}
|
|
3326
|
-
async function
|
|
3327
|
-
let e =
|
|
3328
|
-
|
|
3361
|
+
async function jn() {
|
|
3362
|
+
let e = An();
|
|
3363
|
+
Nn();
|
|
3329
3364
|
let t = document.createElement("video");
|
|
3330
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);
|
|
3331
3366
|
let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
|
|
@@ -3380,12 +3415,12 @@ async function An() {
|
|
|
3380
3415
|
code: e.code,
|
|
3381
3416
|
message: e.message
|
|
3382
3417
|
})), e.on("autoplayblocked", () => {
|
|
3383
|
-
i({ type: "oddin:autoplayblocked" }),
|
|
3418
|
+
i({ type: "oddin:autoplayblocked" }), Mn(t, e);
|
|
3384
3419
|
});
|
|
3385
3420
|
}, s = async (r) => {
|
|
3386
3421
|
a?.destroy(), a = null;
|
|
3387
3422
|
try {
|
|
3388
|
-
let n = await
|
|
3423
|
+
let n = await Tn({
|
|
3389
3424
|
video: t,
|
|
3390
3425
|
baseUrl: e.baseUrl,
|
|
3391
3426
|
matchUrn: r,
|
|
@@ -3447,7 +3482,7 @@ async function An() {
|
|
|
3447
3482
|
a?.retry();
|
|
3448
3483
|
break;
|
|
3449
3484
|
case "oddin:load":
|
|
3450
|
-
typeof e.matchUrn == "string" &&
|
|
3485
|
+
typeof e.matchUrn == "string" && On.test(e.matchUrn) && s(e.matchUrn);
|
|
3451
3486
|
break;
|
|
3452
3487
|
case "oddin:destroy": c();
|
|
3453
3488
|
}
|
|
@@ -3459,18 +3494,18 @@ async function An() {
|
|
|
3459
3494
|
protocol: 1
|
|
3460
3495
|
});
|
|
3461
3496
|
}
|
|
3462
|
-
function
|
|
3497
|
+
function Mn(e, t) {
|
|
3463
3498
|
if (document.getElementById("havik-tap")) return;
|
|
3464
3499
|
let n = document.createElement("button");
|
|
3465
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", () => {
|
|
3466
3501
|
n.remove(), t.play();
|
|
3467
3502
|
}), document.body.appendChild(n);
|
|
3468
3503
|
}
|
|
3469
|
-
function
|
|
3504
|
+
function Nn() {
|
|
3470
3505
|
let e = document.createElement("style");
|
|
3471
3506
|
e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
|
|
3472
3507
|
}
|
|
3473
3508
|
//#endregion
|
|
3474
|
-
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 };
|
|
3475
3510
|
|
|
3476
3511
|
//# sourceMappingURL=index.js.map
|