@oddin-gg/havik-player 2.14.0 → 2.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/havik-player.global.js +4 -3
- package/dist/havik-player.global.js.map +1 -1
- package/dist/index.cjs +4 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +191 -127
- package/dist/index.js.map +1 -1
- package/docs/API.md +15 -1
- package/embed/index.html +1 -1
- package/examples/vanilla.html +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1219,21 +1219,21 @@ function it(e, t) {
|
|
|
1219
1219
|
function at() {
|
|
1220
1220
|
return e.isSupported();
|
|
1221
1221
|
}
|
|
1222
|
-
var ot = 3, st = 5, ct = 3, lt = 3e4, ut = 12e3, dt = 3e3, ft = 6e3, pt =
|
|
1223
|
-
function
|
|
1222
|
+
var ot = 3, st = 5, ct = 3, lt = 3e4, ut = 12e3, dt = 3e3, ft = 6e3, pt = 3, mt = .5, ht = 1.2, gt = .25, _t = 5e3, vt = 4e3, yt = 2, bt = 5, xt = 2, St = 2, Ct = 4, wt = 4, Tt = 2e3, Et = 3e4, Dt = 2, Ot = 9, kt = .5, At = 64;
|
|
1223
|
+
function jt(e) {
|
|
1224
1224
|
if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
|
|
1225
1225
|
delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
|
|
1226
1226
|
return;
|
|
1227
1227
|
}
|
|
1228
|
-
e.liveSyncDuration ??=
|
|
1229
|
-
let t = Math.max(12, e.liveSyncDuration +
|
|
1230
|
-
(e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t),
|
|
1228
|
+
e.liveSyncDuration ??= Dt;
|
|
1229
|
+
let t = Math.max(12, e.liveSyncDuration + Ot);
|
|
1230
|
+
(e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), Mt(e, e.liveSyncDuration);
|
|
1231
1231
|
}
|
|
1232
|
-
function
|
|
1232
|
+
function Mt(e, t) {
|
|
1233
1233
|
let n = (e, t, n) => Math.min(n, Math.max(t, e));
|
|
1234
1234
|
e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
|
|
1235
1235
|
}
|
|
1236
|
-
var
|
|
1236
|
+
var Nt = class {
|
|
1237
1237
|
name = "hls";
|
|
1238
1238
|
hls = null;
|
|
1239
1239
|
deps;
|
|
@@ -1289,6 +1289,9 @@ var kt = class {
|
|
|
1289
1289
|
chaseSuspended = !1;
|
|
1290
1290
|
savedLiveMaxLatency;
|
|
1291
1291
|
savedMaxSyncRate;
|
|
1292
|
+
chaseLatency = -1;
|
|
1293
|
+
chaseTicks = 0;
|
|
1294
|
+
liveRetargets = 0;
|
|
1292
1295
|
capObserver = null;
|
|
1293
1296
|
capTimer = null;
|
|
1294
1297
|
sizeCapLevel = null;
|
|
@@ -1333,11 +1336,11 @@ var kt = class {
|
|
|
1333
1336
|
}
|
|
1334
1337
|
this.loadSuspended = !0;
|
|
1335
1338
|
}
|
|
1336
|
-
},
|
|
1339
|
+
}, _t));
|
|
1337
1340
|
}
|
|
1338
1341
|
resumeLoading() {
|
|
1339
|
-
if (this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.loadSuspended && (this.loadSuspended = !1, !this.stopped && this.hls)) {
|
|
1340
|
-
this.lastAdvanceMs = Date.now(), this.lastFragLoadingMs = 0, this.resetStarvationWindow(), this.clearKick();
|
|
1342
|
+
if (this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.resetChaseWindow(), this.loadSuspended && (this.loadSuspended = !1, !this.stopped && this.hls)) {
|
|
1343
|
+
this.lastAdvanceMs = Date.now(), this.lastFragLoadingMs = 0, this.resetStarvationWindow(), this.resetChaseWindow(), this.clearKick();
|
|
1341
1344
|
try {
|
|
1342
1345
|
this.hls.startLoad(-1);
|
|
1343
1346
|
} catch {}
|
|
@@ -1365,11 +1368,11 @@ var kt = class {
|
|
|
1365
1368
|
let t = e.liveSyncPosition;
|
|
1366
1369
|
if (typeof t != "number" || !Number.isFinite(t)) return;
|
|
1367
1370
|
let { video: n } = this.deps;
|
|
1368
|
-
n.currentTime <= t +
|
|
1371
|
+
n.currentTime <= t + kt || (n.currentTime = t);
|
|
1369
1372
|
}
|
|
1370
1373
|
startHls() {
|
|
1371
1374
|
let { video: t, descriptor: n, credential: r, deviceId: i, userId: a, lowLatency: o, debug: s, hlsConfig: c, emit: l } = this.deps;
|
|
1372
|
-
this.clearKick(), this.resetStarvationWindow(), this.lastFragLoadingMs = 0, this.lastEndSN = -1, this.lastAdvanceMs = 0;
|
|
1375
|
+
this.clearKick(), this.resetStarvationWindow(), this.resetChaseWindow(), this.lastFragLoadingMs = 0, this.lastEndSN = -1, this.lastAdvanceMs = 0;
|
|
1373
1376
|
let u = n.drmEnabled && !!(n.drm?.widevine?.licenseUrl || n.drm?.fairplay?.licenseUrl), d = {
|
|
1374
1377
|
debug: s === !0,
|
|
1375
1378
|
enableWorker: !o,
|
|
@@ -1412,7 +1415,7 @@ var kt = class {
|
|
|
1412
1415
|
}), t = tt(e);
|
|
1413
1416
|
t && (this.prefetch = e, d.fLoader = t);
|
|
1414
1417
|
}
|
|
1415
|
-
|
|
1418
|
+
jt(d), this.sizeCapManaged = d.capLevelToPlayerSize !== !0, this.loadStarted = d.autoStartLoad !== !1;
|
|
1416
1419
|
let f = new e(d);
|
|
1417
1420
|
if (this.hls = f, f.on(e.Events.MANIFEST_PARSED, () => {
|
|
1418
1421
|
this.mediaRecoveries = 0, this.networkRecoveries = 0, u && !this.deps.hlsConfig?.requestMediaKeySystemAccessFunc && this.dropLevelsWidevineCannotPlay(f), this.measureLadder(f), this.applySizeCap(), this.loadSuspended = !1, t.paused && this.schedulePauseSuspend(), l({ type: "ready" });
|
|
@@ -1427,7 +1430,7 @@ var kt = class {
|
|
|
1427
1430
|
let n = t?.frag?.stats, r = n?.loaded;
|
|
1428
1431
|
typeof r == "number" && (this.bytesLoaded += r), !this.reportedFirstBytes && typeof r == "number" && r > 0 && (this.reportedFirstBytes = !0, l({ type: "firstBytes" }));
|
|
1429
1432
|
let i = n?.loading;
|
|
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 >
|
|
1433
|
+
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 > At && this.requestTimesMs.shift());
|
|
1431
1434
|
let a = t?.frag?.url;
|
|
1432
1435
|
if (a) {
|
|
1433
1436
|
let e = H(a);
|
|
@@ -1451,7 +1454,7 @@ var kt = class {
|
|
|
1451
1454
|
f.on(e.Events.KEY_LOADED, () => {
|
|
1452
1455
|
u && !this.reportedDrmReady && (this.reportedDrmReady = !0, l({ type: "drmReady" }));
|
|
1453
1456
|
}), this.endWatchdog && clearInterval(this.endWatchdog), this.endWatchdog = setInterval(() => {
|
|
1454
|
-
this.checkEndStale(), this.checkStarvation();
|
|
1457
|
+
this.checkEndStale(), this.checkStarvation(), this.checkFutileChase();
|
|
1455
1458
|
}, dt), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
|
|
1456
1459
|
this.renditionSwitches += 1;
|
|
1457
1460
|
let n = this.lastLevelIndex >= 0 ? f.levels[this.lastLevelIndex] : void 0, r = f.levels[t.level];
|
|
@@ -1516,7 +1519,7 @@ var kt = class {
|
|
|
1516
1519
|
return;
|
|
1517
1520
|
}
|
|
1518
1521
|
this.capTimer && clearTimeout(this.capTimer);
|
|
1519
|
-
let t = e > this.sizeCapLevel ?
|
|
1522
|
+
let t = e > this.sizeCapLevel ? Tt : Et;
|
|
1520
1523
|
this.capTimer = setTimeout(() => {
|
|
1521
1524
|
this.capTimer = null, this.computeSizeCapTarget() === e ? this.applySizeCap() : this.onMediaResize();
|
|
1522
1525
|
}, t);
|
|
@@ -1592,6 +1595,7 @@ var kt = class {
|
|
|
1592
1595
|
bytesLoaded: this.bytesLoaded,
|
|
1593
1596
|
playlistBytesLoaded: this.playlistBytesLoaded,
|
|
1594
1597
|
renditionSwitches: this.renditionSwitches,
|
|
1598
|
+
liveRetargets: this.liveRetargets,
|
|
1595
1599
|
licenseTimeMs: this.licenseTimeMs,
|
|
1596
1600
|
upshifts: this.upshifts,
|
|
1597
1601
|
downshifts: this.downshifts,
|
|
@@ -1646,7 +1650,7 @@ var kt = class {
|
|
|
1646
1650
|
this.resetStarvationWindow();
|
|
1647
1651
|
return;
|
|
1648
1652
|
}
|
|
1649
|
-
let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < ft, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs >
|
|
1653
|
+
let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < ft, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > vt, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = U(t), c = o - s, l = c !== this.starvedFrames;
|
|
1650
1654
|
if (this.starvedFrames = c, !a || l) {
|
|
1651
1655
|
(t.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA || l) && this.clearKick(), this.resetStarvationWindow();
|
|
1652
1656
|
return;
|
|
@@ -1655,12 +1659,12 @@ var kt = class {
|
|
|
1655
1659
|
this.resetStarvationWindow();
|
|
1656
1660
|
return;
|
|
1657
1661
|
}
|
|
1658
|
-
if (this.kickPending && (this.kickKind === "dead" || i) && (this.kickTicks += 1), this.starvedTicks = i ? this.starvedTicks + 1 : 0, this.deadTicks += 1, this.recentMainFrags.push(this.mainFragsSinceTick), this.mainFragsSinceTick = 0, this.recentMainFrags.length >
|
|
1662
|
+
if (this.kickPending && (this.kickKind === "dead" || i) && (this.kickTicks += 1), this.starvedTicks = i ? this.starvedTicks + 1 : 0, this.deadTicks += 1, this.recentMainFrags.push(this.mainFragsSinceTick), this.mainFragsSinceTick = 0, this.recentMainFrags.length > Ct && this.recentMainFrags.shift(), this.clearRecoveryReset(), this.kickPending && this.kickTicks >= xt && (this.clearKick(), this.starvationRebuilds < St)) {
|
|
1659
1663
|
this.starvationRebuilds += 1, this.resetStarvationWindow(), this.clearRecoveryReset(), console.warn("havik-player: restarting the load did not resume video (the wedge is in hls.js state that only a rebuild clears) — rebuilding the player"), this.rebuildInstance("starvation rebuild");
|
|
1660
1664
|
return;
|
|
1661
1665
|
}
|
|
1662
|
-
let u = i && this.starvedTicks >=
|
|
1663
|
-
if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >=
|
|
1666
|
+
let u = i && this.starvedTicks >= yt, d = !i && this.deadTicks >= Ct && this.recentMainFrags.reduce((e, t) => e + t, 0) >= wt;
|
|
1667
|
+
if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >= bt))) {
|
|
1664
1668
|
this.starvationKicks += 1, this.kickPending = !0, this.kickTicks = 0, this.kickKind = d ? "dead" : "quiet", this.clearRecoveryReset(), console.warn(d ? "havik-player: video starved while hls.js keeps loading media it never plays (dead pipeline at the live edge) — restarting the load" : "havik-player: video starved while the live playlist advances (wedged hls.js fragment pipeline) — restarting the load");
|
|
1665
1669
|
try {
|
|
1666
1670
|
e.stopLoad(), e.startLoad(-1);
|
|
@@ -1669,6 +1673,31 @@ var kt = class {
|
|
|
1669
1673
|
}
|
|
1670
1674
|
}
|
|
1671
1675
|
}
|
|
1676
|
+
resetChaseWindow() {
|
|
1677
|
+
this.chaseLatency = -1, this.chaseTicks = 0;
|
|
1678
|
+
}
|
|
1679
|
+
checkFutileChase() {
|
|
1680
|
+
let e = this.hls, { video: t } = this.deps;
|
|
1681
|
+
if (!e || this.endedEmitted || !this.sawLive || this.chaseSuspended || this.loadSuspended || typeof document < "u" && document.hidden) {
|
|
1682
|
+
this.resetChaseWindow();
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
let n = e.latency, r = t.playbackRate, i = e.config.maxLiveSyncPlaybackRate;
|
|
1686
|
+
if (!(!t.paused && r >= ht && typeof i == "number" && i > 1 && r <= i && typeof n == "number" && Number.isFinite(n))) {
|
|
1687
|
+
this.resetChaseWindow();
|
|
1688
|
+
return;
|
|
1689
|
+
}
|
|
1690
|
+
if (this.chaseLatency < 0 || this.chaseLatency - n >= mt) {
|
|
1691
|
+
this.chaseLatency = n, this.chaseTicks = 0;
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1694
|
+
if (this.chaseTicks += 1, this.chaseTicks < pt) return;
|
|
1695
|
+
this.resetChaseWindow();
|
|
1696
|
+
let a = typeof e.config.liveMaxLatencyDuration == "number" ? e.config.liveMaxLatencyDuration : e.maxLatency;
|
|
1697
|
+
if (!(a > 1)) return;
|
|
1698
|
+
let o = typeof e.targetLatency == "number" ? e.targetLatency : 0, s = Math.min(n, a - 1);
|
|
1699
|
+
s < o + gt || (console.warn(`havik-player: catch-up at ${r}x not gaining on the live edge (latency ${n.toFixed(1)}s); holding at ${s.toFixed(1)}s`), this.liveRetargets += 1, e.targetLatency = s);
|
|
1700
|
+
}
|
|
1672
1701
|
suspendChasing() {
|
|
1673
1702
|
let e = this.hls;
|
|
1674
1703
|
e && (this.chaseSuspended = !0, this.savedLiveMaxLatency = e.config.liveMaxLatencyDuration, this.savedMaxSyncRate = e.config.maxLiveSyncPlaybackRate, e.config.liveMaxLatencyDuration = 2 ** 53 - 1, e.config.maxLiveSyncPlaybackRate = 1);
|
|
@@ -1782,19 +1811,19 @@ var kt = class {
|
|
|
1782
1811
|
let r = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
|
|
1783
1812
|
return new n(t === "DRM" ? i(r) : r === 401 ? "UNAUTHORIZED" : r === 403 ? "FORBIDDEN" : "INTERNAL", r, `${t} error: ${a}${o ? ` (${o})` : ""}`);
|
|
1784
1813
|
}
|
|
1785
|
-
},
|
|
1814
|
+
}, Pt = 15e3, Ft = 6e5, It = 3e5, Lt = 100, q = 1, Rt = 1e3, zt = 3e4, Bt = 1e3, Vt = {
|
|
1786
1815
|
inline: "PRESENTATION_INLINE",
|
|
1787
1816
|
fullscreen: "PRESENTATION_FULLSCREEN",
|
|
1788
1817
|
pip: "PRESENTATION_PIP"
|
|
1789
|
-
},
|
|
1818
|
+
}, Ht = {
|
|
1790
1819
|
widevine: "KEY_SYSTEM_WIDEVINE",
|
|
1791
1820
|
fairplay: "KEY_SYSTEM_FAIRPLAY",
|
|
1792
1821
|
none: "KEY_SYSTEM_NONE"
|
|
1793
1822
|
};
|
|
1794
|
-
function
|
|
1823
|
+
function Ut(e, t) {
|
|
1795
1824
|
return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
|
|
1796
1825
|
}
|
|
1797
|
-
var
|
|
1826
|
+
var Wt = /* @__PURE__ */ new Set([
|
|
1798
1827
|
"INVALID_URN",
|
|
1799
1828
|
"NOT_FOUND",
|
|
1800
1829
|
"GONE",
|
|
@@ -1814,7 +1843,7 @@ var Bt = /* @__PURE__ */ new Set([
|
|
|
1814
1843
|
ended: "PLAYER_STATE_ENDED",
|
|
1815
1844
|
error: "PLAYER_STATE_ERROR"
|
|
1816
1845
|
};
|
|
1817
|
-
function
|
|
1846
|
+
function Gt(e) {
|
|
1818
1847
|
try {
|
|
1819
1848
|
let t = new URL(e), [n, ...r] = t.hostname.split(".");
|
|
1820
1849
|
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);
|
|
@@ -1822,8 +1851,8 @@ function Vt(e) {
|
|
|
1822
1851
|
return;
|
|
1823
1852
|
}
|
|
1824
1853
|
}
|
|
1825
|
-
var
|
|
1826
|
-
function
|
|
1854
|
+
var Kt = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), qt = /* @__PURE__ */ new Set(), Y = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), Jt = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
|
|
1855
|
+
function Yt(e) {
|
|
1827
1856
|
if (e.disposition !== "enforce" || e.effectiveDirective !== "connect-src") return;
|
|
1828
1857
|
let t;
|
|
1829
1858
|
try {
|
|
@@ -1832,19 +1861,19 @@ function Gt(e) {
|
|
|
1832
1861
|
} catch {
|
|
1833
1862
|
return;
|
|
1834
1863
|
}
|
|
1835
|
-
if (
|
|
1836
|
-
Y.add(t), g(
|
|
1864
|
+
if (qt.has(t) && !Y.has(t)) {
|
|
1865
|
+
Y.add(t), g(Jt(new URL(t).origin));
|
|
1837
1866
|
for (let e of X) e.noteCspBlocked();
|
|
1838
1867
|
}
|
|
1839
1868
|
}
|
|
1840
|
-
function
|
|
1869
|
+
function Xt(e, t) {
|
|
1841
1870
|
return g(t, e), { disabledReason: t };
|
|
1842
1871
|
}
|
|
1843
|
-
function
|
|
1844
|
-
if (n) return t && !
|
|
1872
|
+
function Zt(e, t, n) {
|
|
1873
|
+
if (n) return t && !Kt(t, n) && g(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
|
|
1845
1874
|
if (t) return { endpoint: t };
|
|
1846
|
-
if (t !== void 0) return
|
|
1847
|
-
let r =
|
|
1875
|
+
if (t !== void 0) return Xt("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).");
|
|
1876
|
+
let r = Gt(e);
|
|
1848
1877
|
if (r) return { endpoint: r };
|
|
1849
1878
|
let i;
|
|
1850
1879
|
try {
|
|
@@ -1852,9 +1881,9 @@ function qt(e, t, n) {
|
|
|
1852
1881
|
} catch {
|
|
1853
1882
|
i = e;
|
|
1854
1883
|
}
|
|
1855
|
-
return
|
|
1884
|
+
return Xt(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).`);
|
|
1856
1885
|
}
|
|
1857
|
-
var
|
|
1886
|
+
var Qt = class {
|
|
1858
1887
|
opts;
|
|
1859
1888
|
url;
|
|
1860
1889
|
cspKey;
|
|
@@ -1936,8 +1965,8 @@ var Jt = class {
|
|
|
1936
1965
|
constructor(e) {
|
|
1937
1966
|
this.opts = {
|
|
1938
1967
|
...e,
|
|
1939
|
-
heartbeatMs: e.heartbeatMs ??
|
|
1940
|
-
idleMs: e.idleMs ??
|
|
1968
|
+
heartbeatMs: e.heartbeatMs ?? Pt,
|
|
1969
|
+
idleMs: e.idleMs ?? Ft
|
|
1941
1970
|
}, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`;
|
|
1942
1971
|
try {
|
|
1943
1972
|
let e = new URL(this.url);
|
|
@@ -1954,11 +1983,11 @@ var Jt = class {
|
|
|
1954
1983
|
sdkVersion: this.opts.sdkVersion,
|
|
1955
1984
|
...this.opts.environment
|
|
1956
1985
|
};
|
|
1957
|
-
this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(),
|
|
1986
|
+
this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), Rt), 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 && qt.add(this.cspKey), document.addEventListener("securitypolicyviolation", Yt), X.add(this), this.noteCspBlocked(), this.onVisibility());
|
|
1958
1987
|
}
|
|
1959
1988
|
checkSuspension(e) {
|
|
1960
1989
|
if (this.endedFlag || !this.opts.idleMs) return !1;
|
|
1961
|
-
let t = Math.max(this.opts.idleMs,
|
|
1990
|
+
let t = Math.max(this.opts.idleMs, It);
|
|
1962
1991
|
return this.lastTickMs > 0 && e - this.lastTickMs >= t ? (this.endIdle(this.lastTickMs), !0) : (this.lastTickMs = e, !1);
|
|
1963
1992
|
}
|
|
1964
1993
|
syncIdle(e) {
|
|
@@ -1981,12 +2010,12 @@ var Jt = class {
|
|
|
1981
2010
|
let a = {
|
|
1982
2011
|
fromState: J[e] ?? "PLAYER_STATE_UNSPECIFIED",
|
|
1983
2012
|
toState: J[t] ?? "PLAYER_STATE_UNSPECIFIED",
|
|
1984
|
-
presentation:
|
|
2013
|
+
presentation: Vt[this.presentation]
|
|
1985
2014
|
}, o = this.causeOf(e, t, i);
|
|
1986
2015
|
o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
|
|
1987
2016
|
}
|
|
1988
2017
|
seekInduced(e) {
|
|
1989
|
-
return this.lastSeekMs >= 0 && e - this.lastSeekMs <
|
|
2018
|
+
return this.lastSeekMs >= 0 && e - this.lastSeekMs < Bt;
|
|
1990
2019
|
}
|
|
1991
2020
|
causeOf(e, t, n) {
|
|
1992
2021
|
if (t === "error") return "TRANSITION_CAUSE_ERROR";
|
|
@@ -2060,7 +2089,7 @@ var Jt = class {
|
|
|
2060
2089
|
let r = n ?? "";
|
|
2061
2090
|
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";
|
|
2062
2091
|
if (e === "DRM_CLIENT") return "ERROR_STAGE_DRM";
|
|
2063
|
-
if (
|
|
2092
|
+
if (Wt.has(e)) return "ERROR_STAGE_RESOLVE";
|
|
2064
2093
|
let i = t ?? "";
|
|
2065
2094
|
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";
|
|
2066
2095
|
}
|
|
@@ -2107,14 +2136,14 @@ var Jt = class {
|
|
|
2107
2136
|
if (this.lastRequestCount = g, _ > 0) {
|
|
2108
2137
|
o.requestCountDelta = _;
|
|
2109
2138
|
let e = t.requestTimesMs ?? [], n = e.slice(Math.max(0, e.length - _)).sort((e, t) => e - t);
|
|
2110
|
-
n.length > 0 && (o.requestTimeP50Ms =
|
|
2139
|
+
n.length > 0 && (o.requestTimeP50Ms = Ut(n, 50), o.requestTimeP95Ms = Ut(n, 95));
|
|
2111
2140
|
}
|
|
2112
2141
|
typeof t.latencySeconds == "number" && typeof t.targetLatencySeconds == "number" && (o.driftMs = Math.round((t.latencySeconds - t.targetLatencySeconds) * 1e3)), typeof t.playbackRate == "number" && t.playbackRate > 0 && (o.playbackRate = t.playbackRate), t.qualityPinned === !0 && (o.qualityPinned = !0), t.cdnHost && (o.cdnHost = t.cdnHost), this.maybeEmitMediaInfo(t), this.enqueue({ heartbeat: o }), this.flush(), s > 0 && this.noteFirstBytes();
|
|
2113
2142
|
}
|
|
2114
2143
|
maybeEmitMediaInfo(e) {
|
|
2115
2144
|
if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
|
|
2116
2145
|
let t = {};
|
|
2117
|
-
e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem =
|
|
2146
|
+
e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = Ht[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);
|
|
2118
2147
|
let n = JSON.stringify(t);
|
|
2119
2148
|
n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
|
|
2120
2149
|
}
|
|
@@ -2134,7 +2163,7 @@ var Jt = class {
|
|
|
2134
2163
|
let c = r?.positionMs ?? 0;
|
|
2135
2164
|
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));
|
|
2136
2165
|
let l = e !== "END_REASON_ENDED";
|
|
2137
|
-
l && this.lastStallMs > 0 && n - this.lastStallMs <=
|
|
2166
|
+
l && this.lastStallMs > 0 && n - this.lastStallMs <= zt && (i.abandonAfterStallMs = Math.max(q, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= zt && (i.abandonAfterErrorMs = Math.max(q, 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);
|
|
2138
2167
|
let u = r?.decodedFrames ?? this.lastDecoded;
|
|
2139
2168
|
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");
|
|
2140
2169
|
}
|
|
@@ -2154,13 +2183,13 @@ var Jt = class {
|
|
|
2154
2183
|
};
|
|
2155
2184
|
}
|
|
2156
2185
|
noteCspBlocked() {
|
|
2157
|
-
this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(
|
|
2186
|
+
this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(Jt(new URL(this.url).origin)));
|
|
2158
2187
|
}
|
|
2159
2188
|
cspBlocked() {
|
|
2160
2189
|
return !!this.cspKey && Y.has(this.cspKey);
|
|
2161
2190
|
}
|
|
2162
2191
|
enqueue(e) {
|
|
2163
|
-
if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length >
|
|
2192
|
+
if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > Lt;) {
|
|
2164
2193
|
let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
|
|
2165
2194
|
this.pending.splice(e >= 0 ? e : 0, 1);
|
|
2166
2195
|
}
|
|
@@ -2187,7 +2216,7 @@ var Jt = class {
|
|
|
2187
2216
|
};
|
|
2188
2217
|
//#endregion
|
|
2189
2218
|
//#region src/core/environment.ts
|
|
2190
|
-
function
|
|
2219
|
+
function $t() {
|
|
2191
2220
|
if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
|
|
2192
2221
|
let e;
|
|
2193
2222
|
try {
|
|
@@ -2212,14 +2241,14 @@ function Yt() {
|
|
|
2212
2241
|
integration: "INTEGRATION_KIND_IFRAME"
|
|
2213
2242
|
};
|
|
2214
2243
|
}
|
|
2215
|
-
function
|
|
2244
|
+
function en() {
|
|
2216
2245
|
if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
|
|
2217
2246
|
let e = navigator.userAgentData, t = e?.platform ?? "";
|
|
2218
2247
|
if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
|
|
2219
2248
|
let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
|
|
2220
2249
|
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";
|
|
2221
2250
|
}
|
|
2222
|
-
function
|
|
2251
|
+
function tn() {
|
|
2223
2252
|
let e = typeof navigator < "u" ? navigator.connection : void 0;
|
|
2224
2253
|
if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
|
|
2225
2254
|
let t = {
|
|
@@ -2238,17 +2267,17 @@ function Zt() {
|
|
|
2238
2267
|
}
|
|
2239
2268
|
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;
|
|
2240
2269
|
}
|
|
2241
|
-
function
|
|
2242
|
-
let t =
|
|
2243
|
-
deviceClass:
|
|
2270
|
+
function nn(e) {
|
|
2271
|
+
let t = $t(), n = {
|
|
2272
|
+
deviceClass: en(),
|
|
2244
2273
|
integration: t.integration,
|
|
2245
|
-
...
|
|
2274
|
+
...tn()
|
|
2246
2275
|
};
|
|
2247
2276
|
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);
|
|
2248
2277
|
let r = e.getBoundingClientRect?.();
|
|
2249
2278
|
return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
|
|
2250
2279
|
}
|
|
2251
|
-
async function
|
|
2280
|
+
async function rn() {
|
|
2252
2281
|
let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
|
|
2253
2282
|
if (!e?.getHighEntropyValues) return {};
|
|
2254
2283
|
try {
|
|
@@ -2260,7 +2289,7 @@ async function $t() {
|
|
|
2260
2289
|
}
|
|
2261
2290
|
//#endregion
|
|
2262
2291
|
//#region src/core/version.ts
|
|
2263
|
-
var
|
|
2292
|
+
var an = "2.15.1", on = {
|
|
2264
2293
|
accent: "#E1B600",
|
|
2265
2294
|
accentText: "#1B1C23",
|
|
2266
2295
|
background: "#0e0f13",
|
|
@@ -2271,7 +2300,7 @@ var en = "2.14.0", tn = {
|
|
|
2271
2300
|
border: "rgba(255, 255, 255, 0.1)",
|
|
2272
2301
|
radius: "8px",
|
|
2273
2302
|
fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
|
|
2274
|
-
},
|
|
2303
|
+
}, sn = {
|
|
2275
2304
|
accent: "--havik-accent",
|
|
2276
2305
|
accentText: "--havik-accent-text",
|
|
2277
2306
|
background: "--havik-bg",
|
|
@@ -2283,26 +2312,26 @@ var en = "2.14.0", tn = {
|
|
|
2283
2312
|
radius: "--havik-radius",
|
|
2284
2313
|
fontFamily: "--havik-font"
|
|
2285
2314
|
};
|
|
2286
|
-
function
|
|
2315
|
+
function cn(e, t) {
|
|
2287
2316
|
let n = {
|
|
2288
|
-
...
|
|
2317
|
+
...on,
|
|
2289
2318
|
...t
|
|
2290
2319
|
};
|
|
2291
|
-
for (let t of Object.keys(
|
|
2320
|
+
for (let t of Object.keys(sn)) e.style.setProperty(sn[t], n[t]);
|
|
2292
2321
|
n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
|
|
2293
2322
|
}
|
|
2294
2323
|
//#endregion
|
|
2295
2324
|
//#region src/ui/styles.ts
|
|
2296
|
-
var
|
|
2297
|
-
function
|
|
2298
|
-
let n = e.querySelector(`style#${
|
|
2299
|
-
if (n && (!t ||
|
|
2325
|
+
var ln = "havik-player-styles", un = "\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", dn = /* @__PURE__ */ new WeakMap();
|
|
2326
|
+
function fn(e = document, t) {
|
|
2327
|
+
let n = e.querySelector(`style#${ln}`);
|
|
2328
|
+
if (n && (!t || dn.get(n) === t)) return;
|
|
2300
2329
|
let r = e.createElement("style");
|
|
2301
|
-
r.id =
|
|
2330
|
+
r.id = ln, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = un, dn.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
|
|
2302
2331
|
}
|
|
2303
2332
|
//#endregion
|
|
2304
2333
|
//#region src/ui/icons.ts
|
|
2305
|
-
var
|
|
2334
|
+
var pn = {
|
|
2306
2335
|
play: "<path d=\"M8 5v14l11-7z\"/>",
|
|
2307
2336
|
pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
|
|
2308
2337
|
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\"/>",
|
|
@@ -2316,20 +2345,20 @@ var ln = {
|
|
|
2316
2345
|
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\"/>"
|
|
2317
2346
|
};
|
|
2318
2347
|
function Z(e) {
|
|
2319
|
-
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${
|
|
2348
|
+
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${pn[e]}</svg>`;
|
|
2320
2349
|
}
|
|
2321
2350
|
//#endregion
|
|
2322
2351
|
//#region src/ui/controls.ts
|
|
2323
|
-
var
|
|
2352
|
+
var mn = 3e3, hn = 250, gn = /* @__PURE__ */ new Set([
|
|
2324
2353
|
"GONE",
|
|
2325
2354
|
"NOT_FOUND",
|
|
2326
2355
|
"INVALID_URN",
|
|
2327
2356
|
"UNAUTHORIZED"
|
|
2328
2357
|
]);
|
|
2329
|
-
function
|
|
2330
|
-
return new
|
|
2358
|
+
function _n(e) {
|
|
2359
|
+
return new vn(e);
|
|
2331
2360
|
}
|
|
2332
|
-
var
|
|
2361
|
+
var vn = class {
|
|
2333
2362
|
root;
|
|
2334
2363
|
video;
|
|
2335
2364
|
player;
|
|
@@ -2367,11 +2396,15 @@ var pn = class {
|
|
|
2367
2396
|
interruptedMessage;
|
|
2368
2397
|
errorMessage;
|
|
2369
2398
|
hideTimer;
|
|
2399
|
+
tapTimer;
|
|
2400
|
+
surfacePointerType = "";
|
|
2401
|
+
surfaceWasHidden = !1;
|
|
2402
|
+
pointerClosedMenu = !1;
|
|
2370
2403
|
openMenu = null;
|
|
2371
2404
|
scrubbing = !1;
|
|
2372
2405
|
offFns = [];
|
|
2373
2406
|
constructor(e) {
|
|
2374
|
-
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ??
|
|
2407
|
+
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? mn, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, fn(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");
|
|
2375
2408
|
let t = (e, t, n) => {
|
|
2376
2409
|
let r = this.doc.createElement(e);
|
|
2377
2410
|
if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
|
|
@@ -2420,18 +2453,48 @@ var pn = class {
|
|
|
2420
2453
|
}
|
|
2421
2454
|
wire() {
|
|
2422
2455
|
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.
|
|
2456
|
+
this.on(this.playBtn, "click", () => this.togglePlay()), this.on(this.center, "click", () => this.togglePlay()), this.on(this.overlay, "pointerdown", (e) => {
|
|
2457
|
+
this.surfacePointerType = e.pointerType ?? "", this.surfaceWasHidden = this.root.classList.contains("havik-hide");
|
|
2458
|
+
}), this.on(this.overlay, "click", (e) => {
|
|
2459
|
+
e.target === this.overlay && this.onSurfaceClick();
|
|
2425
2460
|
}), this.on(this.muteBtn, "click", () => this.player.setMuted(!e.muted)), this.on(this.volSlider, "input", () => {
|
|
2426
2461
|
let t = Number(this.volSlider.value);
|
|
2427
2462
|
this.setVolumeFill(t), this.player.setVolume(t), t > 0 && e.muted && this.player.setMuted(!1);
|
|
2428
2463
|
}), 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
2464
|
this.lastError = e, this.statusKind = null, this.syncState();
|
|
2430
|
-
}))
|
|
2465
|
+
}));
|
|
2466
|
+
let t = (e) => e.pointerType !== "touch";
|
|
2467
|
+
this.on(this.root, "pointermove", (e) => {
|
|
2468
|
+
t(e) && this.poke();
|
|
2469
|
+
}), this.on(this.root, "pointerleave", (e) => {
|
|
2470
|
+
t(e) && this.scheduleHide(0);
|
|
2471
|
+
}), 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
2472
|
}
|
|
2432
2473
|
togglePlay() {
|
|
2433
2474
|
this.video.paused || this.video.ended ? this.player.play() : this.player.pause();
|
|
2434
2475
|
}
|
|
2476
|
+
onSurfaceClick() {
|
|
2477
|
+
let e = this.surfacePointerType === "touch";
|
|
2478
|
+
if (this.surfacePointerType = "", !e) {
|
|
2479
|
+
this.tapTimer && clearTimeout(this.tapTimer), this.tapTimer = void 0, this.togglePlay();
|
|
2480
|
+
return;
|
|
2481
|
+
}
|
|
2482
|
+
if (this.pointerClosedMenu) {
|
|
2483
|
+
this.tapTimer && clearTimeout(this.tapTimer), this.tapTimer = void 0, this.poke();
|
|
2484
|
+
return;
|
|
2485
|
+
}
|
|
2486
|
+
if (this.tapTimer) {
|
|
2487
|
+
clearTimeout(this.tapTimer), this.tapTimer = void 0, this.togglePlay();
|
|
2488
|
+
return;
|
|
2489
|
+
}
|
|
2490
|
+
let t = this.surfaceWasHidden;
|
|
2491
|
+
this.tapTimer = setTimeout(() => {
|
|
2492
|
+
this.tapTimer = void 0, this.toggleBar(t);
|
|
2493
|
+
}, hn);
|
|
2494
|
+
}
|
|
2495
|
+
toggleBar(e) {
|
|
2496
|
+
e ? this.poke() : this.hideIfIdle();
|
|
2497
|
+
}
|
|
2435
2498
|
syncPlayPause() {
|
|
2436
2499
|
let e = !this.video.paused && !this.video.ended;
|
|
2437
2500
|
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 +2525,7 @@ var pn = class {
|
|
|
2462
2525
|
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
2526
|
} else {
|
|
2464
2527
|
let r = this.doc.createElement("div");
|
|
2465
|
-
if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !
|
|
2528
|
+
if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !gn.has(this.lastError.code)) {
|
|
2466
2529
|
let e = this.doc.createElement("button");
|
|
2467
2530
|
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
2531
|
}
|
|
@@ -2503,7 +2566,7 @@ var pn = class {
|
|
|
2503
2566
|
let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
|
|
2504
2567
|
this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
|
|
2505
2568
|
}
|
|
2506
|
-
!this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${
|
|
2569
|
+
!this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${yn(this.video.currentTime)} / ${yn(this.video.duration)}`);
|
|
2507
2570
|
}
|
|
2508
2571
|
syncLive() {
|
|
2509
2572
|
let e = this.player.getStats(), t = e.isLive !== !1;
|
|
@@ -2553,17 +2616,17 @@ var pn = class {
|
|
|
2553
2616
|
renderQualityMenu() {
|
|
2554
2617
|
let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
|
|
2555
2618
|
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(
|
|
2619
|
+
for (let n of e) this.menu.append(this.menuItem(bn(n), t === n.index, () => this.player.setQuality(n.index)));
|
|
2557
2620
|
}
|
|
2558
2621
|
renderAudioMenu() {
|
|
2559
2622
|
let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
|
|
2560
2623
|
t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
|
|
2561
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
2624
|
+
for (let t of e) this.menu.append(this.menuItem(xn(t), t.default, () => this.player.setAudioTrack(t.id)));
|
|
2562
2625
|
}
|
|
2563
2626
|
renderCcMenu() {
|
|
2564
2627
|
let e = this.player.getTextTracks(), t = this.doc.createElement("div");
|
|
2565
2628
|
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(
|
|
2629
|
+
for (let t of e) this.menu.append(this.menuItem(Sn(t), !1, () => this.player.setTextTrack(t.id)));
|
|
2567
2630
|
}
|
|
2568
2631
|
onTracksChanged() {
|
|
2569
2632
|
this.openMenu && this.renderMenu(this.openMenu);
|
|
@@ -2597,16 +2660,17 @@ var pn = class {
|
|
|
2597
2660
|
this.root.classList.remove("havik-hide");
|
|
2598
2661
|
}
|
|
2599
2662
|
scheduleHide(e = this.autoHideMs) {
|
|
2600
|
-
this.hideTimer && clearTimeout(this.hideTimer), this.hideTimer = setTimeout(() =>
|
|
2601
|
-
|
|
2602
|
-
|
|
2663
|
+
this.hideTimer && clearTimeout(this.hideTimer), this.hideTimer = setTimeout(() => this.hideIfIdle(), e);
|
|
2664
|
+
}
|
|
2665
|
+
hideIfIdle() {
|
|
2666
|
+
!this.video.paused && !this.video.ended && !this.openMenu && !this.statusBox && this.root.classList.add("havik-hide");
|
|
2603
2667
|
}
|
|
2604
2668
|
onDocPointer(e) {
|
|
2605
|
-
this.openMenu && !this.menu.contains(e.target) && ([
|
|
2669
|
+
this.pointerClosedMenu = !1, this.openMenu && !this.menu.contains(e.target) && ([
|
|
2606
2670
|
this.qualityBtn,
|
|
2607
2671
|
this.audioBtn,
|
|
2608
2672
|
this.ccBtn
|
|
2609
|
-
].some((t) => t.contains(e.target)) || this.closeMenu());
|
|
2673
|
+
].some((t) => t.contains(e.target)) || (this.closeMenu(), this.pointerClosedMenu = !0));
|
|
2610
2674
|
}
|
|
2611
2675
|
onKey(e) {
|
|
2612
2676
|
let t = e.target;
|
|
@@ -2649,7 +2713,7 @@ var pn = class {
|
|
|
2649
2713
|
e.innerHTML = Z(t);
|
|
2650
2714
|
}
|
|
2651
2715
|
destroy() {
|
|
2652
|
-
this.hideTimer && clearTimeout(this.hideTimer);
|
|
2716
|
+
this.hideTimer && clearTimeout(this.hideTimer), this.tapTimer && clearTimeout(this.tapTimer);
|
|
2653
2717
|
for (let e of this.offFns) try {
|
|
2654
2718
|
e();
|
|
2655
2719
|
} catch {}
|
|
@@ -2663,41 +2727,41 @@ var pn = class {
|
|
|
2663
2727
|
this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
|
|
2664
2728
|
}
|
|
2665
2729
|
};
|
|
2666
|
-
function
|
|
2730
|
+
function yn(e) {
|
|
2667
2731
|
(!Number.isFinite(e) || e < 0) && (e = 0);
|
|
2668
2732
|
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
2733
|
return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
|
|
2670
2734
|
}
|
|
2671
|
-
function
|
|
2735
|
+
function bn(e) {
|
|
2672
2736
|
return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
|
|
2673
2737
|
}
|
|
2674
|
-
function
|
|
2738
|
+
function xn(e) {
|
|
2675
2739
|
return e.name || e.lang || `Track ${e.id}`;
|
|
2676
2740
|
}
|
|
2677
|
-
function
|
|
2741
|
+
function Sn(e) {
|
|
2678
2742
|
return e.name || e.lang || `Track ${e.id}`;
|
|
2679
2743
|
}
|
|
2680
2744
|
//#endregion
|
|
2681
2745
|
//#region src/managed/index.ts
|
|
2682
|
-
var
|
|
2683
|
-
function
|
|
2746
|
+
var Cn = 6e3, Q = 45e3, wn = 2e3, Tn = 3, En = 2e3, Dn = 48e4;
|
|
2747
|
+
function On(e) {
|
|
2684
2748
|
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
2749
|
if (e.env !== void 0) return B(e.env);
|
|
2686
2750
|
if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
|
|
2687
2751
|
return e.baseUrl;
|
|
2688
2752
|
}
|
|
2689
|
-
async function
|
|
2753
|
+
async function kn(e) {
|
|
2690
2754
|
if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
|
|
2691
|
-
let t =
|
|
2755
|
+
let t = On(e);
|
|
2692
2756
|
if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
|
|
2693
2757
|
if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
|
|
2694
|
-
let r = new
|
|
2758
|
+
let r = new An({
|
|
2695
2759
|
...e,
|
|
2696
2760
|
baseUrl: t
|
|
2697
2761
|
});
|
|
2698
2762
|
return await r.start(), r;
|
|
2699
2763
|
}
|
|
2700
|
-
var
|
|
2764
|
+
var An = class {
|
|
2701
2765
|
opts;
|
|
2702
2766
|
abort = new AbortController();
|
|
2703
2767
|
listeners = {};
|
|
@@ -2785,7 +2849,7 @@ var Tn = class {
|
|
|
2785
2849
|
setTimeout(() => {
|
|
2786
2850
|
a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
|
|
2787
2851
|
phase: i ? "unavailable" : "tooEarly",
|
|
2788
|
-
retryInMs:
|
|
2852
|
+
retryInMs: Q,
|
|
2789
2853
|
attempt: 1,
|
|
2790
2854
|
elapsedMs: 0,
|
|
2791
2855
|
...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
|
|
@@ -2824,13 +2888,13 @@ var Tn = class {
|
|
|
2824
2888
|
!a && !p() && h();
|
|
2825
2889
|
}, e);
|
|
2826
2890
|
};
|
|
2827
|
-
_(
|
|
2891
|
+
_(Cn), this.armingSub = T({
|
|
2828
2892
|
eventsBaseUrl: t,
|
|
2829
2893
|
matchUrn: this.opts.matchUrn,
|
|
2830
2894
|
credential: this.opts.credential,
|
|
2831
|
-
onAlive: () => _(
|
|
2895
|
+
onAlive: () => _(Q),
|
|
2832
2896
|
onState: (e) => {
|
|
2833
|
-
a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() *
|
|
2897
|
+
a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * wn);
|
|
2834
2898
|
},
|
|
2835
2899
|
onError: () => h()
|
|
2836
2900
|
});
|
|
@@ -2875,19 +2939,19 @@ var Tn = class {
|
|
|
2875
2939
|
if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
|
|
2876
2940
|
let i = e.analytics?.sid;
|
|
2877
2941
|
if (!i) return;
|
|
2878
|
-
let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } =
|
|
2942
|
+
let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = Zt(this.opts.baseUrl, a, e.analytics?.endpoint);
|
|
2879
2943
|
if (!o) {
|
|
2880
2944
|
s && setTimeout(() => {
|
|
2881
2945
|
this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, s));
|
|
2882
2946
|
}, 0);
|
|
2883
2947
|
return;
|
|
2884
2948
|
}
|
|
2885
|
-
let c = this.opts.video, l = new
|
|
2949
|
+
let c = this.opts.video, l = new Qt({
|
|
2886
2950
|
endpoint: o,
|
|
2887
2951
|
sid: i,
|
|
2888
2952
|
player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
|
|
2889
|
-
sdkVersion:
|
|
2890
|
-
environment:
|
|
2953
|
+
sdkVersion: an,
|
|
2954
|
+
environment: nn(c),
|
|
2891
2955
|
idleMs: typeof this.opts.analytics == "object" ? this.opts.analytics.idleMs : void 0,
|
|
2892
2956
|
onIdle: () => this.onAnalyticsIdle(t),
|
|
2893
2957
|
onBlocked: (e) => setTimeout(() => {
|
|
@@ -2908,7 +2972,7 @@ var Tn = class {
|
|
|
2908
2972
|
let u = () => l.noteSeek(), d = () => l.noteVolume(c.muted, c.volume);
|
|
2909
2973
|
c.addEventListener("seeking", u), c.addEventListener("volumechange", d), this.detachMediaListeners = () => {
|
|
2910
2974
|
c.removeEventListener("seeking", u), c.removeEventListener("volumechange", d);
|
|
2911
|
-
}, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()),
|
|
2975
|
+
}, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), rn().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
|
|
2912
2976
|
}
|
|
2913
2977
|
currentPresentation() {
|
|
2914
2978
|
let e = this.opts.video;
|
|
@@ -2934,9 +2998,9 @@ var Tn = class {
|
|
|
2934
2998
|
emit: (e) => this.onEngineEvent(e)
|
|
2935
2999
|
}, o = this.opts.engine ?? "auto";
|
|
2936
3000
|
if (o === "native") return new K(a);
|
|
2937
|
-
if (o === "hls") return new
|
|
3001
|
+
if (o === "hls") return new Nt(a);
|
|
2938
3002
|
if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && W(this.opts.video) && G()) return new K(a);
|
|
2939
|
-
if (at()) return new
|
|
3003
|
+
if (at()) return new Nt(a);
|
|
2940
3004
|
if (W(this.opts.video)) return new K(a);
|
|
2941
3005
|
throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
|
|
2942
3006
|
}
|
|
@@ -2989,7 +3053,7 @@ var Tn = class {
|
|
|
2989
3053
|
r = 0, i = !1;
|
|
2990
3054
|
},
|
|
2991
3055
|
onError: () => {
|
|
2992
|
-
this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >=
|
|
3056
|
+
this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= Tn && (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
3057
|
}
|
|
2994
3058
|
});
|
|
2995
3059
|
}
|
|
@@ -3011,7 +3075,7 @@ var Tn = class {
|
|
|
3011
3075
|
scheduleRejoin(e) {
|
|
3012
3076
|
this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
|
|
3013
3077
|
this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
|
|
3014
|
-
}, Math.random() *
|
|
3078
|
+
}, Math.random() * wn));
|
|
3015
3079
|
}
|
|
3016
3080
|
cancelRejoin() {
|
|
3017
3081
|
this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
|
|
@@ -3035,7 +3099,7 @@ var Tn = class {
|
|
|
3035
3099
|
}
|
|
3036
3100
|
t.controls = !1;
|
|
3037
3101
|
let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
|
|
3038
|
-
r && r.insertBefore(n, t), n.appendChild(t),
|
|
3102
|
+
r && r.insertBefore(n, t), n.appendChild(t), cn(n, this.opts.theme), this.container = n, this.controlBar = _n({
|
|
3039
3103
|
root: n,
|
|
3040
3104
|
video: t,
|
|
3041
3105
|
player: this,
|
|
@@ -3066,7 +3130,7 @@ var Tn = class {
|
|
|
3066
3130
|
}
|
|
3067
3131
|
startStats() {
|
|
3068
3132
|
this.stopStats();
|
|
3069
|
-
let e = Math.max(500, this.opts.statsIntervalMs ??
|
|
3133
|
+
let e = Math.max(500, this.opts.statsIntervalMs ?? En);
|
|
3070
3134
|
this.statsTimer = setInterval(() => {
|
|
3071
3135
|
if (!this.engine) return;
|
|
3072
3136
|
let e = this.engine.getStats();
|
|
@@ -3078,7 +3142,7 @@ var Tn = class {
|
|
|
3078
3142
|
}
|
|
3079
3143
|
scheduleLicenseRefresh() {
|
|
3080
3144
|
this.stopRefresh();
|
|
3081
|
-
let e = this.opts.licenseRefreshMs ??
|
|
3145
|
+
let e = this.opts.licenseRefreshMs ?? Dn;
|
|
3082
3146
|
e && this._descriptor?.drmEnabled && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
|
|
3083
3147
|
}
|
|
3084
3148
|
stopRefresh() {
|
|
@@ -3233,7 +3297,7 @@ function $(e) {
|
|
|
3233
3297
|
}
|
|
3234
3298
|
//#endregion
|
|
3235
3299
|
//#region src/embed/host.ts
|
|
3236
|
-
function
|
|
3300
|
+
function jn(e) {
|
|
3237
3301
|
if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
|
|
3238
3302
|
if (!e.src) throw Error("mountEmbed: opts.src is required");
|
|
3239
3303
|
if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
|
|
@@ -3292,15 +3356,15 @@ function En(e) {
|
|
|
3292
3356
|
}
|
|
3293
3357
|
//#endregion
|
|
3294
3358
|
//#region src/embed/page.ts
|
|
3295
|
-
var
|
|
3296
|
-
function
|
|
3359
|
+
var Mn = /^[A-Za-z0-9:_.-]{1,128}$/;
|
|
3360
|
+
function Nn(e) {
|
|
3297
3361
|
if (typeof e == "boolean") return e;
|
|
3298
3362
|
if (typeof e == "object" && e) {
|
|
3299
3363
|
let t = e.endpoint;
|
|
3300
3364
|
return typeof t == "string" ? { endpoint: t } : {};
|
|
3301
3365
|
}
|
|
3302
3366
|
}
|
|
3303
|
-
function
|
|
3367
|
+
function Pn() {
|
|
3304
3368
|
let e = window.HAVIK_EMBED_CONFIG;
|
|
3305
3369
|
if (e) return {
|
|
3306
3370
|
baseUrl: e.baseUrl ?? "",
|
|
@@ -3310,7 +3374,7 @@ function kn() {
|
|
|
3310
3374
|
autoplay: e.autoplay ?? !0,
|
|
3311
3375
|
muted: e.muted ?? !0,
|
|
3312
3376
|
waitForLive: e.waitForLive ?? !0,
|
|
3313
|
-
analytics:
|
|
3377
|
+
analytics: Nn(e.analytics)
|
|
3314
3378
|
};
|
|
3315
3379
|
let t = new URLSearchParams(location.search);
|
|
3316
3380
|
return {
|
|
@@ -3323,9 +3387,9 @@ function kn() {
|
|
|
3323
3387
|
waitForLive: !0
|
|
3324
3388
|
};
|
|
3325
3389
|
}
|
|
3326
|
-
async function
|
|
3327
|
-
let e =
|
|
3328
|
-
|
|
3390
|
+
async function Fn() {
|
|
3391
|
+
let e = Pn();
|
|
3392
|
+
Ln();
|
|
3329
3393
|
let t = document.createElement("video");
|
|
3330
3394
|
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
3395
|
let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
|
|
@@ -3380,12 +3444,12 @@ async function An() {
|
|
|
3380
3444
|
code: e.code,
|
|
3381
3445
|
message: e.message
|
|
3382
3446
|
})), e.on("autoplayblocked", () => {
|
|
3383
|
-
i({ type: "oddin:autoplayblocked" }),
|
|
3447
|
+
i({ type: "oddin:autoplayblocked" }), In(t, e);
|
|
3384
3448
|
});
|
|
3385
3449
|
}, s = async (r) => {
|
|
3386
3450
|
a?.destroy(), a = null;
|
|
3387
3451
|
try {
|
|
3388
|
-
let n = await
|
|
3452
|
+
let n = await kn({
|
|
3389
3453
|
video: t,
|
|
3390
3454
|
baseUrl: e.baseUrl,
|
|
3391
3455
|
matchUrn: r,
|
|
@@ -3447,7 +3511,7 @@ async function An() {
|
|
|
3447
3511
|
a?.retry();
|
|
3448
3512
|
break;
|
|
3449
3513
|
case "oddin:load":
|
|
3450
|
-
typeof e.matchUrn == "string" &&
|
|
3514
|
+
typeof e.matchUrn == "string" && Mn.test(e.matchUrn) && s(e.matchUrn);
|
|
3451
3515
|
break;
|
|
3452
3516
|
case "oddin:destroy": c();
|
|
3453
3517
|
}
|
|
@@ -3459,18 +3523,18 @@ async function An() {
|
|
|
3459
3523
|
protocol: 1
|
|
3460
3524
|
});
|
|
3461
3525
|
}
|
|
3462
|
-
function
|
|
3526
|
+
function In(e, t) {
|
|
3463
3527
|
if (document.getElementById("havik-tap")) return;
|
|
3464
3528
|
let n = document.createElement("button");
|
|
3465
3529
|
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
3530
|
n.remove(), t.play();
|
|
3467
3531
|
}), document.body.appendChild(n);
|
|
3468
3532
|
}
|
|
3469
|
-
function
|
|
3533
|
+
function Ln() {
|
|
3470
3534
|
let e = document.createElement("style");
|
|
3471
3535
|
e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
|
|
3472
3536
|
}
|
|
3473
3537
|
//#endregion
|
|
3474
|
-
export {
|
|
3538
|
+
export { on as ODDIN_THEME, n as PlaybackError, cn as applyTheme, B as baseUrlForEnv, Fn as bootEmbed, Te as clearDeviceId, kn as createPlayer, E as deriveEventsBaseUrl, He as detectDrmSupport, C as enterVideoFullscreen, w as exitVideoFullscreen, _e as fetchCatalog, ye as fetchTournament, xe as flattenMatches, k as getDeviceId, We as isLowLatencyManifest, $ as isOddinMessage, De as licenseRequestHeaders, jn as mountEmbed, de as pollToLive, y as resolveStream, T as subscribeLiveState, S as videoFullscreenActive, Ce as watchStatus };
|
|
3475
3539
|
|
|
3476
3540
|
//# sourceMappingURL=index.js.map
|