@oddin-gg/havik-player 2.23.2 → 2.23.3
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 +2 -2
- package/dist/havik-player.global.js.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +120 -117
- package/dist/index.js.map +1 -1
- package/embed/index.html +1 -1
- package/examples/vanilla.html +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1500,7 +1500,10 @@ function mn(e, t, n) {
|
|
|
1500
1500
|
return a;
|
|
1501
1501
|
}
|
|
1502
1502
|
var hn = .2, gn = 30, _n = 2, vn = 2, yn = 2, bn = 9, xn = .5, Sn = 64, Cn = 1e4, wn = 8, Tn = .6, En = .4, Dn = .4, On = 2, kn = 6e4, q = .5, An = 3.5, jn = 300;
|
|
1503
|
-
function Mn() {
|
|
1503
|
+
function Mn(e) {
|
|
1504
|
+
return (e?.part ?? e?.frag)?.stats;
|
|
1505
|
+
}
|
|
1506
|
+
function Nn() {
|
|
1504
1507
|
if (typeof navigator > "u") return 0;
|
|
1505
1508
|
try {
|
|
1506
1509
|
let e = navigator.connection?.rtt;
|
|
@@ -1509,20 +1512,20 @@ function Mn() {
|
|
|
1509
1512
|
return 0;
|
|
1510
1513
|
}
|
|
1511
1514
|
}
|
|
1512
|
-
function
|
|
1515
|
+
function Pn(e) {
|
|
1513
1516
|
if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
|
|
1514
1517
|
delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
|
|
1515
1518
|
return;
|
|
1516
1519
|
}
|
|
1517
1520
|
e.liveSyncDuration ??= yn;
|
|
1518
1521
|
let t = Math.max(12, e.liveSyncDuration + bn);
|
|
1519
|
-
(e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t),
|
|
1522
|
+
(e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Fn(e, e.liveSyncDuration);
|
|
1520
1523
|
}
|
|
1521
|
-
function
|
|
1524
|
+
function Fn(e, t) {
|
|
1522
1525
|
let n = (e, t, n) => Math.min(n, Math.max(t, e));
|
|
1523
1526
|
e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
|
|
1524
1527
|
}
|
|
1525
|
-
var
|
|
1528
|
+
var In = class {
|
|
1526
1529
|
name = "hls";
|
|
1527
1530
|
hls = null;
|
|
1528
1531
|
deps;
|
|
@@ -1758,7 +1761,7 @@ var Fn = class {
|
|
|
1758
1761
|
}), t = Nt(e);
|
|
1759
1762
|
t && (this.prefetch = e, f.fLoader = t);
|
|
1760
1763
|
}
|
|
1761
|
-
|
|
1764
|
+
Pn(f), this.catchUpRate = typeof f.maxLiveSyncPlaybackRate == "number" ? f.maxLiveSyncPlaybackRate : 1, this.catchUpEngaged = !1, this.catchUpRate > 1 && (f.maxLiveSyncPlaybackRate = 1), this.sizeCapManaged = f.capLevelToPlayerSize !== !0, this.fpsCapManaged = !(c && "capLevelOnFPSDrop" in c), this.loadStarted = f.autoStartLoad !== !1;
|
|
1762
1765
|
let p = new e(f);
|
|
1763
1766
|
if (this.hls = p, this.armHoldBack(p, f), p.on(e.Events.FRAG_PARSED, (e, t) => this.reanchorPartTimeline(p, t)), p.on(e.Events.MANIFEST_PARSED, () => {
|
|
1764
1767
|
this.mediaRecoveries = 0, this.networkRecoveries = 0, u && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && this.dropLevelsWidevineCannotPlay(p), this.measureLadder(p), this.applySizeCap(), this.loadSuspended = !1, t.paused && this.schedulePauseSuspend(), l({ type: "ready" });
|
|
@@ -1770,13 +1773,13 @@ var Fn = class {
|
|
|
1770
1773
|
t?.frag?.type === "main" && (this.lastFragLoadingMs = Date.now());
|
|
1771
1774
|
}), p.on(e.Events.FRAG_LOADED, (e, t) => {
|
|
1772
1775
|
t?.frag?.type === "main" && (this.mainFragsSinceTick += 1, this.bwFragsLoaded += 1, this.maybeRememberBandwidth(!1));
|
|
1773
|
-
let n = t?.frag, r =
|
|
1774
|
-
typeof
|
|
1775
|
-
let
|
|
1776
|
-
|
|
1777
|
-
let
|
|
1778
|
-
if (
|
|
1779
|
-
let e = st(
|
|
1776
|
+
let n = t?.frag, r = Mn(t), i = r?.loaded;
|
|
1777
|
+
typeof i == "number" && (this.bytesLoaded += i), !this.reportedFirstBytes && typeof i == "number" && i > 0 && (this.reportedFirstBytes = !0, l({ type: "firstBytes" }));
|
|
1778
|
+
let a = r?.loading;
|
|
1779
|
+
a && a.end > 0 && a.start > 0 && (this.requestCount += 1, this.requestTimesMs.push(Math.max(0, Math.round(a.end - a.start))), this.requestTimesMs.length > Sn && this.requestTimesMs.shift()), this.sampleHoldBack(t);
|
|
1780
|
+
let o = n?.url;
|
|
1781
|
+
if (o) {
|
|
1782
|
+
let e = st(o);
|
|
1780
1783
|
e && (this.cdnHost = e);
|
|
1781
1784
|
}
|
|
1782
1785
|
}), p.on(e.Events.LEVEL_LOADED, (e, t) => {
|
|
@@ -2127,7 +2130,7 @@ var Fn = class {
|
|
|
2127
2130
|
if (!this.holdBackEnabled || this.holdBackPriorChecked || !e || (this.holdBackPriorChecked = !0, this.holdBackApplied > 0 || this.holdBackStepCount > 0)) return;
|
|
2128
2131
|
let t = e.targetLatency;
|
|
2129
2132
|
if (this.holdBackBase < 0 && typeof t == "number" && t > 0 && (this.holdBackBase = t), this.holdBackBase < 0) return;
|
|
2130
|
-
let n =
|
|
2133
|
+
let n = Nn();
|
|
2131
2134
|
if (!(n >= jn)) return;
|
|
2132
2135
|
let r = typeof t == "number" && t > 0 ? t : this.holdBackBase, i = Math.min(this.holdBackCeiling(e), this.holdBackBase + q);
|
|
2133
2136
|
i > r && (this.holdBackStepCount = 1, this.moveHoldBack(e, r, i, "connection-rtt", `connection rtt ${n} ms`));
|
|
@@ -2142,7 +2145,7 @@ var Fn = class {
|
|
|
2142
2145
|
if (t !== "main" && t !== "audio") return;
|
|
2143
2146
|
let n = e.part ? e.part.duration : this.detectedLowLatency ? 0 : e.frag?.duration;
|
|
2144
2147
|
if (typeof n != "number" || !(n > 0)) return;
|
|
2145
|
-
let r = (e
|
|
2148
|
+
let r = Mn(e)?.loading;
|
|
2146
2149
|
if (!r || !(r.start > 0) || !(r.end > 0)) return;
|
|
2147
2150
|
let i = r.first > r.start ? r.first : r.end, a = Math.max(0, i - r.start) / (n * 1e3);
|
|
2148
2151
|
this.holdBackSamples.push({
|
|
@@ -2329,15 +2332,15 @@ var Fn = class {
|
|
|
2329
2332
|
let o = t.response?.code ?? 0, s = t.details ?? t.type, c = t.reason ?? t.error?.message ?? "", l = r === "DRM" && o === 403 && t.details === e.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED ? i(Dt(t.networkDetails)) : void 0;
|
|
2330
2333
|
return new n(r === "DRM" ? a(o, l) : o === 401 ? "UNAUTHORIZED" : o === 403 ? "FORBIDDEN" : "INTERNAL", o, `${r} error: ${s}${c ? ` (${c})` : ""}`, l ? { serverCode: l } : void 0);
|
|
2331
2334
|
}
|
|
2332
|
-
},
|
|
2335
|
+
}, Ln = 15e3, Rn = 6e5, zn = 3e5, Bn = 100, J = 1, Vn = 1e3, Hn = 3e4, Un = 1e3, Wn = {
|
|
2333
2336
|
inline: "PRESENTATION_INLINE",
|
|
2334
2337
|
fullscreen: "PRESENTATION_FULLSCREEN",
|
|
2335
2338
|
pip: "PRESENTATION_PIP"
|
|
2336
|
-
},
|
|
2339
|
+
}, Gn = {
|
|
2337
2340
|
widevine: "KEY_SYSTEM_WIDEVINE",
|
|
2338
2341
|
fairplay: "KEY_SYSTEM_FAIRPLAY",
|
|
2339
2342
|
none: "KEY_SYSTEM_NONE"
|
|
2340
|
-
},
|
|
2343
|
+
}, Kn = /* @__PURE__ */ new Set([
|
|
2341
2344
|
"INVALID_URN",
|
|
2342
2345
|
"NOT_FOUND",
|
|
2343
2346
|
"GEO_DENIED",
|
|
@@ -2358,7 +2361,7 @@ var Fn = class {
|
|
|
2358
2361
|
ended: "PLAYER_STATE_ENDED",
|
|
2359
2362
|
error: "PLAYER_STATE_ERROR"
|
|
2360
2363
|
};
|
|
2361
|
-
function
|
|
2364
|
+
function qn(e) {
|
|
2362
2365
|
try {
|
|
2363
2366
|
let t = new URL(e), [n, ...r] = t.hostname.split(".");
|
|
2364
2367
|
return !n || r.length === 0 || n !== "feed" && !n.startsWith("feed-") ? void 0 : (t.hostname = [n.replace(/^feed/, "beacons"), ...r].join("."), t.pathname = "", t.search = "", t.origin);
|
|
@@ -2366,8 +2369,8 @@ function Kn(e) {
|
|
|
2366
2369
|
return;
|
|
2367
2370
|
}
|
|
2368
2371
|
}
|
|
2369
|
-
var
|
|
2370
|
-
function
|
|
2372
|
+
var Jn = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), Yn = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), Z = /* @__PURE__ */ new Set(), Xn = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
|
|
2373
|
+
function Zn(e) {
|
|
2371
2374
|
if (e.disposition !== "enforce" || e.effectiveDirective !== "connect-src") return;
|
|
2372
2375
|
let t;
|
|
2373
2376
|
try {
|
|
@@ -2376,19 +2379,19 @@ function Xn(e) {
|
|
|
2376
2379
|
} catch {
|
|
2377
2380
|
return;
|
|
2378
2381
|
}
|
|
2379
|
-
if (
|
|
2380
|
-
X.add(t), _(
|
|
2382
|
+
if (Yn.has(t) && !X.has(t)) {
|
|
2383
|
+
X.add(t), _(Xn(new URL(t).origin));
|
|
2381
2384
|
for (let e of Z) e.noteCspBlocked();
|
|
2382
2385
|
}
|
|
2383
2386
|
}
|
|
2384
|
-
function
|
|
2387
|
+
function Qn(e, t) {
|
|
2385
2388
|
return _(t, e), { disabledReason: t };
|
|
2386
2389
|
}
|
|
2387
|
-
function
|
|
2388
|
-
if (n) return t && !
|
|
2390
|
+
function $n(e, t, n) {
|
|
2391
|
+
if (n) return t && !Jn(t, n) && _(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
|
|
2389
2392
|
if (t) return { endpoint: t };
|
|
2390
|
-
if (t !== void 0) return
|
|
2391
|
-
let r =
|
|
2393
|
+
if (t !== void 0) return Qn("empty-endpoint", "analytics is disabled: no QoE beacons will be sent. analytics.endpoint was supplied but empty. Pass a real beacons origin, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, or omit the option to derive it from baseUrl (or analytics: false to opt out).");
|
|
2394
|
+
let r = qn(e);
|
|
2392
2395
|
if (r) return { endpoint: r };
|
|
2393
2396
|
let i;
|
|
2394
2397
|
try {
|
|
@@ -2396,9 +2399,9 @@ function Qn(e, t, n) {
|
|
|
2396
2399
|
} catch {
|
|
2397
2400
|
i = e;
|
|
2398
2401
|
}
|
|
2399
|
-
return
|
|
2402
|
+
return Qn(i, `analytics is disabled: no QoE beacons will be sent. The beacons endpoint cannot be derived from the baseUrl host "${i}" — the convention is feed[-dev].<domain> → beacons[-dev].<domain>. Pass an explicit endpoint, analytics: { endpoint: 'https://beacons[-dev].<domain>' }, to createPlayer (or analytics: false to opt out deliberately).`);
|
|
2400
2403
|
}
|
|
2401
|
-
var
|
|
2404
|
+
var er = class {
|
|
2402
2405
|
opts;
|
|
2403
2406
|
url;
|
|
2404
2407
|
cspKey;
|
|
@@ -2480,8 +2483,8 @@ var $n = class {
|
|
|
2480
2483
|
constructor(e) {
|
|
2481
2484
|
this.opts = {
|
|
2482
2485
|
...e,
|
|
2483
|
-
heartbeatMs: e.heartbeatMs ??
|
|
2484
|
-
idleMs: e.idleMs ??
|
|
2486
|
+
heartbeatMs: e.heartbeatMs ?? Ln,
|
|
2487
|
+
idleMs: e.idleMs ?? Rn
|
|
2485
2488
|
}, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`;
|
|
2486
2489
|
try {
|
|
2487
2490
|
let e = new URL(this.url);
|
|
@@ -2498,11 +2501,11 @@ var $n = class {
|
|
|
2498
2501
|
sdkVersion: this.opts.sdkVersion,
|
|
2499
2502
|
...this.opts.environment
|
|
2500
2503
|
};
|
|
2501
|
-
this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(),
|
|
2504
|
+
this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), Vn), this.lastTickMs = this.now(), this.timer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs), typeof window < "u" && window.addEventListener("pagehide", this.onPagehide), typeof document < "u" && (document.addEventListener("visibilitychange", this.onVisibility), document.addEventListener("freeze", this.onFreeze), document.addEventListener("resume", this.onResume), this.cspKey && Yn.add(this.cspKey), document.addEventListener("securitypolicyviolation", Zn), Z.add(this), this.noteCspBlocked(), this.onVisibility());
|
|
2502
2505
|
}
|
|
2503
2506
|
checkSuspension(e) {
|
|
2504
2507
|
if (this.endedFlag || !this.opts.idleMs) return !1;
|
|
2505
|
-
let t = Math.max(this.opts.idleMs,
|
|
2508
|
+
let t = Math.max(this.opts.idleMs, zn);
|
|
2506
2509
|
return this.lastTickMs > 0 && e - this.lastTickMs >= t ? (this.endIdle(this.lastTickMs), !0) : (this.lastTickMs = e, !1);
|
|
2507
2510
|
}
|
|
2508
2511
|
syncIdle(e) {
|
|
@@ -2525,12 +2528,12 @@ var $n = class {
|
|
|
2525
2528
|
let a = {
|
|
2526
2529
|
fromState: Y[e] ?? "PLAYER_STATE_UNSPECIFIED",
|
|
2527
2530
|
toState: Y[t] ?? "PLAYER_STATE_UNSPECIFIED",
|
|
2528
|
-
presentation:
|
|
2531
|
+
presentation: Wn[this.presentation]
|
|
2529
2532
|
}, o = this.causeOf(e, t, i);
|
|
2530
2533
|
o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
|
|
2531
2534
|
}
|
|
2532
2535
|
seekInduced(e) {
|
|
2533
|
-
return this.lastSeekMs >= 0 && e - this.lastSeekMs <
|
|
2536
|
+
return this.lastSeekMs >= 0 && e - this.lastSeekMs < Un;
|
|
2534
2537
|
}
|
|
2535
2538
|
causeOf(e, t, n) {
|
|
2536
2539
|
if (t === "error") return "TRANSITION_CAUSE_ERROR";
|
|
@@ -2604,7 +2607,7 @@ var $n = class {
|
|
|
2604
2607
|
let r = n ?? "";
|
|
2605
2608
|
if (r !== "") return /^fragLoad/i.test(r) ? this.sawFirstBytes ? "ERROR_STAGE_SEGMENT" : "ERROR_STAGE_FIRST_SEGMENT" : /^(manifest|level|audioTrack|subtitleTrack)/i.test(r) ? "ERROR_STAGE_MANIFEST" : /key|licen[cs]e/i.test(r) ? "ERROR_STAGE_DRM" : /parsing|append|buffer|remux|decrypt|media/i.test(r) ? "ERROR_STAGE_DECODE" : "ERROR_STAGE_OTHER";
|
|
2606
2609
|
if (e === "DRM_CLIENT" || e === "DRM_DEVICE_REVOKED") return "ERROR_STAGE_DRM";
|
|
2607
|
-
if (
|
|
2610
|
+
if (Kn.has(e)) return "ERROR_STAGE_RESOLVE";
|
|
2608
2611
|
let i = t ?? "";
|
|
2609
2612
|
return /frag|segment/i.test(i) ? this.sawFirstBytes ? "ERROR_STAGE_SEGMENT" : "ERROR_STAGE_FIRST_SEGMENT" : /manifest|level|playlist/i.test(i) ? "ERROR_STAGE_MANIFEST" : /key|licen[cs]e/i.test(i) ? "ERROR_STAGE_DRM" : /buffer|decode|media|append/i.test(i) ? "ERROR_STAGE_DECODE" : "ERROR_STAGE_OTHER";
|
|
2610
2613
|
}
|
|
@@ -2658,7 +2661,7 @@ var $n = class {
|
|
|
2658
2661
|
maybeEmitMediaInfo(e) {
|
|
2659
2662
|
if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
|
|
2660
2663
|
let t = {};
|
|
2661
|
-
e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem =
|
|
2664
|
+
e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = Gn[e.keySystem]), e.drmSecurityLevel && (t.drmSecurityLevel = e.drmSecurityLevel), e.ladderHeights?.length && (t.ladderHeights = e.ladderHeights), e.ladderTopBitrateKbps && (t.ladderTopBitrateKbps = e.ladderTopBitrateKbps), e.lowLatency && (t.lowLatency = !0);
|
|
2662
2665
|
let n = JSON.stringify(t);
|
|
2663
2666
|
n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
|
|
2664
2667
|
}
|
|
@@ -2678,7 +2681,7 @@ var $n = class {
|
|
|
2678
2681
|
let c = r?.positionMs ?? 0;
|
|
2679
2682
|
c > 0 && (i.exitPositionMs = Math.round(c)), this.activeTotalMs > 0 && (i.activeWatchTimeMs = Math.round(this.activeTotalMs)), this.mutedTotalMs > 0 && (i.mutedTimeMs = Math.round(this.mutedTotalMs));
|
|
2680
2683
|
let l = e !== "END_REASON_ENDED";
|
|
2681
|
-
l && this.lastStallMs > 0 && n - this.lastStallMs <=
|
|
2684
|
+
l && this.lastStallMs > 0 && n - this.lastStallMs <= Hn && (i.abandonAfterStallMs = Math.max(J, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= Hn && (i.abandonAfterErrorMs = Math.max(J, Math.round(n - this.lastErrorMs))), this.fullscreenTotalMs > 0 && (i.fullscreenTimeMs = Math.round(this.fullscreenTotalMs)), this.pipTotalMs > 0 && (i.pipTimeMs = Math.round(this.pipTotalMs)), this.volumeChanges > 0 && (i.volumeChanges = this.volumeChanges), this.muteChanges > 0 && (i.muteChanges = this.muteChanges);
|
|
2682
2685
|
let u = r?.decodedFrames ?? this.lastDecoded;
|
|
2683
2686
|
u > 0 && (i.decodedFramesTotal = u), e === "END_REASON_STOPPED" && this.startupTimeMs === 0 && this.lastErrorStage === null && (e = "END_REASON_ABORTED", i.reason = e), this.startupTimeMs === 0 && e !== "END_REASON_ABORTED" && (i.failedStage = this.lastErrorStage ?? (this.manifestLoadMs === 0 ? "ERROR_STAGE_MANIFEST" : this.firstFragLoadMs === 0 ? "ERROR_STAGE_FIRST_SEGMENT" : "ERROR_STAGE_OTHER")), this.pending.push(this.wrap({ sessionEnd: i })), this.flush(e === "END_REASON_PAGEHIDE");
|
|
2684
2687
|
}
|
|
@@ -2698,13 +2701,13 @@ var $n = class {
|
|
|
2698
2701
|
};
|
|
2699
2702
|
}
|
|
2700
2703
|
noteCspBlocked() {
|
|
2701
|
-
this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(
|
|
2704
|
+
this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(Xn(new URL(this.url).origin)));
|
|
2702
2705
|
}
|
|
2703
2706
|
cspBlocked() {
|
|
2704
2707
|
return !!this.cspKey && X.has(this.cspKey);
|
|
2705
2708
|
}
|
|
2706
2709
|
enqueue(e) {
|
|
2707
|
-
if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length >
|
|
2710
|
+
if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > Bn;) {
|
|
2708
2711
|
let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
|
|
2709
2712
|
this.pending.splice(e >= 0 ? e : 0, 1);
|
|
2710
2713
|
}
|
|
@@ -2731,7 +2734,7 @@ var $n = class {
|
|
|
2731
2734
|
};
|
|
2732
2735
|
//#endregion
|
|
2733
2736
|
//#region src/core/environment.ts
|
|
2734
|
-
function
|
|
2737
|
+
function tr() {
|
|
2735
2738
|
if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
|
|
2736
2739
|
let e;
|
|
2737
2740
|
try {
|
|
@@ -2756,14 +2759,14 @@ function er() {
|
|
|
2756
2759
|
integration: "INTEGRATION_KIND_IFRAME"
|
|
2757
2760
|
};
|
|
2758
2761
|
}
|
|
2759
|
-
function
|
|
2762
|
+
function nr() {
|
|
2760
2763
|
if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
|
|
2761
2764
|
let e = navigator.userAgentData, t = e?.platform ?? "";
|
|
2762
2765
|
if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
|
|
2763
2766
|
let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
|
|
2764
2767
|
return e?.mobile ?? (typeof navigator.maxTouchPoints == "number" && navigator.maxTouchPoints > 1 && n > 0 && n < 900) ? n >= 600 ? "DEVICE_CLASS_TABLET" : "DEVICE_CLASS_PHONE" : e || n >= 900 || /mac|win|linux|chrome os/i.test(t) ? "DEVICE_CLASS_DESKTOP" : "DEVICE_CLASS_OTHER";
|
|
2765
2768
|
}
|
|
2766
|
-
function
|
|
2769
|
+
function rr() {
|
|
2767
2770
|
let e = typeof navigator < "u" ? navigator.connection : void 0;
|
|
2768
2771
|
if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
|
|
2769
2772
|
let t = {
|
|
@@ -2782,17 +2785,17 @@ function nr() {
|
|
|
2782
2785
|
}
|
|
2783
2786
|
return typeof e.downlink == "number" && e.downlink > 0 && (i.downlinkKbps = Math.round(e.downlink * 1e3)), typeof e.rtt == "number" && e.rtt > 0 && (i.rttMs = Math.round(e.rtt)), e.saveData && (i.saveData = !0), i;
|
|
2784
2787
|
}
|
|
2785
|
-
function
|
|
2786
|
-
let t =
|
|
2787
|
-
deviceClass:
|
|
2788
|
+
function ir(e) {
|
|
2789
|
+
let t = tr(), n = {
|
|
2790
|
+
deviceClass: nr(),
|
|
2788
2791
|
integration: t.integration,
|
|
2789
|
-
...
|
|
2792
|
+
...rr()
|
|
2790
2793
|
};
|
|
2791
2794
|
t.host && (n.host = t.host), typeof screen < "u" && screen.width > 0 && (n.screenWidth = screen.width, n.screenHeight = screen.height), typeof window < "u" && window.devicePixelRatio > 0 && (n.devicePixelRatio = Math.round(window.devicePixelRatio * 100) / 100);
|
|
2792
2795
|
let r = e.getBoundingClientRect?.();
|
|
2793
2796
|
return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
|
|
2794
2797
|
}
|
|
2795
|
-
async function
|
|
2798
|
+
async function ar() {
|
|
2796
2799
|
let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
|
|
2797
2800
|
if (!e?.getHighEntropyValues) return {};
|
|
2798
2801
|
try {
|
|
@@ -2804,7 +2807,7 @@ async function ir() {
|
|
|
2804
2807
|
}
|
|
2805
2808
|
//#endregion
|
|
2806
2809
|
//#region src/core/version.ts
|
|
2807
|
-
var
|
|
2810
|
+
var or = "2.23.3", sr = {
|
|
2808
2811
|
accent: "#E1B600",
|
|
2809
2812
|
accentText: "#1B1C23",
|
|
2810
2813
|
background: "#0e0f13",
|
|
@@ -2815,7 +2818,7 @@ var ar = "2.23.2", or = {
|
|
|
2815
2818
|
border: "rgba(255, 255, 255, 0.1)",
|
|
2816
2819
|
radius: "8px",
|
|
2817
2820
|
fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
|
|
2818
|
-
},
|
|
2821
|
+
}, cr = {
|
|
2819
2822
|
accent: "--havik-accent",
|
|
2820
2823
|
accentText: "--havik-accent-text",
|
|
2821
2824
|
background: "--havik-bg",
|
|
@@ -2827,26 +2830,26 @@ var ar = "2.23.2", or = {
|
|
|
2827
2830
|
radius: "--havik-radius",
|
|
2828
2831
|
fontFamily: "--havik-font"
|
|
2829
2832
|
};
|
|
2830
|
-
function
|
|
2833
|
+
function lr(e, t) {
|
|
2831
2834
|
let n = {
|
|
2832
|
-
...
|
|
2835
|
+
...sr,
|
|
2833
2836
|
...t
|
|
2834
2837
|
};
|
|
2835
|
-
for (let t of Object.keys(
|
|
2838
|
+
for (let t of Object.keys(cr)) e.style.setProperty(cr[t], n[t]);
|
|
2836
2839
|
n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
|
|
2837
2840
|
}
|
|
2838
2841
|
//#endregion
|
|
2839
2842
|
//#region src/ui/styles.ts
|
|
2840
|
-
var
|
|
2841
|
-
function
|
|
2842
|
-
let n = e.querySelector(`style#${
|
|
2843
|
-
if (n && (!t ||
|
|
2843
|
+
var ur = "havik-player-styles", dr = "\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", fr = /* @__PURE__ */ new WeakMap();
|
|
2844
|
+
function pr(e = document, t) {
|
|
2845
|
+
let n = e.querySelector(`style#${ur}`);
|
|
2846
|
+
if (n && (!t || fr.get(n) === t)) return;
|
|
2844
2847
|
let r = e.createElement("style");
|
|
2845
|
-
r.id =
|
|
2848
|
+
r.id = ur, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = dr, fr.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
|
|
2846
2849
|
}
|
|
2847
2850
|
//#endregion
|
|
2848
2851
|
//#region src/ui/icons.ts
|
|
2849
|
-
var
|
|
2852
|
+
var mr = {
|
|
2850
2853
|
play: "<path d=\"M8 5v14l11-7z\"/>",
|
|
2851
2854
|
pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
|
|
2852
2855
|
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\"/>",
|
|
@@ -2860,11 +2863,11 @@ var pr = {
|
|
|
2860
2863
|
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\"/>"
|
|
2861
2864
|
};
|
|
2862
2865
|
function Q(e) {
|
|
2863
|
-
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${
|
|
2866
|
+
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${mr[e]}</svg>`;
|
|
2864
2867
|
}
|
|
2865
2868
|
//#endregion
|
|
2866
2869
|
//#region src/ui/controls.ts
|
|
2867
|
-
var
|
|
2870
|
+
var hr = 3e3, gr = 250, _r = /* @__PURE__ */ new Set([
|
|
2868
2871
|
"GONE",
|
|
2869
2872
|
"NOT_FOUND",
|
|
2870
2873
|
"GEO_DENIED",
|
|
@@ -2872,15 +2875,15 @@ var mr = 3e3, hr = 250, gr = /* @__PURE__ */ new Set([
|
|
|
2872
2875
|
"UNAUTHORIZED",
|
|
2873
2876
|
"DRM_DEVICE_REVOKED"
|
|
2874
2877
|
]);
|
|
2875
|
-
function
|
|
2878
|
+
function vr(e) {
|
|
2876
2879
|
let t = "This match isn't available in your country";
|
|
2877
2880
|
return e ? `${t} (${e})` : t;
|
|
2878
2881
|
}
|
|
2879
|
-
var
|
|
2880
|
-
function
|
|
2881
|
-
return new
|
|
2882
|
+
var yr = "This device can't play protected video";
|
|
2883
|
+
function br(e) {
|
|
2884
|
+
return new xr(e);
|
|
2882
2885
|
}
|
|
2883
|
-
var
|
|
2886
|
+
var xr = class {
|
|
2884
2887
|
root;
|
|
2885
2888
|
video;
|
|
2886
2889
|
player;
|
|
@@ -2928,7 +2931,7 @@ var br = class {
|
|
|
2928
2931
|
scrubbing = !1;
|
|
2929
2932
|
offFns = [];
|
|
2930
2933
|
constructor(e) {
|
|
2931
|
-
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ??
|
|
2934
|
+
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? hr, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.streamEnded = e.streamEnded ?? (() => !1), this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, pr(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");
|
|
2932
2935
|
let t = (e, t, n) => {
|
|
2933
2936
|
let r = this.doc.createElement(e);
|
|
2934
2937
|
if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
|
|
@@ -3014,7 +3017,7 @@ var br = class {
|
|
|
3014
3017
|
let t = this.surfaceWasHidden;
|
|
3015
3018
|
this.tapTimer = setTimeout(() => {
|
|
3016
3019
|
this.tapTimer = void 0, this.toggleBar(t);
|
|
3017
|
-
},
|
|
3020
|
+
}, gr);
|
|
3018
3021
|
}
|
|
3019
3022
|
toggleBar(e) {
|
|
3020
3023
|
e ? this.poke() : this.hideIfIdle();
|
|
@@ -3034,7 +3037,7 @@ var br = class {
|
|
|
3034
3037
|
this.statusOverlays && (e === "ended" ? t = "ended" : e === "error" ? t = "error" : e === "waiting" ? t = "waiting" : !this.hasPlayed && (e === "idle" || e === "paused") && (t = "preplay")), this.renderStatus(t);
|
|
3035
3038
|
}
|
|
3036
3039
|
statusMessageFor(e) {
|
|
3037
|
-
return e === "ended" ? this.player.endedReason === "interrupted" ? this.interruptedMessage : this.endedMessage : e === "waiting" ? this.hasPlayed ? "Reconnecting — please stand by" : "Starting soon…" : e === "error" ? this.errorMessage === void 0 ? this.lastError?.code === "GEO_DENIED" ?
|
|
3040
|
+
return e === "ended" ? this.player.endedReason === "interrupted" ? this.interruptedMessage : this.endedMessage : e === "waiting" ? this.hasPlayed ? "Reconnecting — please stand by" : "Starting soon…" : e === "error" ? this.errorMessage === void 0 ? this.lastError?.code === "GEO_DENIED" ? vr(this.lastError.country) : this.lastError?.code === "DRM_DEVICE_REVOKED" ? yr : this.lastError?.message ?? "Playback unavailable" : this.errorMessage : null;
|
|
3038
3041
|
}
|
|
3039
3042
|
renderStatus(e) {
|
|
3040
3043
|
let t = this.statusMessageFor(e);
|
|
@@ -3049,7 +3052,7 @@ var br = class {
|
|
|
3049
3052
|
e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = Q("play"), e.addEventListener("click", () => void this.player.play()), n.append(e);
|
|
3050
3053
|
} else {
|
|
3051
3054
|
let r = this.doc.createElement("div");
|
|
3052
|
-
if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !
|
|
3055
|
+
if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !_r.has(this.lastError.code)) {
|
|
3053
3056
|
let e = this.doc.createElement("button");
|
|
3054
3057
|
e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${Q("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), n.append(e);
|
|
3055
3058
|
}
|
|
@@ -3090,7 +3093,7 @@ var br = class {
|
|
|
3090
3093
|
let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
|
|
3091
3094
|
this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
|
|
3092
3095
|
}
|
|
3093
|
-
!this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${
|
|
3096
|
+
!this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Sr(this.video.currentTime)} / ${Sr(this.video.duration)}`);
|
|
3094
3097
|
}
|
|
3095
3098
|
syncLive() {
|
|
3096
3099
|
let e = this.player.getStats(), t = e.isLive !== !1, n = this.player.state === "ended", r = !t && !this.streamEnded();
|
|
@@ -3140,17 +3143,17 @@ var br = class {
|
|
|
3140
3143
|
renderQualityMenu() {
|
|
3141
3144
|
let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
|
|
3142
3145
|
n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
|
|
3143
|
-
for (let n of e) this.menu.append(this.menuItem(
|
|
3146
|
+
for (let n of e) this.menu.append(this.menuItem(Cr(n), t === n.index, () => this.player.setQuality(n.index)));
|
|
3144
3147
|
}
|
|
3145
3148
|
renderAudioMenu() {
|
|
3146
3149
|
let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
|
|
3147
3150
|
t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
|
|
3148
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
3151
|
+
for (let t of e) this.menu.append(this.menuItem(wr(t), t.default, () => this.player.setAudioTrack(t.id)));
|
|
3149
3152
|
}
|
|
3150
3153
|
renderCcMenu() {
|
|
3151
3154
|
let e = this.player.getTextTracks(), t = this.doc.createElement("div");
|
|
3152
3155
|
t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
|
|
3153
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
3156
|
+
for (let t of e) this.menu.append(this.menuItem(Tr(t), !1, () => this.player.setTextTrack(t.id)));
|
|
3154
3157
|
}
|
|
3155
3158
|
onTracksChanged() {
|
|
3156
3159
|
this.openMenu && this.renderMenu(this.openMenu);
|
|
@@ -3255,41 +3258,41 @@ var br = class {
|
|
|
3255
3258
|
this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
|
|
3256
3259
|
}
|
|
3257
3260
|
};
|
|
3258
|
-
function
|
|
3261
|
+
function Sr(e) {
|
|
3259
3262
|
(!Number.isFinite(e) || e < 0) && (e = 0);
|
|
3260
3263
|
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");
|
|
3261
3264
|
return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
|
|
3262
3265
|
}
|
|
3263
|
-
function
|
|
3266
|
+
function Cr(e) {
|
|
3264
3267
|
return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
|
|
3265
3268
|
}
|
|
3266
|
-
function
|
|
3269
|
+
function wr(e) {
|
|
3267
3270
|
return e.name || e.lang || `Track ${e.id}`;
|
|
3268
3271
|
}
|
|
3269
|
-
function
|
|
3272
|
+
function Tr(e) {
|
|
3270
3273
|
return e.name || e.lang || `Track ${e.id}`;
|
|
3271
3274
|
}
|
|
3272
3275
|
//#endregion
|
|
3273
3276
|
//#region src/managed/index.ts
|
|
3274
|
-
var
|
|
3275
|
-
function
|
|
3277
|
+
var Er = 6e3, Dr = 45e3, Or = 2e3, kr = 3, Ar = 5e3, jr = 2e3, Mr = 48e4, Nr = "keySystemLicenseRequestFailed";
|
|
3278
|
+
function Pr(e) {
|
|
3276
3279
|
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)})`);
|
|
3277
3280
|
if (e.env !== void 0) return at(e.env);
|
|
3278
3281
|
if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
|
|
3279
3282
|
return e.baseUrl;
|
|
3280
3283
|
}
|
|
3281
|
-
async function
|
|
3284
|
+
async function Fr(e) {
|
|
3282
3285
|
if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
|
|
3283
|
-
let t =
|
|
3286
|
+
let t = Pr(e);
|
|
3284
3287
|
if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
|
|
3285
3288
|
if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
|
|
3286
|
-
let r = new
|
|
3289
|
+
let r = new Ir({
|
|
3287
3290
|
...e,
|
|
3288
3291
|
baseUrl: t
|
|
3289
3292
|
});
|
|
3290
3293
|
return await r.start(), r;
|
|
3291
3294
|
}
|
|
3292
|
-
var
|
|
3295
|
+
var Ir = class {
|
|
3293
3296
|
opts;
|
|
3294
3297
|
abort = new AbortController();
|
|
3295
3298
|
listeners = {};
|
|
@@ -3384,7 +3387,7 @@ var Fr = class {
|
|
|
3384
3387
|
setTimeout(() => {
|
|
3385
3388
|
a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
|
|
3386
3389
|
phase: i ? "unavailable" : "tooEarly",
|
|
3387
|
-
retryInMs:
|
|
3390
|
+
retryInMs: Dr,
|
|
3388
3391
|
attempt: 1,
|
|
3389
3392
|
elapsedMs: 0,
|
|
3390
3393
|
...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
|
|
@@ -3423,13 +3426,13 @@ var Fr = class {
|
|
|
3423
3426
|
!a && !p() && h();
|
|
3424
3427
|
}, e);
|
|
3425
3428
|
};
|
|
3426
|
-
_(
|
|
3429
|
+
_(Er), this.armingSub = E({
|
|
3427
3430
|
eventsBaseUrl: t,
|
|
3428
3431
|
matchUrn: this.opts.matchUrn,
|
|
3429
3432
|
credential: this.opts.credential,
|
|
3430
|
-
onAlive: () => _(
|
|
3433
|
+
onAlive: () => _(Dr),
|
|
3431
3434
|
onState: (e) => {
|
|
3432
|
-
a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() *
|
|
3435
|
+
a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Or);
|
|
3433
3436
|
},
|
|
3434
3437
|
onError: () => h()
|
|
3435
3438
|
});
|
|
@@ -3474,19 +3477,19 @@ var Fr = class {
|
|
|
3474
3477
|
if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
|
|
3475
3478
|
let i = e.analytics?.sid;
|
|
3476
3479
|
if (!i) return;
|
|
3477
|
-
let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } =
|
|
3480
|
+
let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = $n(this.opts.baseUrl, a, e.analytics?.endpoint);
|
|
3478
3481
|
if (!o) {
|
|
3479
3482
|
s && setTimeout(() => {
|
|
3480
3483
|
this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, s));
|
|
3481
3484
|
}, 0);
|
|
3482
3485
|
return;
|
|
3483
3486
|
}
|
|
3484
|
-
let c = this.opts.video, l = new
|
|
3487
|
+
let c = this.opts.video, l = new er({
|
|
3485
3488
|
endpoint: o,
|
|
3486
3489
|
sid: i,
|
|
3487
3490
|
player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
|
|
3488
|
-
sdkVersion:
|
|
3489
|
-
environment:
|
|
3491
|
+
sdkVersion: or,
|
|
3492
|
+
environment: ir(c),
|
|
3490
3493
|
idleMs: typeof this.opts.analytics == "object" ? this.opts.analytics.idleMs : void 0,
|
|
3491
3494
|
onIdle: () => this.onAnalyticsIdle(t),
|
|
3492
3495
|
onBlocked: (e) => setTimeout(() => {
|
|
@@ -3507,7 +3510,7 @@ var Fr = class {
|
|
|
3507
3510
|
let u = () => l.noteSeek(), d = () => l.noteVolume(c.muted, c.volume);
|
|
3508
3511
|
c.addEventListener("seeking", u), c.addEventListener("volumechange", d), this.detachMediaListeners = () => {
|
|
3509
3512
|
c.removeEventListener("seeking", u), c.removeEventListener("volumechange", d);
|
|
3510
|
-
}, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()),
|
|
3513
|
+
}, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), ar().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
|
|
3511
3514
|
}
|
|
3512
3515
|
currentPresentation() {
|
|
3513
3516
|
let e = this.opts.video;
|
|
@@ -3538,9 +3541,9 @@ var Fr = class {
|
|
|
3538
3541
|
freshLicenseUrl: () => this.refreshLicense()
|
|
3539
3542
|
}, o = this.opts.engine ?? "auto";
|
|
3540
3543
|
if (o === "native") return new U(a);
|
|
3541
|
-
if (o === "hls") return new
|
|
3544
|
+
if (o === "hls") return new In(a);
|
|
3542
3545
|
if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && V(this.opts.video) && H() || !e.drmEnabled && Ct() && V(this.opts.video)) return new U(a);
|
|
3543
|
-
if (Lt()) return new
|
|
3546
|
+
if (Lt()) return new In(a);
|
|
3544
3547
|
if (V(this.opts.video)) return new U(a);
|
|
3545
3548
|
throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
|
|
3546
3549
|
}
|
|
@@ -3606,7 +3609,7 @@ var Fr = class {
|
|
|
3606
3609
|
r = 0, i = !1;
|
|
3607
3610
|
},
|
|
3608
3611
|
onError: () => {
|
|
3609
|
-
this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >=
|
|
3612
|
+
this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= kr && (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.`))));
|
|
3610
3613
|
}
|
|
3611
3614
|
});
|
|
3612
3615
|
}
|
|
@@ -3620,7 +3623,7 @@ var Fr = class {
|
|
|
3620
3623
|
return;
|
|
3621
3624
|
}
|
|
3622
3625
|
let e = this.hiddenAtMs;
|
|
3623
|
-
this.hiddenAtMs = null, e != null && Date.now() - e >=
|
|
3626
|
+
this.hiddenAtMs = null, e != null && Date.now() - e >= Ar && this.resyncLiveState();
|
|
3624
3627
|
}, this.onReturnPageShow = (e) => {
|
|
3625
3628
|
e.persisted && this.resyncLiveState();
|
|
3626
3629
|
}, document.addEventListener("visibilitychange", this.onReturnVisible), typeof window < "u" && window.addEventListener("pageshow", this.onReturnPageShow));
|
|
@@ -3652,7 +3655,7 @@ var Fr = class {
|
|
|
3652
3655
|
scheduleRejoin(e) {
|
|
3653
3656
|
this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
|
|
3654
3657
|
this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
|
|
3655
|
-
}, Math.random() *
|
|
3658
|
+
}, Math.random() * Or));
|
|
3656
3659
|
}
|
|
3657
3660
|
cancelRejoin() {
|
|
3658
3661
|
this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
|
|
@@ -3676,7 +3679,7 @@ var Fr = class {
|
|
|
3676
3679
|
}
|
|
3677
3680
|
t.controls = !1;
|
|
3678
3681
|
let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
|
|
3679
|
-
r && r.insertBefore(n, t), n.appendChild(t),
|
|
3682
|
+
r && r.insertBefore(n, t), n.appendChild(t), lr(n, this.opts.theme), this.container = n, this.controlBar = br({
|
|
3680
3683
|
root: n,
|
|
3681
3684
|
video: t,
|
|
3682
3685
|
player: this,
|
|
@@ -3714,7 +3717,7 @@ var Fr = class {
|
|
|
3714
3717
|
}
|
|
3715
3718
|
startStats() {
|
|
3716
3719
|
this.stopStats();
|
|
3717
|
-
let e = Math.max(500, this.opts.statsIntervalMs ??
|
|
3720
|
+
let e = Math.max(500, this.opts.statsIntervalMs ?? jr);
|
|
3718
3721
|
this.statsTimer = setInterval(() => {
|
|
3719
3722
|
if (!this.engine) return;
|
|
3720
3723
|
let e = this.engine.getStats();
|
|
@@ -3725,14 +3728,14 @@ var Fr = class {
|
|
|
3725
3728
|
this.statsTimer && clearInterval(this.statsTimer), this.statsTimer = void 0;
|
|
3726
3729
|
}
|
|
3727
3730
|
isStaleLicenseDenial(e) {
|
|
3728
|
-
return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !==
|
|
3731
|
+
return this.staleLicenseReloaded || this.opts.hlsConfig?.licenseXhrSetup || this._state === "ended" || this._state === "error" || e.detail !== Nr || e.error.httpStatus !== 403 || e.error.code !== "FORBIDDEN" || e.error.serverCode === "license_refused" ? !1 : G(this._descriptor?.drm?.widevine?.licenseUrl);
|
|
3729
3732
|
}
|
|
3730
3733
|
scheduleLicenseRefresh() {
|
|
3731
3734
|
if (this.stopRefresh(), !this._descriptor?.drmEnabled) return;
|
|
3732
3735
|
typeof document < "u" && this.engine?.name === "hls" && (this.onRefocus = () => {
|
|
3733
3736
|
document.visibilityState !== "visible" || this.destroyed || G(this._descriptor?.drm?.widevine?.licenseUrl) && this.refreshLicense();
|
|
3734
3737
|
}, document.addEventListener("visibilitychange", this.onRefocus));
|
|
3735
|
-
let e = this.opts.licenseRefreshMs ??
|
|
3738
|
+
let e = this.opts.licenseRefreshMs ?? Mr;
|
|
3736
3739
|
e && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
|
|
3737
3740
|
}
|
|
3738
3741
|
stopRefresh() {
|
|
@@ -3902,7 +3905,7 @@ function $(e) {
|
|
|
3902
3905
|
}
|
|
3903
3906
|
//#endregion
|
|
3904
3907
|
//#region src/embed/host.ts
|
|
3905
|
-
function
|
|
3908
|
+
function Lr(e) {
|
|
3906
3909
|
if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
|
|
3907
3910
|
if (!e.src) throw Error("mountEmbed: opts.src is required");
|
|
3908
3911
|
if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
|
|
@@ -3961,15 +3964,15 @@ function Ir(e) {
|
|
|
3961
3964
|
}
|
|
3962
3965
|
//#endregion
|
|
3963
3966
|
//#region src/embed/page.ts
|
|
3964
|
-
var
|
|
3965
|
-
function
|
|
3967
|
+
var Rr = /^[A-Za-z0-9:_.-]{1,128}$/;
|
|
3968
|
+
function zr(e) {
|
|
3966
3969
|
if (typeof e == "boolean") return e;
|
|
3967
3970
|
if (typeof e == "object" && e) {
|
|
3968
3971
|
let t = e.endpoint;
|
|
3969
3972
|
return typeof t == "string" ? { endpoint: t } : {};
|
|
3970
3973
|
}
|
|
3971
3974
|
}
|
|
3972
|
-
function
|
|
3975
|
+
function Br() {
|
|
3973
3976
|
let e = window.HAVIK_EMBED_CONFIG;
|
|
3974
3977
|
if (e) return {
|
|
3975
3978
|
baseUrl: e.baseUrl ?? "",
|
|
@@ -3979,7 +3982,7 @@ function zr() {
|
|
|
3979
3982
|
autoplay: e.autoplay ?? !0,
|
|
3980
3983
|
muted: e.muted ?? !0,
|
|
3981
3984
|
waitForLive: e.waitForLive ?? !0,
|
|
3982
|
-
analytics:
|
|
3985
|
+
analytics: zr(e.analytics)
|
|
3983
3986
|
};
|
|
3984
3987
|
let t = new URLSearchParams(location.search);
|
|
3985
3988
|
return {
|
|
@@ -3992,9 +3995,9 @@ function zr() {
|
|
|
3992
3995
|
waitForLive: !0
|
|
3993
3996
|
};
|
|
3994
3997
|
}
|
|
3995
|
-
async function
|
|
3996
|
-
let e =
|
|
3997
|
-
|
|
3998
|
+
async function Vr() {
|
|
3999
|
+
let e = Br();
|
|
4000
|
+
Ur();
|
|
3998
4001
|
let t = document.createElement("video");
|
|
3999
4002
|
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);
|
|
4000
4003
|
let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
|
|
@@ -4050,12 +4053,12 @@ async function Br() {
|
|
|
4050
4053
|
message: e.message,
|
|
4051
4054
|
...e.country === void 0 ? {} : { country: e.country }
|
|
4052
4055
|
})), e.on("autoplayblocked", () => {
|
|
4053
|
-
i({ type: "oddin:autoplayblocked" }),
|
|
4056
|
+
i({ type: "oddin:autoplayblocked" }), Hr(t, e);
|
|
4054
4057
|
});
|
|
4055
4058
|
}, s = async (r) => {
|
|
4056
4059
|
a?.destroy(), a = null;
|
|
4057
4060
|
try {
|
|
4058
|
-
let n = await
|
|
4061
|
+
let n = await Fr({
|
|
4059
4062
|
video: t,
|
|
4060
4063
|
baseUrl: e.baseUrl,
|
|
4061
4064
|
matchUrn: r,
|
|
@@ -4118,7 +4121,7 @@ async function Br() {
|
|
|
4118
4121
|
a?.retry();
|
|
4119
4122
|
break;
|
|
4120
4123
|
case "oddin:load":
|
|
4121
|
-
typeof e.matchUrn == "string" &&
|
|
4124
|
+
typeof e.matchUrn == "string" && Rr.test(e.matchUrn) && s(e.matchUrn);
|
|
4122
4125
|
break;
|
|
4123
4126
|
case "oddin:destroy": c();
|
|
4124
4127
|
}
|
|
@@ -4130,18 +4133,18 @@ async function Br() {
|
|
|
4130
4133
|
protocol: 1
|
|
4131
4134
|
});
|
|
4132
4135
|
}
|
|
4133
|
-
function
|
|
4136
|
+
function Hr(e, t) {
|
|
4134
4137
|
if (document.getElementById("havik-tap")) return;
|
|
4135
4138
|
let n = document.createElement("button");
|
|
4136
4139
|
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", () => {
|
|
4137
4140
|
n.remove(), t.play();
|
|
4138
4141
|
}), document.body.appendChild(n);
|
|
4139
4142
|
}
|
|
4140
|
-
function
|
|
4143
|
+
function Ur() {
|
|
4141
4144
|
let e = document.createElement("style");
|
|
4142
4145
|
e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
|
|
4143
4146
|
}
|
|
4144
4147
|
//#endregion
|
|
4145
|
-
export {
|
|
4148
|
+
export { sr as ODDIN_THEME, n as PlaybackError, lr as applyTheme, at as baseUrlForEnv, Vr as bootEmbed, Ee as clearDeviceId, Re as clearRememberedBandwidth, Fr as createPlayer, D as deriveEventsBaseUrl, tt as detectDrmSupport, w as enterVideoFullscreen, T as exitVideoFullscreen, ve as fetchCatalog, be as fetchTournament, Se as flattenMatches, C as fullscreenSupported, A as getDeviceId, rt as isLowLatencyManifest, $ as isOddinMessage, Oe as licenseRequestHeaders, Lr as mountEmbed, fe as pollToLive, y as resolveStream, E as subscribeLiveState, S as videoFullscreenActive, we as watchStatus };
|
|
4146
4149
|
|
|
4147
4150
|
//# sourceMappingURL=index.js.map
|