@oddin-gg/havik-player 2.15.2 → 2.15.4
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 +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +195 -134
- package/dist/index.js.map +1 -1
- package/docs/API.md +18 -0
- 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 = .5, mt = 3, ht = .5, gt = 1.1, _t = .25, vt = 5e3, yt = 4e3, bt = 2, xt = 5, St = 2, Ct = 2, wt = 4, Tt = 4, Et = 2e3, Dt = 3e4, Ot = .2, kt = 30, At = 2, jt = 2, Mt = 9, Nt = .5, Pt = 64;
|
|
1223
|
+
function Ft(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 ??= jt;
|
|
1229
|
+
let t = Math.max(12, e.liveSyncDuration + Mt);
|
|
1230
|
+
(e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), It(e, e.liveSyncDuration);
|
|
1231
1231
|
}
|
|
1232
|
-
function
|
|
1232
|
+
function It(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 Lt = class {
|
|
1237
1237
|
name = "hls";
|
|
1238
1238
|
hls = null;
|
|
1239
1239
|
deps;
|
|
@@ -1290,12 +1290,22 @@ var Nt = class {
|
|
|
1290
1290
|
savedLiveMaxLatency;
|
|
1291
1291
|
savedMaxSyncRate;
|
|
1292
1292
|
chaseLatency = -1;
|
|
1293
|
+
catchUpRate = 1;
|
|
1294
|
+
catchUpEngaged = !1;
|
|
1295
|
+
onTimeUpdate = null;
|
|
1293
1296
|
chaseTicks = 0;
|
|
1294
1297
|
liveRetargets = 0;
|
|
1295
1298
|
capObserver = null;
|
|
1296
1299
|
capTimer = null;
|
|
1297
1300
|
sizeCapLevel = null;
|
|
1298
1301
|
bitrateCapLevel = -1;
|
|
1302
|
+
fpsCapBitrate = null;
|
|
1303
|
+
fpsCapFailedHeight = null;
|
|
1304
|
+
fpsCapManaged = !0;
|
|
1305
|
+
fpsLastFrames = -1;
|
|
1306
|
+
fpsLastDropped = -1;
|
|
1307
|
+
fpsBadTicks = 0;
|
|
1308
|
+
fpsDownCaps = 0;
|
|
1299
1309
|
sizeCapManaged = !0;
|
|
1300
1310
|
dprMedia = null;
|
|
1301
1311
|
onDprChange = null;
|
|
@@ -1322,7 +1332,7 @@ var Nt = class {
|
|
|
1322
1332
|
t - r.currentTime > Math.max(n * 3, 8) && (r.currentTime = t);
|
|
1323
1333
|
}, document.addEventListener("visibilitychange", this.onVisibility)), this.onSeeking = () => {
|
|
1324
1334
|
this.clampOverSeek(), this.loadSuspended && (this.resumeLoading(), this.schedulePauseSuspend());
|
|
1325
|
-
}, r.addEventListener("seeking", this.onSeeking), this.onPause = () => this.schedulePauseSuspend(), this.onPlay = () => this.resumeLoading(), r.addEventListener("pause", this.onPause), r.addEventListener("play", this.onPlay), this.sizeCapManaged && typeof ResizeObserver < "u" && (this.capObserver = new ResizeObserver(() => this.onMediaResize()), this.capObserver.observe(r), this.watchDprChanges());
|
|
1335
|
+
}, r.addEventListener("seeking", this.onSeeking), this.onTimeUpdate = () => this.updateCatchUpBand(), r.addEventListener("timeupdate", this.onTimeUpdate), this.onPause = () => this.schedulePauseSuspend(), this.onPlay = () => this.resumeLoading(), r.addEventListener("pause", this.onPause), r.addEventListener("play", this.onPlay), this.sizeCapManaged && typeof ResizeObserver < "u" && (this.capObserver = new ResizeObserver(() => this.onMediaResize()), this.capObserver.observe(r), this.watchDprChanges());
|
|
1326
1336
|
}
|
|
1327
1337
|
schedulePauseSuspend() {
|
|
1328
1338
|
this.pauseSuspendTimer || this.loadSuspended || this.stopped || !this.loadStarted || (this.pauseSuspendTimer = setTimeout(() => {
|
|
@@ -1336,10 +1346,10 @@ var Nt = class {
|
|
|
1336
1346
|
}
|
|
1337
1347
|
this.loadSuspended = !0;
|
|
1338
1348
|
}
|
|
1339
|
-
},
|
|
1349
|
+
}, vt));
|
|
1340
1350
|
}
|
|
1341
1351
|
resumeLoading() {
|
|
1342
|
-
if (this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.resetChaseWindow(), this.loadSuspended && (this.loadSuspended = !1, !this.stopped && this.hls)) {
|
|
1352
|
+
if (this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.resetChaseWindow(), this.resetFrameDropWindow(), this.loadSuspended && (this.loadSuspended = !1, !this.stopped && this.hls)) {
|
|
1343
1353
|
this.lastAdvanceMs = Date.now(), this.lastFragLoadingMs = 0, this.resetStarvationWindow(), this.resetChaseWindow(), this.clearKick();
|
|
1344
1354
|
try {
|
|
1345
1355
|
this.hls.startLoad(-1);
|
|
@@ -1368,11 +1378,11 @@ var Nt = class {
|
|
|
1368
1378
|
let t = e.liveSyncPosition;
|
|
1369
1379
|
if (typeof t != "number" || !Number.isFinite(t)) return;
|
|
1370
1380
|
let { video: n } = this.deps;
|
|
1371
|
-
n.currentTime <= t +
|
|
1381
|
+
n.currentTime <= t + Nt || (n.currentTime = t);
|
|
1372
1382
|
}
|
|
1373
1383
|
startHls() {
|
|
1374
1384
|
let { video: t, descriptor: n, credential: r, deviceId: i, userId: a, lowLatency: o, debug: s, hlsConfig: c, emit: l } = this.deps;
|
|
1375
|
-
this.clearKick(), this.resetStarvationWindow(), this.resetChaseWindow(), this.lastFragLoadingMs = 0, this.lastEndSN = -1, this.lastAdvanceMs = 0;
|
|
1385
|
+
this.clearKick(), this.resetStarvationWindow(), this.resetChaseWindow(), this.resetFrameDropWindow(), this.lastFragLoadingMs = 0, this.lastEndSN = -1, this.lastAdvanceMs = 0;
|
|
1376
1386
|
let u = n.drmEnabled && !!(n.drm?.widevine?.licenseUrl || n.drm?.fairplay?.licenseUrl), d = {
|
|
1377
1387
|
debug: s === !0,
|
|
1378
1388
|
enableWorker: !o,
|
|
@@ -1415,7 +1425,7 @@ var Nt = class {
|
|
|
1415
1425
|
}), t = tt(e);
|
|
1416
1426
|
t && (this.prefetch = e, d.fLoader = t);
|
|
1417
1427
|
}
|
|
1418
|
-
|
|
1428
|
+
Ft(d), this.catchUpRate = typeof d.maxLiveSyncPlaybackRate == "number" ? d.maxLiveSyncPlaybackRate : 1, this.catchUpEngaged = !1, this.catchUpRate > 1 && (d.maxLiveSyncPlaybackRate = 1), this.sizeCapManaged = d.capLevelToPlayerSize !== !0, this.fpsCapManaged = !(c && "capLevelOnFPSDrop" in c), this.loadStarted = d.autoStartLoad !== !1;
|
|
1419
1429
|
let f = new e(d);
|
|
1420
1430
|
if (this.hls = f, f.on(e.Events.MANIFEST_PARSED, () => {
|
|
1421
1431
|
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" });
|
|
@@ -1430,7 +1440,7 @@ var Nt = class {
|
|
|
1430
1440
|
let n = t?.frag?.stats, r = n?.loaded;
|
|
1431
1441
|
typeof r == "number" && (this.bytesLoaded += r), !this.reportedFirstBytes && typeof r == "number" && r > 0 && (this.reportedFirstBytes = !0, l({ type: "firstBytes" }));
|
|
1432
1442
|
let i = n?.loading;
|
|
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 >
|
|
1443
|
+
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 > Pt && this.requestTimesMs.shift());
|
|
1434
1444
|
let a = t?.frag?.url;
|
|
1435
1445
|
if (a) {
|
|
1436
1446
|
let e = H(a);
|
|
@@ -1454,7 +1464,7 @@ var Nt = class {
|
|
|
1454
1464
|
f.on(e.Events.KEY_LOADED, () => {
|
|
1455
1465
|
u && !this.reportedDrmReady && (this.reportedDrmReady = !0, l({ type: "drmReady" }));
|
|
1456
1466
|
}), this.endWatchdog && clearInterval(this.endWatchdog), this.endWatchdog = setInterval(() => {
|
|
1457
|
-
this.checkEndStale(), this.checkStarvation(), this.checkFutileChase();
|
|
1467
|
+
this.checkEndStale(), this.checkStarvation(), this.updateCatchUpBand(), this.checkFutileChase(), this.checkFrameDrops();
|
|
1458
1468
|
}, dt), f.on(e.Events.LEVEL_SWITCHED, (e, t) => {
|
|
1459
1469
|
this.renditionSwitches += 1;
|
|
1460
1470
|
let n = this.lastLevelIndex >= 0 ? f.levels[this.lastLevelIndex] : void 0, r = f.levels[t.level];
|
|
@@ -1519,7 +1529,7 @@ var Nt = class {
|
|
|
1519
1529
|
return;
|
|
1520
1530
|
}
|
|
1521
1531
|
this.capTimer && clearTimeout(this.capTimer);
|
|
1522
|
-
let t = e > this.sizeCapLevel ?
|
|
1532
|
+
let t = e > this.sizeCapLevel ? Et : Dt;
|
|
1523
1533
|
this.capTimer = setTimeout(() => {
|
|
1524
1534
|
this.capTimer = null, this.computeSizeCapTarget() === e ? this.applySizeCap() : this.onMediaResize();
|
|
1525
1535
|
}, t);
|
|
@@ -1555,8 +1565,12 @@ var Nt = class {
|
|
|
1555
1565
|
pushCap() {
|
|
1556
1566
|
let e = this.hls;
|
|
1557
1567
|
if (!e) return;
|
|
1558
|
-
let t =
|
|
1559
|
-
|
|
1568
|
+
let t = [
|
|
1569
|
+
this.sizeCapLevel ?? -1,
|
|
1570
|
+
this.bitrateCapLevel,
|
|
1571
|
+
this.fpsCapLevel(e)
|
|
1572
|
+
].filter((e) => e >= 0);
|
|
1573
|
+
e.autoLevelCapping = t.length ? Math.min(...t) : -1;
|
|
1560
1574
|
}
|
|
1561
1575
|
getAudioTracks() {
|
|
1562
1576
|
return this.hls ? this.hls.audioTracks.map((e) => ({
|
|
@@ -1596,6 +1610,7 @@ var Nt = class {
|
|
|
1596
1610
|
playlistBytesLoaded: this.playlistBytesLoaded,
|
|
1597
1611
|
renditionSwitches: this.renditionSwitches,
|
|
1598
1612
|
liveRetargets: this.liveRetargets,
|
|
1613
|
+
fpsDownCaps: this.fpsDownCaps,
|
|
1599
1614
|
licenseTimeMs: this.licenseTimeMs,
|
|
1600
1615
|
upshifts: this.upshifts,
|
|
1601
1616
|
downshifts: this.downshifts,
|
|
@@ -1650,7 +1665,7 @@ var Nt = class {
|
|
|
1650
1665
|
this.resetStarvationWindow();
|
|
1651
1666
|
return;
|
|
1652
1667
|
}
|
|
1653
|
-
let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < ft, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs >
|
|
1668
|
+
let n = Date.now(), r = this.lastAdvanceMs !== 0 && n - this.lastAdvanceMs < ft, i = this.lastFragLoadingMs !== 0 && n - this.lastFragLoadingMs > yt, a = t.readyState < HTMLMediaElement.HAVE_FUTURE_DATA && !t.paused, { totalFrames: o, dropped: s } = U(t), c = o - s, l = c !== this.starvedFrames;
|
|
1654
1669
|
if (this.starvedFrames = c, !a || l) {
|
|
1655
1670
|
(t.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA || l) && this.clearKick(), this.resetStarvationWindow();
|
|
1656
1671
|
return;
|
|
@@ -1659,12 +1674,12 @@ var Nt = class {
|
|
|
1659
1674
|
this.resetStarvationWindow();
|
|
1660
1675
|
return;
|
|
1661
1676
|
}
|
|
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 >
|
|
1677
|
+
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 > wt && this.recentMainFrags.shift(), this.clearRecoveryReset(), this.kickPending && this.kickTicks >= St && (this.clearKick(), this.starvationRebuilds < Ct)) {
|
|
1663
1678
|
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");
|
|
1664
1679
|
return;
|
|
1665
1680
|
}
|
|
1666
|
-
let u = i && this.starvedTicks >=
|
|
1667
|
-
if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >=
|
|
1681
|
+
let u = i && this.starvedTicks >= bt, d = !i && this.deadTicks >= wt && this.recentMainFrags.reduce((e, t) => e + t, 0) >= Tt;
|
|
1682
|
+
if ((u || d) && (this.resetStarvationWindow(), !(this.starvationKicks >= xt))) {
|
|
1668
1683
|
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");
|
|
1669
1684
|
try {
|
|
1670
1685
|
e.stopLoad(), e.startLoad(-1);
|
|
@@ -1673,6 +1688,49 @@ var Nt = class {
|
|
|
1673
1688
|
}
|
|
1674
1689
|
}
|
|
1675
1690
|
}
|
|
1691
|
+
updateCatchUpBand() {
|
|
1692
|
+
let e = this.hls;
|
|
1693
|
+
if (!e || this.catchUpRate <= 1 || this.chaseSuspended || this.loadSuspended) return;
|
|
1694
|
+
let t = e.latency, n = e.targetLatency;
|
|
1695
|
+
if (typeof t != "number" || typeof n != "number" || !Number.isFinite(t)) return;
|
|
1696
|
+
let r = t - n;
|
|
1697
|
+
!this.catchUpEngaged && r > pt ? this.catchUpEngaged = !0 : this.catchUpEngaged && r <= pt && this.deps.video.playbackRate === 1 && (this.catchUpEngaged = !1);
|
|
1698
|
+
let i = this.catchUpEngaged ? this.catchUpRate : 1;
|
|
1699
|
+
e.config.maxLiveSyncPlaybackRate !== i && (e.config.maxLiveSyncPlaybackRate = i);
|
|
1700
|
+
}
|
|
1701
|
+
fpsCapLevel(e) {
|
|
1702
|
+
if (this.fpsCapBitrate == null) return -1;
|
|
1703
|
+
let t = this.fpsCapFailedHeight, n = -1;
|
|
1704
|
+
return e.levels.forEach((e, r) => {
|
|
1705
|
+
let i = t == null || !(e.height > 0) || e.height < t;
|
|
1706
|
+
e.bitrate <= this.fpsCapBitrate && i && (n = r);
|
|
1707
|
+
}), n < 0 ? 0 : n;
|
|
1708
|
+
}
|
|
1709
|
+
resetFrameDropWindow() {
|
|
1710
|
+
this.fpsLastFrames = -1, this.fpsLastDropped = -1, this.fpsBadTicks = 0;
|
|
1711
|
+
}
|
|
1712
|
+
checkFrameDrops() {
|
|
1713
|
+
let e = this.hls, { video: t } = this.deps;
|
|
1714
|
+
if (!e || !this.fpsCapManaged || this.loadSuspended || t.paused || !e.autoLevelEnabled || typeof document < "u" && document.hidden) {
|
|
1715
|
+
this.resetFrameDropWindow();
|
|
1716
|
+
return;
|
|
1717
|
+
}
|
|
1718
|
+
let { totalFrames: n, dropped: r } = U(t), i = this.fpsLastFrames < 0, a = n - this.fpsLastFrames, o = r - this.fpsLastDropped;
|
|
1719
|
+
if (this.fpsLastFrames = n, this.fpsLastDropped = r, i || a < kt || o < 0) {
|
|
1720
|
+
this.fpsBadTicks = 0;
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
if (o <= Ot * a) {
|
|
1724
|
+
this.fpsBadTicks = 0;
|
|
1725
|
+
return;
|
|
1726
|
+
}
|
|
1727
|
+
if (this.fpsBadTicks += 1, this.fpsBadTicks < At) return;
|
|
1728
|
+
this.fpsBadTicks = 0;
|
|
1729
|
+
let s = e.levels, c = s[e.currentLevel];
|
|
1730
|
+
if (!c) return;
|
|
1731
|
+
let l = s.filter((e) => e.height > 0 && e.height < c.height), u = (l.length ? l : s.filter((e) => e.bitrate < c.bitrate)).map((e) => e.bitrate).sort((e, t) => t - e)[0];
|
|
1732
|
+
u != null && (this.fpsCapBitrate != null && u >= this.fpsCapBitrate || (this.fpsCapBitrate = u, l.length && (this.fpsCapFailedHeight = c.height), this.fpsDownCaps += 1, console.warn(`havik-player: dropping ${Math.round(100 * o / a)}% of frames at ${c.height}p — capping quality below it`), this.pushCap()));
|
|
1733
|
+
}
|
|
1676
1734
|
resetChaseWindow() {
|
|
1677
1735
|
this.chaseLatency = -1, this.chaseTicks = 0;
|
|
1678
1736
|
}
|
|
@@ -1683,24 +1741,27 @@ var Nt = class {
|
|
|
1683
1741
|
return;
|
|
1684
1742
|
}
|
|
1685
1743
|
let n = e.latency, r = t.playbackRate, i = e.config.maxLiveSyncPlaybackRate;
|
|
1686
|
-
if (!(!t.paused && typeof i == "number" && i > 1 && r >= Math.min(
|
|
1744
|
+
if (!(!t.paused && typeof i == "number" && i > 1 && r >= Math.min(gt, i) && r <= i && typeof n == "number" && Number.isFinite(n))) {
|
|
1687
1745
|
this.resetChaseWindow();
|
|
1688
1746
|
return;
|
|
1689
1747
|
}
|
|
1690
|
-
if (this.chaseLatency < 0 || this.chaseLatency - n >=
|
|
1748
|
+
if (this.chaseLatency < 0 || this.chaseLatency - n >= ht) {
|
|
1691
1749
|
this.chaseLatency = n, this.chaseTicks = 0;
|
|
1692
1750
|
return;
|
|
1693
1751
|
}
|
|
1694
|
-
if (this.chaseTicks += 1, this.chaseTicks <
|
|
1752
|
+
if (this.chaseTicks += 1, this.chaseTicks < mt) return;
|
|
1695
1753
|
this.resetChaseWindow();
|
|
1696
1754
|
let a = typeof e.config.liveMaxLatencyDuration == "number" ? e.config.liveMaxLatencyDuration : e.maxLatency;
|
|
1697
1755
|
if (!(a > 1)) return;
|
|
1698
1756
|
let o = typeof e.targetLatency == "number" ? e.targetLatency : 0, s = Math.min(n, a - 1);
|
|
1699
|
-
s < o +
|
|
1757
|
+
s < o + _t || (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
1758
|
}
|
|
1701
1759
|
suspendChasing() {
|
|
1702
1760
|
let e = this.hls;
|
|
1703
|
-
|
|
1761
|
+
if (!e) return;
|
|
1762
|
+
this.chaseSuspended = !0, this.savedLiveMaxLatency = e.config.liveMaxLatencyDuration, this.savedMaxSyncRate = e.config.maxLiveSyncPlaybackRate, e.config.liveMaxLatencyDuration = 2 ** 53 - 1, e.config.maxLiveSyncPlaybackRate = 1;
|
|
1763
|
+
let { video: t } = this.deps, n = this.savedMaxSyncRate;
|
|
1764
|
+
t.playbackRate > 1 && typeof n == "number" && t.playbackRate <= n && (t.playbackRate = 1);
|
|
1704
1765
|
}
|
|
1705
1766
|
resumeChasing() {
|
|
1706
1767
|
this.chaseSuspended = !1;
|
|
@@ -1723,7 +1784,7 @@ var Nt = class {
|
|
|
1723
1784
|
} catch {}
|
|
1724
1785
|
}
|
|
1725
1786
|
destroy() {
|
|
1726
|
-
if (this.prefetch?.destroy(), this.prefetch = null, this.clearRecoveryReset(), this.capTimer &&= (clearTimeout(this.capTimer), null), this.capObserver &&= (this.capObserver.disconnect(), null), this.unwatchDprChanges(), this.endWatchdog &&= (clearInterval(this.endWatchdog), null), this.onVisibility && typeof document < "u" && (document.removeEventListener("visibilitychange", this.onVisibility), this.onVisibility = null), this.onSeeking &&= (this.deps.video.removeEventListener("seeking", this.onSeeking), null), this.onPause &&= (this.deps.video.removeEventListener("pause", this.onPause), null), this.onPlay &&= (this.deps.video.removeEventListener("play", this.onPlay), null), this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.networkRetryTimer &&= (clearTimeout(this.networkRetryTimer), null), this.licenseRetryTimer &&= (clearTimeout(this.licenseRetryTimer), null), this.hls) {
|
|
1787
|
+
if (this.prefetch?.destroy(), this.prefetch = null, this.clearRecoveryReset(), this.capTimer &&= (clearTimeout(this.capTimer), null), this.capObserver &&= (this.capObserver.disconnect(), null), this.unwatchDprChanges(), this.endWatchdog &&= (clearInterval(this.endWatchdog), null), this.onVisibility && typeof document < "u" && (document.removeEventListener("visibilitychange", this.onVisibility), this.onVisibility = null), this.onSeeking &&= (this.deps.video.removeEventListener("seeking", this.onSeeking), null), this.onTimeUpdate &&= (this.deps.video.removeEventListener("timeupdate", this.onTimeUpdate), null), this.onPause &&= (this.deps.video.removeEventListener("pause", this.onPause), null), this.onPlay &&= (this.deps.video.removeEventListener("play", this.onPlay), null), this.pauseSuspendTimer &&= (clearTimeout(this.pauseSuspendTimer), null), this.networkRetryTimer &&= (clearTimeout(this.networkRetryTimer), null), this.licenseRetryTimer &&= (clearTimeout(this.licenseRetryTimer), null), this.hls) {
|
|
1727
1788
|
try {
|
|
1728
1789
|
this.hls.destroy();
|
|
1729
1790
|
} catch (e) {
|
|
@@ -1811,19 +1872,19 @@ var Nt = class {
|
|
|
1811
1872
|
let r = e.response?.code ?? 0, a = e.details ?? e.type, o = e.reason ?? e.error?.message ?? "";
|
|
1812
1873
|
return new n(t === "DRM" ? i(r) : r === 401 ? "UNAUTHORIZED" : r === 403 ? "FORBIDDEN" : "INTERNAL", r, `${t} error: ${a}${o ? ` (${o})` : ""}`);
|
|
1813
1874
|
}
|
|
1814
|
-
},
|
|
1875
|
+
}, Rt = 15e3, zt = 6e5, Bt = 3e5, Vt = 100, q = 1, Ht = 1e3, Ut = 3e4, Wt = 1e3, Gt = {
|
|
1815
1876
|
inline: "PRESENTATION_INLINE",
|
|
1816
1877
|
fullscreen: "PRESENTATION_FULLSCREEN",
|
|
1817
1878
|
pip: "PRESENTATION_PIP"
|
|
1818
|
-
},
|
|
1879
|
+
}, Kt = {
|
|
1819
1880
|
widevine: "KEY_SYSTEM_WIDEVINE",
|
|
1820
1881
|
fairplay: "KEY_SYSTEM_FAIRPLAY",
|
|
1821
1882
|
none: "KEY_SYSTEM_NONE"
|
|
1822
1883
|
};
|
|
1823
|
-
function
|
|
1884
|
+
function qt(e, t) {
|
|
1824
1885
|
return e.length === 0 ? 0 : e[Math.min(e.length, Math.max(1, Math.ceil(t / 100 * e.length))) - 1] ?? 0;
|
|
1825
1886
|
}
|
|
1826
|
-
var
|
|
1887
|
+
var Jt = /* @__PURE__ */ new Set([
|
|
1827
1888
|
"INVALID_URN",
|
|
1828
1889
|
"NOT_FOUND",
|
|
1829
1890
|
"GONE",
|
|
@@ -1843,7 +1904,7 @@ var Wt = /* @__PURE__ */ new Set([
|
|
|
1843
1904
|
ended: "PLAYER_STATE_ENDED",
|
|
1844
1905
|
error: "PLAYER_STATE_ERROR"
|
|
1845
1906
|
};
|
|
1846
|
-
function
|
|
1907
|
+
function Yt(e) {
|
|
1847
1908
|
try {
|
|
1848
1909
|
let t = new URL(e), [n, ...r] = t.hostname.split(".");
|
|
1849
1910
|
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);
|
|
@@ -1851,8 +1912,8 @@ function Gt(e) {
|
|
|
1851
1912
|
return;
|
|
1852
1913
|
}
|
|
1853
1914
|
}
|
|
1854
|
-
var
|
|
1855
|
-
function
|
|
1915
|
+
var Xt = (e, t) => e.replace(/\/$/, "").toLowerCase() === t.replace(/\/$/, "").toLowerCase(), Zt = /* @__PURE__ */ new Set(), Y = /* @__PURE__ */ new Set(), X = /* @__PURE__ */ new Set(), Qt = (e) => `this page's Content-Security-Policy blocks QoE beacons. Add ${e} to connect-src; until then none are sent.`;
|
|
1916
|
+
function $t(e) {
|
|
1856
1917
|
if (e.disposition !== "enforce" || e.effectiveDirective !== "connect-src") return;
|
|
1857
1918
|
let t;
|
|
1858
1919
|
try {
|
|
@@ -1861,19 +1922,19 @@ function Yt(e) {
|
|
|
1861
1922
|
} catch {
|
|
1862
1923
|
return;
|
|
1863
1924
|
}
|
|
1864
|
-
if (
|
|
1865
|
-
Y.add(t), g(
|
|
1925
|
+
if (Zt.has(t) && !Y.has(t)) {
|
|
1926
|
+
Y.add(t), g(Qt(new URL(t).origin));
|
|
1866
1927
|
for (let e of X) e.noteCspBlocked();
|
|
1867
1928
|
}
|
|
1868
1929
|
}
|
|
1869
|
-
function
|
|
1930
|
+
function en(e, t) {
|
|
1870
1931
|
return g(t, e), { disabledReason: t };
|
|
1871
1932
|
}
|
|
1872
|
-
function
|
|
1873
|
-
if (n) return t && !
|
|
1933
|
+
function tn(e, t, n) {
|
|
1934
|
+
if (n) return t && !Xt(t, n) && g(`analytics.endpoint "${t}" ignored; the server named "${n}".`), { endpoint: n };
|
|
1874
1935
|
if (t) return { endpoint: t };
|
|
1875
|
-
if (t !== void 0) return
|
|
1876
|
-
let r =
|
|
1936
|
+
if (t !== void 0) return en("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).");
|
|
1937
|
+
let r = Yt(e);
|
|
1877
1938
|
if (r) return { endpoint: r };
|
|
1878
1939
|
let i;
|
|
1879
1940
|
try {
|
|
@@ -1881,9 +1942,9 @@ function Zt(e, t, n) {
|
|
|
1881
1942
|
} catch {
|
|
1882
1943
|
i = e;
|
|
1883
1944
|
}
|
|
1884
|
-
return
|
|
1945
|
+
return en(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).`);
|
|
1885
1946
|
}
|
|
1886
|
-
var
|
|
1947
|
+
var nn = class {
|
|
1887
1948
|
opts;
|
|
1888
1949
|
url;
|
|
1889
1950
|
cspKey;
|
|
@@ -1965,8 +2026,8 @@ var Qt = class {
|
|
|
1965
2026
|
constructor(e) {
|
|
1966
2027
|
this.opts = {
|
|
1967
2028
|
...e,
|
|
1968
|
-
heartbeatMs: e.heartbeatMs ??
|
|
1969
|
-
idleMs: e.idleMs ??
|
|
2029
|
+
heartbeatMs: e.heartbeatMs ?? Rt,
|
|
2030
|
+
idleMs: e.idleMs ?? zt
|
|
1970
2031
|
}, this.url = `${e.endpoint.replace(/\/$/, "")}/v1/beacons`;
|
|
1971
2032
|
try {
|
|
1972
2033
|
let e = new URL(this.url);
|
|
@@ -1983,11 +2044,11 @@ var Qt = class {
|
|
|
1983
2044
|
sdkVersion: this.opts.sdkVersion,
|
|
1984
2045
|
...this.opts.environment
|
|
1985
2046
|
};
|
|
1986
|
-
this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(),
|
|
2047
|
+
this.pendingStart = e, this.enqueue({ sessionStart: e }), this.firstFlushTimer = setTimeout(() => this.flush(), Ht), 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 && Zt.add(this.cspKey), document.addEventListener("securitypolicyviolation", $t), X.add(this), this.noteCspBlocked(), this.onVisibility());
|
|
1987
2048
|
}
|
|
1988
2049
|
checkSuspension(e) {
|
|
1989
2050
|
if (this.endedFlag || !this.opts.idleMs) return !1;
|
|
1990
|
-
let t = Math.max(this.opts.idleMs,
|
|
2051
|
+
let t = Math.max(this.opts.idleMs, Bt);
|
|
1991
2052
|
return this.lastTickMs > 0 && e - this.lastTickMs >= t ? (this.endIdle(this.lastTickMs), !0) : (this.lastTickMs = e, !1);
|
|
1992
2053
|
}
|
|
1993
2054
|
syncIdle(e) {
|
|
@@ -2010,12 +2071,12 @@ var Qt = class {
|
|
|
2010
2071
|
let a = {
|
|
2011
2072
|
fromState: J[e] ?? "PLAYER_STATE_UNSPECIFIED",
|
|
2012
2073
|
toState: J[t] ?? "PLAYER_STATE_UNSPECIFIED",
|
|
2013
|
-
presentation:
|
|
2074
|
+
presentation: Gt[this.presentation]
|
|
2014
2075
|
}, o = this.causeOf(e, t, i);
|
|
2015
2076
|
o && (a.cause = o), n > 0 && (a.positionMs = Math.round(n)), r && (a.renditionHeight = r), this.enqueue({ stateChange: a });
|
|
2016
2077
|
}
|
|
2017
2078
|
seekInduced(e) {
|
|
2018
|
-
return this.lastSeekMs >= 0 && e - this.lastSeekMs <
|
|
2079
|
+
return this.lastSeekMs >= 0 && e - this.lastSeekMs < Wt;
|
|
2019
2080
|
}
|
|
2020
2081
|
causeOf(e, t, n) {
|
|
2021
2082
|
if (t === "error") return "TRANSITION_CAUSE_ERROR";
|
|
@@ -2089,7 +2150,7 @@ var Qt = class {
|
|
|
2089
2150
|
let r = n ?? "";
|
|
2090
2151
|
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";
|
|
2091
2152
|
if (e === "DRM_CLIENT") return "ERROR_STAGE_DRM";
|
|
2092
|
-
if (
|
|
2153
|
+
if (Jt.has(e)) return "ERROR_STAGE_RESOLVE";
|
|
2093
2154
|
let i = t ?? "";
|
|
2094
2155
|
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";
|
|
2095
2156
|
}
|
|
@@ -2136,14 +2197,14 @@ var Qt = class {
|
|
|
2136
2197
|
if (this.lastRequestCount = g, _ > 0) {
|
|
2137
2198
|
o.requestCountDelta = _;
|
|
2138
2199
|
let e = t.requestTimesMs ?? [], n = e.slice(Math.max(0, e.length - _)).sort((e, t) => e - t);
|
|
2139
|
-
n.length > 0 && (o.requestTimeP50Ms =
|
|
2200
|
+
n.length > 0 && (o.requestTimeP50Ms = qt(n, 50), o.requestTimeP95Ms = qt(n, 95));
|
|
2140
2201
|
}
|
|
2141
2202
|
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();
|
|
2142
2203
|
}
|
|
2143
2204
|
maybeEmitMediaInfo(e) {
|
|
2144
2205
|
if (!(e.videoCodec ?? e.keySystem ?? e.ladderHeights)) return;
|
|
2145
2206
|
let t = {};
|
|
2146
|
-
e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem =
|
|
2207
|
+
e.videoCodec && (t.videoCodec = e.videoCodec), e.audioCodec && (t.audioCodec = e.audioCodec), e.keySystem && (t.keySystem = Kt[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);
|
|
2147
2208
|
let n = JSON.stringify(t);
|
|
2148
2209
|
n !== this.mediaInfoSignature && (this.mediaInfoSignature = n, this.enqueue({ mediaInfo: t }));
|
|
2149
2210
|
}
|
|
@@ -2163,7 +2224,7 @@ var Qt = class {
|
|
|
2163
2224
|
let c = r?.positionMs ?? 0;
|
|
2164
2225
|
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));
|
|
2165
2226
|
let l = e !== "END_REASON_ENDED";
|
|
2166
|
-
l && this.lastStallMs > 0 && n - this.lastStallMs <=
|
|
2227
|
+
l && this.lastStallMs > 0 && n - this.lastStallMs <= Ut && (i.abandonAfterStallMs = Math.max(q, Math.round(n - this.lastStallMs))), l && this.lastErrorMs > 0 && n - this.lastErrorMs <= Ut && (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);
|
|
2167
2228
|
let u = r?.decodedFrames ?? this.lastDecoded;
|
|
2168
2229
|
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");
|
|
2169
2230
|
}
|
|
@@ -2183,13 +2244,13 @@ var Qt = class {
|
|
|
2183
2244
|
};
|
|
2184
2245
|
}
|
|
2185
2246
|
noteCspBlocked() {
|
|
2186
|
-
this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(
|
|
2247
|
+
this.cspBlockedNoted || this.endedFlag || !this.cspBlocked() || (this.cspBlockedNoted = !0, this.opts.onBlocked?.(Qt(new URL(this.url).origin)));
|
|
2187
2248
|
}
|
|
2188
2249
|
cspBlocked() {
|
|
2189
2250
|
return !!this.cspKey && Y.has(this.cspKey);
|
|
2190
2251
|
}
|
|
2191
2252
|
enqueue(e) {
|
|
2192
|
-
if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length >
|
|
2253
|
+
if (!this.cspBlocked()) for (this.pending.push(this.wrap(e)); this.pending.length > Vt;) {
|
|
2193
2254
|
let e = this.pending.findIndex((e) => e.heartbeat || e.stateChange);
|
|
2194
2255
|
this.pending.splice(e >= 0 ? e : 0, 1);
|
|
2195
2256
|
}
|
|
@@ -2216,7 +2277,7 @@ var Qt = class {
|
|
|
2216
2277
|
};
|
|
2217
2278
|
//#endregion
|
|
2218
2279
|
//#region src/core/environment.ts
|
|
2219
|
-
function
|
|
2280
|
+
function rn() {
|
|
2220
2281
|
if (typeof window > "u") return { integration: "INTEGRATION_KIND_SDK" };
|
|
2221
2282
|
let e;
|
|
2222
2283
|
try {
|
|
@@ -2241,14 +2302,14 @@ function $t() {
|
|
|
2241
2302
|
integration: "INTEGRATION_KIND_IFRAME"
|
|
2242
2303
|
};
|
|
2243
2304
|
}
|
|
2244
|
-
function
|
|
2305
|
+
function an() {
|
|
2245
2306
|
if (typeof navigator > "u") return "DEVICE_CLASS_OTHER";
|
|
2246
2307
|
let e = navigator.userAgentData, t = e?.platform ?? "";
|
|
2247
2308
|
if (/tv|tizen|webos/i.test(t)) return "DEVICE_CLASS_TV";
|
|
2248
2309
|
let n = typeof screen < "u" ? Math.min(screen.width, screen.height) : 0;
|
|
2249
2310
|
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";
|
|
2250
2311
|
}
|
|
2251
|
-
function
|
|
2312
|
+
function on() {
|
|
2252
2313
|
let e = typeof navigator < "u" ? navigator.connection : void 0;
|
|
2253
2314
|
if (!e) return { connectionType: "CONNECTION_TYPE_UNKNOWN" };
|
|
2254
2315
|
let t = {
|
|
@@ -2267,17 +2328,17 @@ function tn() {
|
|
|
2267
2328
|
}
|
|
2268
2329
|
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;
|
|
2269
2330
|
}
|
|
2270
|
-
function
|
|
2271
|
-
let t =
|
|
2272
|
-
deviceClass:
|
|
2331
|
+
function sn(e) {
|
|
2332
|
+
let t = rn(), n = {
|
|
2333
|
+
deviceClass: an(),
|
|
2273
2334
|
integration: t.integration,
|
|
2274
|
-
...
|
|
2335
|
+
...on()
|
|
2275
2336
|
};
|
|
2276
2337
|
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);
|
|
2277
2338
|
let r = e.getBoundingClientRect?.();
|
|
2278
2339
|
return r && r.width > 0 && (n.viewportWidth = Math.round(r.width), n.viewportHeight = Math.round(r.height)), n;
|
|
2279
2340
|
}
|
|
2280
|
-
async function
|
|
2341
|
+
async function cn() {
|
|
2281
2342
|
let e = typeof navigator < "u" ? navigator.userAgentData : void 0;
|
|
2282
2343
|
if (!e?.getHighEntropyValues) return {};
|
|
2283
2344
|
try {
|
|
@@ -2289,7 +2350,7 @@ async function rn() {
|
|
|
2289
2350
|
}
|
|
2290
2351
|
//#endregion
|
|
2291
2352
|
//#region src/core/version.ts
|
|
2292
|
-
var
|
|
2353
|
+
var ln = "2.15.4", un = {
|
|
2293
2354
|
accent: "#E1B600",
|
|
2294
2355
|
accentText: "#1B1C23",
|
|
2295
2356
|
background: "#0e0f13",
|
|
@@ -2300,7 +2361,7 @@ var an = "2.15.2", on = {
|
|
|
2300
2361
|
border: "rgba(255, 255, 255, 0.1)",
|
|
2301
2362
|
radius: "8px",
|
|
2302
2363
|
fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
|
|
2303
|
-
},
|
|
2364
|
+
}, dn = {
|
|
2304
2365
|
accent: "--havik-accent",
|
|
2305
2366
|
accentText: "--havik-accent-text",
|
|
2306
2367
|
background: "--havik-bg",
|
|
@@ -2312,26 +2373,26 @@ var an = "2.15.2", on = {
|
|
|
2312
2373
|
radius: "--havik-radius",
|
|
2313
2374
|
fontFamily: "--havik-font"
|
|
2314
2375
|
};
|
|
2315
|
-
function
|
|
2376
|
+
function fn(e, t) {
|
|
2316
2377
|
let n = {
|
|
2317
|
-
...
|
|
2378
|
+
...un,
|
|
2318
2379
|
...t
|
|
2319
2380
|
};
|
|
2320
|
-
for (let t of Object.keys(
|
|
2381
|
+
for (let t of Object.keys(dn)) e.style.setProperty(dn[t], n[t]);
|
|
2321
2382
|
n.logoUrl && e.style.setProperty("--havik-logo", `url("${n.logoUrl}")`);
|
|
2322
2383
|
}
|
|
2323
2384
|
//#endregion
|
|
2324
2385
|
//#region src/ui/styles.ts
|
|
2325
|
-
var
|
|
2326
|
-
function
|
|
2327
|
-
let n = e.querySelector(`style#${
|
|
2328
|
-
if (n && (!t ||
|
|
2386
|
+
var pn = "havik-player-styles", mn = "\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", Z = /* @__PURE__ */ new WeakMap();
|
|
2387
|
+
function hn(e = document, t) {
|
|
2388
|
+
let n = e.querySelector(`style#${pn}`);
|
|
2389
|
+
if (n && (!t || Z.get(n) === t)) return;
|
|
2329
2390
|
let r = e.createElement("style");
|
|
2330
|
-
r.id =
|
|
2391
|
+
r.id = pn, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = mn, Z.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
|
|
2331
2392
|
}
|
|
2332
2393
|
//#endregion
|
|
2333
2394
|
//#region src/ui/icons.ts
|
|
2334
|
-
var
|
|
2395
|
+
var gn = {
|
|
2335
2396
|
play: "<path d=\"M8 5v14l11-7z\"/>",
|
|
2336
2397
|
pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
|
|
2337
2398
|
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\"/>",
|
|
@@ -2344,21 +2405,21 @@ var pn = {
|
|
|
2344
2405
|
liveDot: "<circle cx=\"12\" cy=\"12\" r=\"6\"/>",
|
|
2345
2406
|
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\"/>"
|
|
2346
2407
|
};
|
|
2347
|
-
function
|
|
2348
|
-
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${
|
|
2408
|
+
function Q(e) {
|
|
2409
|
+
return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${gn[e]}</svg>`;
|
|
2349
2410
|
}
|
|
2350
2411
|
//#endregion
|
|
2351
2412
|
//#region src/ui/controls.ts
|
|
2352
|
-
var
|
|
2413
|
+
var _n = 3e3, vn = 250, yn = /* @__PURE__ */ new Set([
|
|
2353
2414
|
"GONE",
|
|
2354
2415
|
"NOT_FOUND",
|
|
2355
2416
|
"INVALID_URN",
|
|
2356
2417
|
"UNAUTHORIZED"
|
|
2357
2418
|
]);
|
|
2358
|
-
function
|
|
2359
|
-
return new
|
|
2419
|
+
function bn(e) {
|
|
2420
|
+
return new xn(e);
|
|
2360
2421
|
}
|
|
2361
|
-
var
|
|
2422
|
+
var xn = class {
|
|
2362
2423
|
root;
|
|
2363
2424
|
video;
|
|
2364
2425
|
player;
|
|
@@ -2404,7 +2465,7 @@ var vn = class {
|
|
|
2404
2465
|
scrubbing = !1;
|
|
2405
2466
|
offFns = [];
|
|
2406
2467
|
constructor(e) {
|
|
2407
|
-
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ??
|
|
2468
|
+
this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? _n, 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, hn(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");
|
|
2408
2469
|
let t = (e, t, n) => {
|
|
2409
2470
|
let r = this.doc.createElement(e);
|
|
2410
2471
|
if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
|
|
@@ -2415,12 +2476,12 @@ var vn = class {
|
|
|
2415
2476
|
"aria-label": r,
|
|
2416
2477
|
title: r
|
|
2417
2478
|
});
|
|
2418
|
-
return i.innerHTML =
|
|
2479
|
+
return i.innerHTML = Q(n), i;
|
|
2419
2480
|
};
|
|
2420
2481
|
this.overlay = t("div", "havik-overlay"), this.center = t("button", "havik-center", {
|
|
2421
2482
|
type: "button",
|
|
2422
2483
|
"aria-label": "Play"
|
|
2423
|
-
}), this.center.innerHTML =
|
|
2484
|
+
}), this.center.innerHTML = Q("play"), this.spinner = t("div", "havik-spinner"), this.spinner.style.display = "none", this.overlay.append(this.center, this.spinner), this.gradient = t("div", "havik-gradient"), this.bar = t("div", "havik-bar"), this.seek = t("div", "havik-seek", {
|
|
2424
2485
|
role: "slider",
|
|
2425
2486
|
"aria-label": "Seek",
|
|
2426
2487
|
"aria-valuemin": "0",
|
|
@@ -2438,7 +2499,7 @@ var vn = class {
|
|
|
2438
2499
|
"aria-label": "Volume"
|
|
2439
2500
|
});
|
|
2440
2501
|
let a = t("div", "havik-vol");
|
|
2441
|
-
a.append(this.muteBtn, this.volSlider), this.live = t("div", "havik-live"), this.live.innerHTML =
|
|
2502
|
+
a.append(this.muteBtn, this.volSlider), this.live = t("div", "havik-live"), this.live.innerHTML = Q("liveDot");
|
|
2442
2503
|
let o = this.doc.createElement("span");
|
|
2443
2504
|
o.textContent = "LIVE", this.liveGo = t("button", "havik-live__go", { type: "button" }), this.liveGo.textContent = "Go live", this.live.append(o, this.liveGo), this.time = t("div", "havik-time"), this.time.textContent = "0:00", this.time.style.display = "none";
|
|
2444
2505
|
let s = t("div", "havik-spacer");
|
|
@@ -2490,7 +2551,7 @@ var vn = class {
|
|
|
2490
2551
|
let t = this.surfaceWasHidden;
|
|
2491
2552
|
this.tapTimer = setTimeout(() => {
|
|
2492
2553
|
this.tapTimer = void 0, this.toggleBar(t);
|
|
2493
|
-
},
|
|
2554
|
+
}, vn);
|
|
2494
2555
|
}
|
|
2495
2556
|
toggleBar(e) {
|
|
2496
2557
|
e ? this.poke() : this.hideIfIdle();
|
|
@@ -2522,12 +2583,12 @@ var vn = class {
|
|
|
2522
2583
|
}
|
|
2523
2584
|
if (e === "preplay") {
|
|
2524
2585
|
let e = this.doc.createElement("button");
|
|
2525
|
-
e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML =
|
|
2586
|
+
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);
|
|
2526
2587
|
} else {
|
|
2527
2588
|
let r = this.doc.createElement("div");
|
|
2528
|
-
if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !
|
|
2589
|
+
if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !yn.has(this.lastError.code)) {
|
|
2529
2590
|
let e = this.doc.createElement("button");
|
|
2530
|
-
e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${
|
|
2591
|
+
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);
|
|
2531
2592
|
}
|
|
2532
2593
|
}
|
|
2533
2594
|
this.root.append(n), this.statusBox = n, this.show();
|
|
@@ -2566,7 +2627,7 @@ var vn = class {
|
|
|
2566
2627
|
let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
|
|
2567
2628
|
this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
|
|
2568
2629
|
}
|
|
2569
|
-
!this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${
|
|
2630
|
+
!this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Sn(this.video.currentTime)} / ${Sn(this.video.duration)}`);
|
|
2570
2631
|
}
|
|
2571
2632
|
syncLive() {
|
|
2572
2633
|
let e = this.player.getStats(), t = e.isLive !== !1;
|
|
@@ -2616,17 +2677,17 @@ var vn = class {
|
|
|
2616
2677
|
renderQualityMenu() {
|
|
2617
2678
|
let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
|
|
2618
2679
|
n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
|
|
2619
|
-
for (let n of e) this.menu.append(this.menuItem(
|
|
2680
|
+
for (let n of e) this.menu.append(this.menuItem(Cn(n), t === n.index, () => this.player.setQuality(n.index)));
|
|
2620
2681
|
}
|
|
2621
2682
|
renderAudioMenu() {
|
|
2622
2683
|
let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
|
|
2623
2684
|
t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
|
|
2624
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
2685
|
+
for (let t of e) this.menu.append(this.menuItem(wn(t), t.default, () => this.player.setAudioTrack(t.id)));
|
|
2625
2686
|
}
|
|
2626
2687
|
renderCcMenu() {
|
|
2627
2688
|
let e = this.player.getTextTracks(), t = this.doc.createElement("div");
|
|
2628
2689
|
t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
|
|
2629
|
-
for (let t of e) this.menu.append(this.menuItem(
|
|
2690
|
+
for (let t of e) this.menu.append(this.menuItem(Tn(t), !1, () => this.player.setTextTrack(t.id)));
|
|
2630
2691
|
}
|
|
2631
2692
|
onTracksChanged() {
|
|
2632
2693
|
this.openMenu && this.renderMenu(this.openMenu);
|
|
@@ -2710,7 +2771,7 @@ var vn = class {
|
|
|
2710
2771
|
}
|
|
2711
2772
|
}
|
|
2712
2773
|
setIcon(e, t) {
|
|
2713
|
-
e.innerHTML =
|
|
2774
|
+
e.innerHTML = Q(t);
|
|
2714
2775
|
}
|
|
2715
2776
|
destroy() {
|
|
2716
2777
|
this.hideTimer && clearTimeout(this.hideTimer), this.tapTimer && clearTimeout(this.tapTimer);
|
|
@@ -2727,41 +2788,41 @@ var vn = class {
|
|
|
2727
2788
|
this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
|
|
2728
2789
|
}
|
|
2729
2790
|
};
|
|
2730
|
-
function
|
|
2791
|
+
function Sn(e) {
|
|
2731
2792
|
(!Number.isFinite(e) || e < 0) && (e = 0);
|
|
2732
2793
|
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");
|
|
2733
2794
|
return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
|
|
2734
2795
|
}
|
|
2735
|
-
function
|
|
2796
|
+
function Cn(e) {
|
|
2736
2797
|
return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
|
|
2737
2798
|
}
|
|
2738
|
-
function
|
|
2799
|
+
function wn(e) {
|
|
2739
2800
|
return e.name || e.lang || `Track ${e.id}`;
|
|
2740
2801
|
}
|
|
2741
|
-
function
|
|
2802
|
+
function Tn(e) {
|
|
2742
2803
|
return e.name || e.lang || `Track ${e.id}`;
|
|
2743
2804
|
}
|
|
2744
2805
|
//#endregion
|
|
2745
2806
|
//#region src/managed/index.ts
|
|
2746
|
-
var
|
|
2747
|
-
function
|
|
2807
|
+
var En = 6e3, Dn = 45e3, On = 2e3, kn = 3, An = 2e3, jn = 48e4;
|
|
2808
|
+
function Mn(e) {
|
|
2748
2809
|
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)})`);
|
|
2749
2810
|
if (e.env !== void 0) return B(e.env);
|
|
2750
2811
|
if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
|
|
2751
2812
|
return e.baseUrl;
|
|
2752
2813
|
}
|
|
2753
|
-
async function
|
|
2814
|
+
async function Nn(e) {
|
|
2754
2815
|
if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
|
|
2755
|
-
let t =
|
|
2816
|
+
let t = Mn(e);
|
|
2756
2817
|
if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
|
|
2757
2818
|
if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
|
|
2758
|
-
let r = new
|
|
2819
|
+
let r = new Pn({
|
|
2759
2820
|
...e,
|
|
2760
2821
|
baseUrl: t
|
|
2761
2822
|
});
|
|
2762
2823
|
return await r.start(), r;
|
|
2763
2824
|
}
|
|
2764
|
-
var
|
|
2825
|
+
var Pn = class {
|
|
2765
2826
|
opts;
|
|
2766
2827
|
abort = new AbortController();
|
|
2767
2828
|
listeners = {};
|
|
@@ -2849,7 +2910,7 @@ var An = class {
|
|
|
2849
2910
|
setTimeout(() => {
|
|
2850
2911
|
a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
|
|
2851
2912
|
phase: i ? "unavailable" : "tooEarly",
|
|
2852
|
-
retryInMs:
|
|
2913
|
+
retryInMs: Dn,
|
|
2853
2914
|
attempt: 1,
|
|
2854
2915
|
elapsedMs: 0,
|
|
2855
2916
|
...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
|
|
@@ -2888,13 +2949,13 @@ var An = class {
|
|
|
2888
2949
|
!a && !p() && h();
|
|
2889
2950
|
}, e);
|
|
2890
2951
|
};
|
|
2891
|
-
_(
|
|
2952
|
+
_(En), this.armingSub = T({
|
|
2892
2953
|
eventsBaseUrl: t,
|
|
2893
2954
|
matchUrn: this.opts.matchUrn,
|
|
2894
2955
|
credential: this.opts.credential,
|
|
2895
|
-
onAlive: () => _(
|
|
2956
|
+
onAlive: () => _(Dn),
|
|
2896
2957
|
onState: (e) => {
|
|
2897
|
-
a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() *
|
|
2958
|
+
a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * On);
|
|
2898
2959
|
},
|
|
2899
2960
|
onError: () => h()
|
|
2900
2961
|
});
|
|
@@ -2939,19 +3000,19 @@ var An = class {
|
|
|
2939
3000
|
if (this.analyticsSession?.noteDestroyed(), this.analyticsSession = null, this.detachMediaListeners?.(), this.opts.analytics === !1) return;
|
|
2940
3001
|
let i = e.analytics?.sid;
|
|
2941
3002
|
if (!i) return;
|
|
2942
|
-
let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } =
|
|
3003
|
+
let a = typeof this.opts.analytics == "object" ? this.opts.analytics.endpoint : void 0, { endpoint: o, disabledReason: s } = tn(this.opts.baseUrl, a, e.analytics?.endpoint);
|
|
2943
3004
|
if (!o) {
|
|
2944
3005
|
s && setTimeout(() => {
|
|
2945
3006
|
this.destroyed || r !== this.loadGen || this.emit("warning", new n("INTERNAL", 0, s));
|
|
2946
3007
|
}, 0);
|
|
2947
3008
|
return;
|
|
2948
3009
|
}
|
|
2949
|
-
let c = this.opts.video, l = new
|
|
3010
|
+
let c = this.opts.video, l = new nn({
|
|
2950
3011
|
endpoint: o,
|
|
2951
3012
|
sid: i,
|
|
2952
3013
|
player: t.name === "native" ? "PLAYER_SAFARI_NATIVE" : "PLAYER_HLSJS",
|
|
2953
|
-
sdkVersion:
|
|
2954
|
-
environment:
|
|
3014
|
+
sdkVersion: ln,
|
|
3015
|
+
environment: sn(c),
|
|
2955
3016
|
idleMs: typeof this.opts.analytics == "object" ? this.opts.analytics.idleMs : void 0,
|
|
2956
3017
|
onIdle: () => this.onAnalyticsIdle(t),
|
|
2957
3018
|
onBlocked: (e) => setTimeout(() => {
|
|
@@ -2972,7 +3033,7 @@ var An = class {
|
|
|
2972
3033
|
let u = () => l.noteSeek(), d = () => l.noteVolume(c.muted, c.volume);
|
|
2973
3034
|
c.addEventListener("seeking", u), c.addEventListener("volumechange", d), this.detachMediaListeners = () => {
|
|
2974
3035
|
c.removeEventListener("seeking", u), c.removeEventListener("volumechange", d);
|
|
2975
|
-
}, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()),
|
|
3036
|
+
}, l.start(), l.seedVolume(c.muted, c.volume), l.notePresentation(this.currentPresentation()), cn().then((e) => l.noteEnvironment(e)), this.opts.autoplay === !1 && l.noteAutoplay("AUTOPLAY_RESULT_NOT_ATTEMPTED");
|
|
2976
3037
|
}
|
|
2977
3038
|
currentPresentation() {
|
|
2978
3039
|
let e = this.opts.video;
|
|
@@ -2998,9 +3059,9 @@ var An = class {
|
|
|
2998
3059
|
emit: (e) => this.onEngineEvent(e)
|
|
2999
3060
|
}, o = this.opts.engine ?? "auto";
|
|
3000
3061
|
if (o === "native") return new K(a);
|
|
3001
|
-
if (o === "hls") return new
|
|
3062
|
+
if (o === "hls") return new Lt(a);
|
|
3002
3063
|
if (e.drmEnabled && e.drm?.fairplay?.licenseUrl && e.drm.fairplay.certificateUrl && W(this.opts.video) && G()) return new K(a);
|
|
3003
|
-
if (at()) return new
|
|
3064
|
+
if (at()) return new Lt(a);
|
|
3004
3065
|
if (W(this.opts.video)) return new K(a);
|
|
3005
3066
|
throw new n("INTERNAL", 0, "no supported HLS playback engine in this browser");
|
|
3006
3067
|
}
|
|
@@ -3053,7 +3114,7 @@ var An = class {
|
|
|
3053
3114
|
r = 0, i = !1;
|
|
3054
3115
|
},
|
|
3055
3116
|
onError: () => {
|
|
3056
|
-
this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >=
|
|
3117
|
+
this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= kn && (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.`))));
|
|
3057
3118
|
}
|
|
3058
3119
|
});
|
|
3059
3120
|
}
|
|
@@ -3075,7 +3136,7 @@ var An = class {
|
|
|
3075
3136
|
scheduleRejoin(e) {
|
|
3076
3137
|
this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
|
|
3077
3138
|
this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
|
|
3078
|
-
}, Math.random() *
|
|
3139
|
+
}, Math.random() * On));
|
|
3079
3140
|
}
|
|
3080
3141
|
cancelRejoin() {
|
|
3081
3142
|
this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
|
|
@@ -3099,7 +3160,7 @@ var An = class {
|
|
|
3099
3160
|
}
|
|
3100
3161
|
t.controls = !1;
|
|
3101
3162
|
let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
|
|
3102
|
-
r && r.insertBefore(n, t), n.appendChild(t),
|
|
3163
|
+
r && r.insertBefore(n, t), n.appendChild(t), fn(n, this.opts.theme), this.container = n, this.controlBar = bn({
|
|
3103
3164
|
root: n,
|
|
3104
3165
|
video: t,
|
|
3105
3166
|
player: this,
|
|
@@ -3130,7 +3191,7 @@ var An = class {
|
|
|
3130
3191
|
}
|
|
3131
3192
|
startStats() {
|
|
3132
3193
|
this.stopStats();
|
|
3133
|
-
let e = Math.max(500, this.opts.statsIntervalMs ??
|
|
3194
|
+
let e = Math.max(500, this.opts.statsIntervalMs ?? An);
|
|
3134
3195
|
this.statsTimer = setInterval(() => {
|
|
3135
3196
|
if (!this.engine) return;
|
|
3136
3197
|
let e = this.engine.getStats();
|
|
@@ -3142,7 +3203,7 @@ var An = class {
|
|
|
3142
3203
|
}
|
|
3143
3204
|
scheduleLicenseRefresh() {
|
|
3144
3205
|
this.stopRefresh();
|
|
3145
|
-
let e = this.opts.licenseRefreshMs ??
|
|
3206
|
+
let e = this.opts.licenseRefreshMs ?? jn;
|
|
3146
3207
|
e && this._descriptor?.drmEnabled && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
|
|
3147
3208
|
}
|
|
3148
3209
|
stopRefresh() {
|
|
@@ -3297,7 +3358,7 @@ function $(e) {
|
|
|
3297
3358
|
}
|
|
3298
3359
|
//#endregion
|
|
3299
3360
|
//#region src/embed/host.ts
|
|
3300
|
-
function
|
|
3361
|
+
function Fn(e) {
|
|
3301
3362
|
if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
|
|
3302
3363
|
if (!e.src) throw Error("mountEmbed: opts.src is required");
|
|
3303
3364
|
if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
|
|
@@ -3356,15 +3417,15 @@ function jn(e) {
|
|
|
3356
3417
|
}
|
|
3357
3418
|
//#endregion
|
|
3358
3419
|
//#region src/embed/page.ts
|
|
3359
|
-
var
|
|
3360
|
-
function
|
|
3420
|
+
var In = /^[A-Za-z0-9:_.-]{1,128}$/;
|
|
3421
|
+
function Ln(e) {
|
|
3361
3422
|
if (typeof e == "boolean") return e;
|
|
3362
3423
|
if (typeof e == "object" && e) {
|
|
3363
3424
|
let t = e.endpoint;
|
|
3364
3425
|
return typeof t == "string" ? { endpoint: t } : {};
|
|
3365
3426
|
}
|
|
3366
3427
|
}
|
|
3367
|
-
function
|
|
3428
|
+
function Rn() {
|
|
3368
3429
|
let e = window.HAVIK_EMBED_CONFIG;
|
|
3369
3430
|
if (e) return {
|
|
3370
3431
|
baseUrl: e.baseUrl ?? "",
|
|
@@ -3374,7 +3435,7 @@ function Pn() {
|
|
|
3374
3435
|
autoplay: e.autoplay ?? !0,
|
|
3375
3436
|
muted: e.muted ?? !0,
|
|
3376
3437
|
waitForLive: e.waitForLive ?? !0,
|
|
3377
|
-
analytics:
|
|
3438
|
+
analytics: Ln(e.analytics)
|
|
3378
3439
|
};
|
|
3379
3440
|
let t = new URLSearchParams(location.search);
|
|
3380
3441
|
return {
|
|
@@ -3387,9 +3448,9 @@ function Pn() {
|
|
|
3387
3448
|
waitForLive: !0
|
|
3388
3449
|
};
|
|
3389
3450
|
}
|
|
3390
|
-
async function
|
|
3391
|
-
let e =
|
|
3392
|
-
|
|
3451
|
+
async function zn() {
|
|
3452
|
+
let e = Rn();
|
|
3453
|
+
Vn();
|
|
3393
3454
|
let t = document.createElement("video");
|
|
3394
3455
|
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);
|
|
3395
3456
|
let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
|
|
@@ -3444,12 +3505,12 @@ async function Fn() {
|
|
|
3444
3505
|
code: e.code,
|
|
3445
3506
|
message: e.message
|
|
3446
3507
|
})), e.on("autoplayblocked", () => {
|
|
3447
|
-
i({ type: "oddin:autoplayblocked" }),
|
|
3508
|
+
i({ type: "oddin:autoplayblocked" }), Bn(t, e);
|
|
3448
3509
|
});
|
|
3449
3510
|
}, s = async (r) => {
|
|
3450
3511
|
a?.destroy(), a = null;
|
|
3451
3512
|
try {
|
|
3452
|
-
let n = await
|
|
3513
|
+
let n = await Nn({
|
|
3453
3514
|
video: t,
|
|
3454
3515
|
baseUrl: e.baseUrl,
|
|
3455
3516
|
matchUrn: r,
|
|
@@ -3511,7 +3572,7 @@ async function Fn() {
|
|
|
3511
3572
|
a?.retry();
|
|
3512
3573
|
break;
|
|
3513
3574
|
case "oddin:load":
|
|
3514
|
-
typeof e.matchUrn == "string" &&
|
|
3575
|
+
typeof e.matchUrn == "string" && In.test(e.matchUrn) && s(e.matchUrn);
|
|
3515
3576
|
break;
|
|
3516
3577
|
case "oddin:destroy": c();
|
|
3517
3578
|
}
|
|
@@ -3523,18 +3584,18 @@ async function Fn() {
|
|
|
3523
3584
|
protocol: 1
|
|
3524
3585
|
});
|
|
3525
3586
|
}
|
|
3526
|
-
function
|
|
3587
|
+
function Bn(e, t) {
|
|
3527
3588
|
if (document.getElementById("havik-tap")) return;
|
|
3528
3589
|
let n = document.createElement("button");
|
|
3529
3590
|
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", () => {
|
|
3530
3591
|
n.remove(), t.play();
|
|
3531
3592
|
}), document.body.appendChild(n);
|
|
3532
3593
|
}
|
|
3533
|
-
function
|
|
3594
|
+
function Vn() {
|
|
3534
3595
|
let e = document.createElement("style");
|
|
3535
3596
|
e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
|
|
3536
3597
|
}
|
|
3537
3598
|
//#endregion
|
|
3538
|
-
export {
|
|
3599
|
+
export { un as ODDIN_THEME, n as PlaybackError, fn as applyTheme, B as baseUrlForEnv, zn as bootEmbed, Te as clearDeviceId, Nn 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, Fn as mountEmbed, de as pollToLive, y as resolveStream, T as subscribeLiveState, S as videoFullscreenActive, Ce as watchStatus };
|
|
3539
3600
|
|
|
3540
3601
|
//# sourceMappingURL=index.js.map
|