@oddin-gg/havik-player 1.0.2 → 1.0.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/index.js CHANGED
@@ -510,17 +510,17 @@ 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 = 3, fe = 9;
514
- function pe(e) {
513
+ var R = 3, z = 5, B = 3, V = 3e4, H = 12e3, U = 3e3, de = 6e3, fe = 3, pe = 9;
514
+ function me(e) {
515
515
  if (e.liveSyncDurationCount != null || e.liveMaxLatencyDurationCount != null) {
516
516
  delete e.liveSyncDuration, delete e.liveMaxLatencyDuration;
517
517
  return;
518
518
  }
519
- e.liveSyncDuration ??= de;
520
- let t = Math.max(12, e.liveSyncDuration + fe);
519
+ e.liveSyncDuration ??= fe;
520
+ let t = Math.max(12, e.liveSyncDuration + pe);
521
521
  (e.liveMaxLatencyDuration == null || e.liveMaxLatencyDuration <= e.liveSyncDuration) && (e.liveMaxLatencyDuration = t);
522
522
  }
523
- var me = class {
523
+ var he = class {
524
524
  name = "hls";
525
525
  hls = null;
526
526
  deps;
@@ -538,6 +538,9 @@ var me = class {
538
538
  lastAdvanceMs = 0;
539
539
  endWatchdog = null;
540
540
  endedEmitted = !1;
541
+ chaseSuspended = !1;
542
+ savedLiveMaxLatency;
543
+ savedMaxSyncRate;
541
544
  constructor(e) {
542
545
  this.deps = e, this.licenseUrl = e.descriptor.drm?.widevine?.licenseUrl;
543
546
  }
@@ -584,7 +587,7 @@ var me = class {
584
587
  } : {},
585
588
  ...s
586
589
  };
587
- pe(u);
590
+ me(u);
588
591
  let d = new e(u);
589
592
  this.hls = d, d.on(e.Events.MANIFEST_PARSED, () => {
590
593
  this.mediaRecoveries = 0, this.networkRecoveries = 0, c({ type: "ready" });
@@ -684,20 +687,29 @@ var me = class {
684
687
  if (t.live === !0) {
685
688
  this.sawLive = !0;
686
689
  let e = t.endSN ?? -1;
687
- e !== this.lastEndSN && (this.lastEndSN = e, this.lastAdvanceMs = Date.now());
690
+ e !== this.lastEndSN && (this.lastEndSN = e, this.lastAdvanceMs = Date.now(), this.chaseSuspended && this.resumeChasing());
688
691
  return;
689
692
  }
690
693
  this.sawLive && this.emitEnded("endlist");
691
694
  }
692
695
  }
693
696
  checkEndStale() {
694
- if (!(this.endedEmitted || !this.sawLive || this.lastAdvanceMs === 0)) {
695
- if (typeof document < "u" && document.hidden) {
696
- this.lastAdvanceMs = Date.now();
697
- return;
698
- }
699
- Date.now() - this.lastAdvanceMs > H && this.emitEnded("stalled");
697
+ if (this.endedEmitted || !this.sawLive || this.lastAdvanceMs === 0) return;
698
+ if (typeof document < "u" && document.hidden) {
699
+ this.lastAdvanceMs = Date.now();
700
+ return;
700
701
  }
702
+ let e = Date.now() - this.lastAdvanceMs;
703
+ !this.chaseSuspended && e > de && this.suspendChasing(), e > H && this.emitEnded("stalled");
704
+ }
705
+ suspendChasing() {
706
+ let e = this.hls;
707
+ e && (this.chaseSuspended = !0, this.savedLiveMaxLatency = e.config.liveMaxLatencyDuration, this.savedMaxSyncRate = e.config.maxLiveSyncPlaybackRate, e.config.liveMaxLatencyDuration = 2 ** 53 - 1, e.config.maxLiveSyncPlaybackRate = 1);
708
+ }
709
+ resumeChasing() {
710
+ this.chaseSuspended = !1;
711
+ let e = this.hls;
712
+ e && (e.config.liveMaxLatencyDuration = this.savedLiveMaxLatency, e.config.maxLiveSyncPlaybackRate = this.savedMaxSyncRate);
701
713
  }
702
714
  emitEnded(e) {
703
715
  this.endedEmitted || (this.endedEmitted = !0, this.endWatchdog &&= (clearInterval(this.endWatchdog), null), this.deps.emit({
@@ -799,7 +811,7 @@ var me = class {
799
811
  };
800
812
  //#endregion
801
813
  //#region src/core/engine/native.ts
802
- function he(e) {
814
+ function ge(e) {
803
815
  return !!(e ?? (typeof document < "u" ? document.createElement("video") : void 0))?.canPlayType("application/vnd.apple.mpegurl");
804
816
  }
805
817
  var W = class {
@@ -930,15 +942,15 @@ function q(e, t) {
930
942
  }
931
943
  //#endregion
932
944
  //#region src/ui/styles.ts
933
- var J = "havik-player-styles", ge = "\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";
934
- function _e(e = document) {
945
+ var J = "havik-player-styles", _e = "\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";
946
+ function ve(e = document) {
935
947
  if (e.getElementById(J)) return;
936
948
  let t = e.createElement("style");
937
- t.id = J, t.textContent = ge, e.head.appendChild(t);
949
+ t.id = J, t.textContent = _e, e.head.appendChild(t);
938
950
  }
939
951
  //#endregion
940
952
  //#region src/ui/icons.ts
941
- var ve = {
953
+ var ye = {
942
954
  play: "<path d=\"M8 5v14l11-7z\"/>",
943
955
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
944
956
  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\"/>",
@@ -952,20 +964,20 @@ var ve = {
952
964
  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\"/>"
953
965
  };
954
966
  function Y(e) {
955
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${ve[e]}</svg>`;
967
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${ye[e]}</svg>`;
956
968
  }
957
969
  //#endregion
958
970
  //#region src/ui/controls.ts
959
- var ye = 3e3, be = /* @__PURE__ */ new Set([
971
+ var be = 3e3, xe = /* @__PURE__ */ new Set([
960
972
  "GONE",
961
973
  "NOT_FOUND",
962
974
  "INVALID_URN",
963
975
  "UNAUTHORIZED"
964
976
  ]);
965
- function xe(e) {
966
- return new Se(e);
977
+ function Se(e) {
978
+ return new Ce(e);
967
979
  }
968
- var Se = class {
980
+ var Ce = class {
969
981
  root;
970
982
  video;
971
983
  player;
@@ -1005,7 +1017,7 @@ var Se = class {
1005
1017
  scrubbing = !1;
1006
1018
  offFns = [];
1007
1019
  constructor(e) {
1008
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? ye, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.errorMessage = e.errorMessage, _e(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");
1020
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? be, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.errorMessage = e.errorMessage, ve(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");
1009
1021
  let t = (e, t, n) => {
1010
1022
  let r = this.doc.createElement(e);
1011
1023
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -1092,7 +1104,7 @@ var Se = class {
1092
1104
  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);
1093
1105
  } else {
1094
1106
  let n = this.doc.createElement("div");
1095
- 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 && !be.has(this.lastError.code)) {
1107
+ 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 && !xe.has(this.lastError.code)) {
1096
1108
  let e = this.doc.createElement("button");
1097
1109
  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);
1098
1110
  }
@@ -1181,17 +1193,17 @@ var Se = class {
1181
1193
  renderQualityMenu() {
1182
1194
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
1183
1195
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
1184
- for (let n of e) this.menu.append(this.menuItem(Ce(n), t === n.index, () => this.player.setQuality(n.index)));
1196
+ for (let n of e) this.menu.append(this.menuItem(we(n), t === n.index, () => this.player.setQuality(n.index)));
1185
1197
  }
1186
1198
  renderAudioMenu() {
1187
1199
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
1188
1200
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
1189
- for (let t of e) this.menu.append(this.menuItem(we(t), t.default, () => this.player.setAudioTrack(t.id)));
1201
+ for (let t of e) this.menu.append(this.menuItem(Te(t), t.default, () => this.player.setAudioTrack(t.id)));
1190
1202
  }
1191
1203
  renderCcMenu() {
1192
1204
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
1193
1205
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
1194
- for (let t of e) this.menu.append(this.menuItem(Te(t), !1, () => this.player.setTextTrack(t.id)));
1206
+ for (let t of e) this.menu.append(this.menuItem(Ee(t), !1, () => this.player.setTextTrack(t.id)));
1195
1207
  }
1196
1208
  onTracksChanged() {
1197
1209
  this.openMenu && this.renderMenu(this.openMenu);
@@ -1297,27 +1309,27 @@ function X(e) {
1297
1309
  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");
1298
1310
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
1299
1311
  }
1300
- function Ce(e) {
1312
+ function we(e) {
1301
1313
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
1302
1314
  }
1303
- function we(e) {
1315
+ function Te(e) {
1304
1316
  return e.name || e.lang || `Track ${e.id}`;
1305
1317
  }
1306
- function Te(e) {
1318
+ function Ee(e) {
1307
1319
  return e.name || e.lang || `Track ${e.id}`;
1308
1320
  }
1309
1321
  //#endregion
1310
1322
  //#region src/managed/index.ts
1311
- var Ee = 6e3, De = 45e3, Oe = 2e3, ke = 3, Z = 2e3, Ae = 480 * 1e3;
1323
+ var De = 6e3, Oe = 45e3, ke = 2e3, Z = 3, Ae = 2e3, je = 480 * 1e3;
1312
1324
  async function Q(e) {
1313
1325
  if (!e.video || typeof e.video.play != "function") throw new t("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
1314
1326
  if (!e.baseUrl) throw new t("INTERNAL", 0, "createPlayer: baseUrl is required");
1315
1327
  if (!e.matchUrn) throw new t("INVALID_URN", 0, "createPlayer: matchUrn is required");
1316
1328
  if (!e.credential) throw new t("INTERNAL", 0, "createPlayer: credential is required");
1317
- let n = new je(e);
1329
+ let n = new Me(e);
1318
1330
  return await n.start(), n;
1319
1331
  }
1320
- var je = class {
1332
+ var Me = class {
1321
1333
  opts;
1322
1334
  abort = new AbortController();
1323
1335
  listeners = {};
@@ -1396,13 +1408,13 @@ var je = class {
1396
1408
  !r && !s() && l();
1397
1409
  }, e);
1398
1410
  };
1399
- d(Ee), this.armingSub = w({
1411
+ d(De), this.armingSub = w({
1400
1412
  eventsBaseUrl: n,
1401
1413
  matchUrn: this.opts.matchUrn,
1402
1414
  credential: this.opts.credential,
1403
- onAlive: () => d(De),
1415
+ onAlive: () => d(Oe),
1404
1416
  onState: (e) => {
1405
- r || s() || (e.state === "live" || e.state === "gone") && setTimeout(u, Math.random() * Oe);
1417
+ r || s() || (e.state === "live" || e.state === "gone") && setTimeout(u, Math.random() * ke);
1406
1418
  },
1407
1419
  onError: () => l()
1408
1420
  });
@@ -1447,8 +1459,8 @@ var je = class {
1447
1459
  emit: (e) => this.onEngineEvent(e)
1448
1460
  }, a = this.opts.engine ?? "auto";
1449
1461
  if (a === "native") return new W(i);
1450
- if (a === "hls" || L()) return new me(i);
1451
- if (he(this.opts.video)) return new W(i);
1462
+ if (a === "hls" || L()) return new he(i);
1463
+ if (ge(this.opts.video)) return new W(i);
1452
1464
  throw new t("INTERNAL", 0, "no supported HLS playback engine in this browser");
1453
1465
  }
1454
1466
  onEngineEvent(e) {
@@ -1493,7 +1505,7 @@ var je = class {
1493
1505
  r = 0, i = !1;
1494
1506
  },
1495
1507
  onError: () => {
1496
- this.destroyed || e !== this.loadGen || i || (r += 1, r >= ke && (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.`))));
1508
+ this.destroyed || e !== this.loadGen || i || (r += 1, r >= Z && (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.`))));
1497
1509
  }
1498
1510
  });
1499
1511
  }
@@ -1515,7 +1527,7 @@ var je = class {
1515
1527
  }
1516
1528
  t.controls = !1;
1517
1529
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
1518
- r && r.insertBefore(n, t), n.appendChild(t), q(n, this.opts.theme), this.container = n, this.controlBar = xe({
1530
+ r && r.insertBefore(n, t), n.appendChild(t), q(n, this.opts.theme), this.container = n, this.controlBar = Se({
1519
1531
  root: n,
1520
1532
  video: t,
1521
1533
  player: this,
@@ -1538,7 +1550,7 @@ var je = class {
1538
1550
  }
1539
1551
  startStats() {
1540
1552
  this.stopStats();
1541
- let e = Math.max(500, this.opts.statsIntervalMs ?? Z);
1553
+ let e = Math.max(500, this.opts.statsIntervalMs ?? Ae);
1542
1554
  this.statsTimer = setInterval(() => {
1543
1555
  this.engine && this.emit("stats", this.engine.getStats());
1544
1556
  }, e);
@@ -1548,7 +1560,7 @@ var je = class {
1548
1560
  }
1549
1561
  scheduleLicenseRefresh() {
1550
1562
  this.stopRefresh();
1551
- let e = this.opts.licenseRefreshMs ?? Ae;
1563
+ let e = this.opts.licenseRefreshMs ?? je;
1552
1564
  !e || !this._descriptor?.drmEnabled || (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
1553
1565
  }
1554
1566
  stopRefresh() {
@@ -1683,7 +1695,7 @@ function $(e) {
1683
1695
  }
1684
1696
  //#endregion
1685
1697
  //#region src/embed/host.ts
1686
- function Me(e) {
1698
+ function Ne(e) {
1687
1699
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
1688
1700
  if (!e.src) throw Error("mountEmbed: opts.src is required");
1689
1701
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -1742,8 +1754,8 @@ function Me(e) {
1742
1754
  }
1743
1755
  //#endregion
1744
1756
  //#region src/embed/page.ts
1745
- var Ne = /^[A-Za-z0-9:_.-]{1,128}$/;
1746
- function Pe() {
1757
+ var Pe = /^[A-Za-z0-9:_.-]{1,128}$/;
1758
+ function Fe() {
1747
1759
  let e = window.HAVIK_EMBED_CONFIG;
1748
1760
  if (e) return {
1749
1761
  baseUrl: e.baseUrl ?? "",
@@ -1765,9 +1777,9 @@ function Pe() {
1765
1777
  waitForLive: !0
1766
1778
  };
1767
1779
  }
1768
- async function Fe() {
1769
- let e = Pe();
1770
- Le();
1780
+ async function Ie() {
1781
+ let e = Fe();
1782
+ Re();
1771
1783
  let n = document.createElement("video");
1772
1784
  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);
1773
1785
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -1822,7 +1834,7 @@ async function Fe() {
1822
1834
  code: e.code,
1823
1835
  message: e.message
1824
1836
  })), e.on("autoplayblocked", () => {
1825
- i({ type: "oddin:autoplayblocked" }), Ie(n, e);
1837
+ i({ type: "oddin:autoplayblocked" }), Le(n, e);
1826
1838
  });
1827
1839
  }, s = async (r) => {
1828
1840
  a?.destroy(), a = null;
@@ -1888,7 +1900,7 @@ async function Fe() {
1888
1900
  a?.retry();
1889
1901
  break;
1890
1902
  case "oddin:load":
1891
- typeof e.matchUrn == "string" && Ne.test(e.matchUrn) && s(e.matchUrn);
1903
+ typeof e.matchUrn == "string" && Pe.test(e.matchUrn) && s(e.matchUrn);
1892
1904
  break;
1893
1905
  case "oddin:destroy":
1894
1906
  c();
@@ -1902,18 +1914,18 @@ async function Fe() {
1902
1914
  protocol: 1
1903
1915
  });
1904
1916
  }
1905
- function Ie(e, t) {
1917
+ function Le(e, t) {
1906
1918
  if (document.getElementById("havik-tap")) return;
1907
1919
  let n = document.createElement("button");
1908
1920
  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", () => {
1909
1921
  n.remove(), t.play();
1910
1922
  }), document.body.appendChild(n);
1911
1923
  }
1912
- function Le() {
1924
+ function Re() {
1913
1925
  let e = document.createElement("style");
1914
1926
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
1915
1927
  }
1916
1928
  //#endregion
1917
- export { G as ODDIN_THEME, t as PlaybackError, q as applyTheme, Fe 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, Me as mountEmbed, y as pollToLive, x as resolveStream, w as subscribeLiveState, C as watchStatus };
1929
+ export { G as ODDIN_THEME, t as PlaybackError, q as applyTheme, Ie 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, Ne as mountEmbed, y as pollToLive, x as resolveStream, w as subscribeLiveState, C as watchStatus };
1918
1930
 
1919
1931
  //# sourceMappingURL=index.js.map