@oddin-gg/havik-player 1.0.5 → 1.0.6

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/index.js CHANGED
@@ -510,21 +510,21 @@ function I(e, t) {
510
510
  function L() {
511
511
  return e.isSupported();
512
512
  }
513
- var R = 3, z = 5, B = 3, V = 3e4, H = 12e3, U = 3e3, de = 6e3, fe = 3, pe = 9;
514
- function me(e) {
513
+ var R = 3, z = 5, B = 3, V = 3e4, H = 12e3, U = 3e3, de = 6e3, fe = 3, pe = 9, me = .5;
514
+ function he(e) {
515
515
  if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
516
516
  delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
517
517
  return;
518
518
  }
519
519
  e.liveSyncDuration ??= fe;
520
520
  let t = Math.max(12, e.liveSyncDuration + pe);
521
- (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), he(e, e.liveSyncDuration);
521
+ (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t), ge(e, e.liveSyncDuration);
522
522
  }
523
- function he(e, t) {
523
+ function ge(e, t) {
524
524
  let n = (e, t, n) => Math.min(n, Math.max(t, e));
525
525
  e.maxStarvationDelay ??= n(t / 2, .5, 4), e.maxLoadingDelay ??= n(t, 1, 4), e.abrEwmaFastLive ??= n(t / 2, 1, 3);
526
526
  }
527
- var ge = class {
527
+ var _e = class {
528
528
  name = "hls";
529
529
  hls = null;
530
530
  deps;
@@ -537,6 +537,7 @@ var ge = class {
537
537
  networkRetryTimer = null;
538
538
  licenseRetryTimer = null;
539
539
  onVisibility = null;
540
+ onSeeking = null;
540
541
  sawLive = !1;
541
542
  lastEndSN = -1;
542
543
  lastAdvanceMs = 0;
@@ -565,7 +566,15 @@ var ge = class {
565
566
  if (typeof t != "number") return;
566
567
  let n = typeof e.targetLatency == "number" && e.targetLatency > 0 ? e.targetLatency : 3;
567
568
  t - r.currentTime > Math.max(n * 3, 8) && (r.currentTime = t);
568
- }, document.addEventListener("visibilitychange", this.onVisibility));
569
+ }, document.addEventListener("visibilitychange", this.onVisibility)), this.onSeeking = () => this.clampOverSeek(), r.addEventListener("seeking", this.onSeeking);
570
+ }
571
+ clampOverSeek() {
572
+ let e = this.hls;
573
+ if (!e || this.chaseSuspended || (e.levels[e.currentLevel] ?? e.levels[e.loadLevel])?.details?.live !== !0) return;
574
+ let t = e.liveSyncPosition;
575
+ if (typeof t != "number" || !Number.isFinite(t)) return;
576
+ let { video: n } = this.deps;
577
+ n.currentTime <= t + me || (n.currentTime = t);
569
578
  }
570
579
  startHls() {
571
580
  let { video: t, descriptor: n, credential: r, deviceId: i, userId: a, lowLatency: o, hlsConfig: s, emit: c } = this.deps, l = n.drmEnabled && !!n.drm?.widevine?.licenseUrl, u = {
@@ -591,7 +600,7 @@ var ge = class {
591
600
  } : {},
592
601
  ...s
593
602
  };
594
- me(u);
603
+ he(u);
595
604
  let d = new e(u);
596
605
  this.hls = d, d.on(e.Events.MANIFEST_PARSED, () => {
597
606
  this.mediaRecoveries = 0, this.networkRecoveries = 0, c({ type: "ready" });
@@ -731,7 +740,7 @@ var ge = class {
731
740
  } catch {}
732
741
  }
733
742
  destroy() {
734
- if (this.clearRecoveryReset(), this.endWatchdog &&= (clearInterval(this.endWatchdog), null), this.onVisibility && typeof document < "u" && (document.removeEventListener("visibilitychange", this.onVisibility), this.onVisibility = null), this.networkRetryTimer &&= (clearTimeout(this.networkRetryTimer), null), this.licenseRetryTimer &&= (clearTimeout(this.licenseRetryTimer), null), this.hls) {
743
+ if (this.clearRecoveryReset(), 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.networkRetryTimer &&= (clearTimeout(this.networkRetryTimer), null), this.licenseRetryTimer &&= (clearTimeout(this.licenseRetryTimer), null), this.hls) {
735
744
  try {
736
745
  this.hls.destroy();
737
746
  } catch (e) {
@@ -815,7 +824,7 @@ var ge = class {
815
824
  };
816
825
  //#endregion
817
826
  //#region src/core/engine/native.ts
818
- function _e(e) {
827
+ function ve(e) {
819
828
  return !!(e ?? (typeof document < "u" ? document.createElement("video") : void 0))?.canPlayType("application/vnd.apple.mpegurl");
820
829
  }
821
830
  var W = class {
@@ -946,15 +955,15 @@ function q(e, t) {
946
955
  }
947
956
  //#endregion
948
957
  //#region src/ui/styles.ts
949
- var J = "havik-player-styles", ve = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n.havik-spinner{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\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.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:rgba(255,255,255,.3);border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:70px;opacity:1}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:12px;height:12px;border-radius:50%;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-moz-range-thumb{width:12px;height:12px;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";
950
- function ye(e = document) {
958
+ var J = "havik-player-styles", ye = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n.havik-spinner{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\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.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:rgba(255,255,255,.3);border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:70px;opacity:1}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:12px;height:12px;border-radius:50%;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-moz-range-thumb{width:12px;height:12px;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";
959
+ function be(e = document) {
951
960
  if (e.getElementById(J)) return;
952
961
  let t = e.createElement("style");
953
- t.id = J, t.textContent = ve, e.head.appendChild(t);
962
+ t.id = J, t.textContent = ye, e.head.appendChild(t);
954
963
  }
955
964
  //#endregion
956
965
  //#region src/ui/icons.ts
957
- var be = {
966
+ var xe = {
958
967
  play: "<path d=\"M8 5v14l11-7z\"/>",
959
968
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
960
969
  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\"/>",
@@ -968,20 +977,20 @@ var be = {
968
977
  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\"/>"
969
978
  };
970
979
  function Y(e) {
971
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${be[e]}</svg>`;
980
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${xe[e]}</svg>`;
972
981
  }
973
982
  //#endregion
974
983
  //#region src/ui/controls.ts
975
- var xe = 3e3, Se = /* @__PURE__ */ new Set([
984
+ var Se = 3e3, Ce = /* @__PURE__ */ new Set([
976
985
  "GONE",
977
986
  "NOT_FOUND",
978
987
  "INVALID_URN",
979
988
  "UNAUTHORIZED"
980
989
  ]);
981
- function Ce(e) {
982
- return new we(e);
990
+ function we(e) {
991
+ return new Te(e);
983
992
  }
984
- var we = class {
993
+ var Te = class {
985
994
  root;
986
995
  video;
987
996
  player;
@@ -1021,7 +1030,7 @@ var we = class {
1021
1030
  scrubbing = !1;
1022
1031
  offFns = [];
1023
1032
  constructor(e) {
1024
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? xe, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.errorMessage = e.errorMessage, ye(this.doc), 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");
1033
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? Se, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.errorMessage = e.errorMessage, be(this.doc), 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");
1025
1034
  let t = (e, t, n) => {
1026
1035
  let r = this.doc.createElement(e);
1027
1036
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -1108,7 +1117,7 @@ var we = class {
1108
1117
  e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = Y("play"), e.addEventListener("click", () => void this.player.play()), t.append(e);
1109
1118
  } else {
1110
1119
  let n = this.doc.createElement("div");
1111
- if (n.className = "havik-status__msg", n.textContent = e === "ended" ? this.endedMessage : e === "waiting" ? "Starting soon…" : this.errorMessage ?? this.lastError?.message ?? "Playback unavailable", t.append(n), e === "error" && this.lastError && !Se.has(this.lastError.code)) {
1120
+ if (n.className = "havik-status__msg", n.textContent = e === "ended" ? this.endedMessage : e === "waiting" ? "Starting soon…" : this.errorMessage ?? this.lastError?.message ?? "Playback unavailable", t.append(n), e === "error" && this.lastError && !Ce.has(this.lastError.code)) {
1112
1121
  let e = this.doc.createElement("button");
1113
1122
  e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${Y("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), t.append(e);
1114
1123
  }
@@ -1120,17 +1129,14 @@ var we = class {
1120
1129
  this.setIcon(this.muteBtn, t ? "volumeMute" : "volumeHigh"), this.muteBtn.setAttribute("aria-label", t ? "Unmute" : "Mute"), this.volSlider.value = String(t ? 0 : e.volume);
1121
1130
  }
1122
1131
  seekableRange() {
1123
- let e = this.video;
1124
- if (e.seekable && e.seekable.length > 0) {
1125
- let t = e.seekable.start(0), n = e.seekable.end(e.seekable.length - 1);
1126
- if (n > t) return {
1127
- start: t,
1128
- end: n
1129
- };
1130
- }
1131
- return Number.isFinite(e.duration) && e.duration > 0 ? {
1132
- start: 0,
1133
- end: e.duration
1132
+ let e = this.video, t, n;
1133
+ if (e.seekable && e.seekable.length > 0) t = e.seekable.start(0), n = e.seekable.end(e.seekable.length - 1);
1134
+ else if (Number.isFinite(e.duration) && e.duration > 0) t = 0, n = e.duration;
1135
+ else return null;
1136
+ let r = this.player.getStats();
1137
+ return r.isLive !== !1 && r.targetLatencySeconds && r.targetLatencySeconds > 0 && (n = Math.max(t, n - r.targetLatencySeconds)), n > t ? {
1138
+ start: t,
1139
+ end: n
1134
1140
  } : null;
1135
1141
  }
1136
1142
  syncProgress() {
@@ -1197,17 +1203,17 @@ var we = class {
1197
1203
  renderQualityMenu() {
1198
1204
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
1199
1205
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
1200
- for (let n of e) this.menu.append(this.menuItem(Te(n), t === n.index, () => this.player.setQuality(n.index)));
1206
+ for (let n of e) this.menu.append(this.menuItem(Ee(n), t === n.index, () => this.player.setQuality(n.index)));
1201
1207
  }
1202
1208
  renderAudioMenu() {
1203
1209
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
1204
1210
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
1205
- for (let t of e) this.menu.append(this.menuItem(Ee(t), t.default, () => this.player.setAudioTrack(t.id)));
1211
+ for (let t of e) this.menu.append(this.menuItem(De(t), t.default, () => this.player.setAudioTrack(t.id)));
1206
1212
  }
1207
1213
  renderCcMenu() {
1208
1214
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
1209
1215
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
1210
- for (let t of e) this.menu.append(this.menuItem(De(t), !1, () => this.player.setTextTrack(t.id)));
1216
+ for (let t of e) this.menu.append(this.menuItem(Oe(t), !1, () => this.player.setTextTrack(t.id)));
1211
1217
  }
1212
1218
  onTracksChanged() {
1213
1219
  this.openMenu && this.renderMenu(this.openMenu);
@@ -1313,27 +1319,27 @@ function X(e) {
1313
1319
  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");
1314
1320
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
1315
1321
  }
1316
- function Te(e) {
1322
+ function Ee(e) {
1317
1323
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
1318
1324
  }
1319
- function Ee(e) {
1325
+ function De(e) {
1320
1326
  return e.name || e.lang || `Track ${e.id}`;
1321
1327
  }
1322
- function De(e) {
1328
+ function Oe(e) {
1323
1329
  return e.name || e.lang || `Track ${e.id}`;
1324
1330
  }
1325
1331
  //#endregion
1326
1332
  //#region src/managed/index.ts
1327
- var Oe = 6e3, ke = 45e3, Z = 2e3, Ae = 3, je = 2e3, Me = 480 * 1e3;
1333
+ var ke = 6e3, Z = 45e3, Ae = 2e3, je = 3, Me = 2e3, Ne = 480 * 1e3;
1328
1334
  async function Q(e) {
1329
1335
  if (!e.video || typeof e.video.play != "function") throw new t("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
1330
1336
  if (!e.baseUrl) throw new t("INTERNAL", 0, "createPlayer: baseUrl is required");
1331
1337
  if (!e.matchUrn) throw new t("INVALID_URN", 0, "createPlayer: matchUrn is required");
1332
1338
  if (!e.credential) throw new t("INTERNAL", 0, "createPlayer: credential is required");
1333
- let n = new Ne(e);
1339
+ let n = new Pe(e);
1334
1340
  return await n.start(), n;
1335
1341
  }
1336
- var Ne = class {
1342
+ var Pe = class {
1337
1343
  opts;
1338
1344
  abort = new AbortController();
1339
1345
  listeners = {};
@@ -1412,13 +1418,13 @@ var Ne = class {
1412
1418
  !r && !s() && l();
1413
1419
  }, e);
1414
1420
  };
1415
- d(Oe), this.armingSub = w({
1421
+ d(ke), this.armingSub = w({
1416
1422
  eventsBaseUrl: n,
1417
1423
  matchUrn: this.opts.matchUrn,
1418
1424
  credential: this.opts.credential,
1419
- onAlive: () => d(ke),
1425
+ onAlive: () => d(Z),
1420
1426
  onState: (e) => {
1421
- r || s() || (e.state === "live" || e.state === "gone") && setTimeout(u, Math.random() * Z);
1427
+ r || s() || (e.state === "live" || e.state === "gone") && setTimeout(u, Math.random() * Ae);
1422
1428
  },
1423
1429
  onError: () => l()
1424
1430
  });
@@ -1463,8 +1469,8 @@ var Ne = class {
1463
1469
  emit: (e) => this.onEngineEvent(e)
1464
1470
  }, a = this.opts.engine ?? "auto";
1465
1471
  if (a === "native") return new W(i);
1466
- if (a === "hls" || L()) return new ge(i);
1467
- if (_e(this.opts.video)) return new W(i);
1472
+ if (a === "hls" || L()) return new _e(i);
1473
+ if (ve(this.opts.video)) return new W(i);
1468
1474
  throw new t("INTERNAL", 0, "no supported HLS playback engine in this browser");
1469
1475
  }
1470
1476
  onEngineEvent(e) {
@@ -1509,7 +1515,7 @@ var Ne = class {
1509
1515
  r = 0, i = !1;
1510
1516
  },
1511
1517
  onError: () => {
1512
- this.destroyed || e !== this.loadGen || i || (r += 1, r >= Ae && (i = !0, this.emit("warning", new t("NETWORK", 0, `live-state events unreachable at ${n} 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.`))));
1518
+ this.destroyed || e !== this.loadGen || i || (r += 1, r >= je && (i = !0, this.emit("warning", new t("NETWORK", 0, `live-state events unreachable at ${n} 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.`))));
1513
1519
  }
1514
1520
  });
1515
1521
  }
@@ -1531,7 +1537,7 @@ var Ne = class {
1531
1537
  }
1532
1538
  t.controls = !1;
1533
1539
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
1534
- r && r.insertBefore(n, t), n.appendChild(t), q(n, this.opts.theme), this.container = n, this.controlBar = Ce({
1540
+ r && r.insertBefore(n, t), n.appendChild(t), q(n, this.opts.theme), this.container = n, this.controlBar = we({
1535
1541
  root: n,
1536
1542
  video: t,
1537
1543
  player: this,
@@ -1554,7 +1560,7 @@ var Ne = class {
1554
1560
  }
1555
1561
  startStats() {
1556
1562
  this.stopStats();
1557
- let e = Math.max(500, this.opts.statsIntervalMs ?? je);
1563
+ let e = Math.max(500, this.opts.statsIntervalMs ?? Me);
1558
1564
  this.statsTimer = setInterval(() => {
1559
1565
  this.engine && this.emit("stats", this.engine.getStats());
1560
1566
  }, e);
@@ -1564,7 +1570,7 @@ var Ne = class {
1564
1570
  }
1565
1571
  scheduleLicenseRefresh() {
1566
1572
  this.stopRefresh();
1567
- let e = this.opts.licenseRefreshMs ?? Me;
1573
+ let e = this.opts.licenseRefreshMs ?? Ne;
1568
1574
  !e || !this._descriptor?.drmEnabled || (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
1569
1575
  }
1570
1576
  stopRefresh() {
@@ -1699,7 +1705,7 @@ function $(e) {
1699
1705
  }
1700
1706
  //#endregion
1701
1707
  //#region src/embed/host.ts
1702
- function Pe(e) {
1708
+ function Fe(e) {
1703
1709
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
1704
1710
  if (!e.src) throw Error("mountEmbed: opts.src is required");
1705
1711
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -1758,8 +1764,8 @@ function Pe(e) {
1758
1764
  }
1759
1765
  //#endregion
1760
1766
  //#region src/embed/page.ts
1761
- var Fe = /^[A-Za-z0-9:_.-]{1,128}$/;
1762
- function Ie() {
1767
+ var Ie = /^[A-Za-z0-9:_.-]{1,128}$/;
1768
+ function Le() {
1763
1769
  let e = window.HAVIK_EMBED_CONFIG;
1764
1770
  if (e) return {
1765
1771
  baseUrl: e.baseUrl ?? "",
@@ -1781,9 +1787,9 @@ function Ie() {
1781
1787
  waitForLive: !0
1782
1788
  };
1783
1789
  }
1784
- async function Le() {
1785
- let e = Ie();
1786
- ze();
1790
+ async function Re() {
1791
+ let e = Le();
1792
+ Be();
1787
1793
  let n = document.createElement("video");
1788
1794
  n.setAttribute("playsinline", ""), n.muted = e.muted ?? !0, n.style.cssText = "width:100%;height:100%;object-fit:contain;background:#000;aspect-ratio:auto", document.body.appendChild(n);
1789
1795
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -1838,7 +1844,7 @@ async function Le() {
1838
1844
  code: e.code,
1839
1845
  message: e.message
1840
1846
  })), e.on("autoplayblocked", () => {
1841
- i({ type: "oddin:autoplayblocked" }), Re(n, e);
1847
+ i({ type: "oddin:autoplayblocked" }), ze(n, e);
1842
1848
  });
1843
1849
  }, s = async (r) => {
1844
1850
  a?.destroy(), a = null;
@@ -1904,7 +1910,7 @@ async function Le() {
1904
1910
  a?.retry();
1905
1911
  break;
1906
1912
  case "oddin:load":
1907
- typeof e.matchUrn == "string" && Fe.test(e.matchUrn) && s(e.matchUrn);
1913
+ typeof e.matchUrn == "string" && Ie.test(e.matchUrn) && s(e.matchUrn);
1908
1914
  break;
1909
1915
  case "oddin:destroy":
1910
1916
  c();
@@ -1918,18 +1924,18 @@ async function Le() {
1918
1924
  protocol: 1
1919
1925
  });
1920
1926
  }
1921
- function Re(e, t) {
1927
+ function ze(e, t) {
1922
1928
  if (document.getElementById("havik-tap")) return;
1923
1929
  let n = document.createElement("button");
1924
1930
  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", () => {
1925
1931
  n.remove(), t.play();
1926
1932
  }), document.body.appendChild(n);
1927
1933
  }
1928
- function ze() {
1934
+ function Be() {
1929
1935
  let e = document.createElement("style");
1930
1936
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
1931
1937
  }
1932
1938
  //#endregion
1933
- export { G as ODDIN_THEME, t as PlaybackError, q as applyTheme, Le as bootEmbed, k as clearDeviceId, Q as createPlayer, T as deriveEventsBaseUrl, oe as fetchCatalog, ce as fetchTournament, ue as flattenMatches, O as getDeviceId, N as isLowLatencyManifest, $ as isOddinMessage, j as licenseRequestHeaders, Pe as mountEmbed, y as pollToLive, x as resolveStream, w as subscribeLiveState, C as watchStatus };
1939
+ export { G as ODDIN_THEME, t as PlaybackError, q as applyTheme, Re as bootEmbed, k as clearDeviceId, Q as createPlayer, T as deriveEventsBaseUrl, oe as fetchCatalog, ce as fetchTournament, ue as flattenMatches, O as getDeviceId, N as isLowLatencyManifest, $ as isOddinMessage, j as licenseRequestHeaders, Fe as mountEmbed, y as pollToLive, x as resolveStream, w as subscribeLiveState, C as watchStatus };
1934
1940
 
1935
1941
  //# sourceMappingURL=index.js.map