UNPKG

@tidal-music/player

Version:
3,190 lines 78.5 kB
let z = Object.freeze({
  apiUrl: "https://api.tidal.com/v1",
  desiredVolumeLevel: 1,
  gatherEvents: !0,
  loudnessNormalizationMode: "ALBUM",
  outputDevicesEnabled: !1,
  streamingWifiAudioQuality: "LOW"
});
function T(t) {
  return z[t];
}
function k(t) {
  return z = Object.freeze({
    ...z,
    ...t
  }), Object.keys(t).forEach((e) => {
    const r = e;
    Me.dispatchEvent(new CustomEvent(r, { detail: t[r] }));
  }), z;
}
const Me = new EventTarget();
var re = (t) => {
  throw TypeError(t);
}, se = (t, e, r) => e.has(t) || re("Cannot " + r), M = (t, e, r) => (se(t, e, "read from private field"), r ? r.call(t) : e.get(t)), W = (t, e, r) => e.has(t) ? re("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(t) : e.set(t, r), C = (t, e, r, s) => (se(t, e, "write to private field"), e.set(t, r), r), I, R, x, U;
class Ee {
  constructor(e) {
    W(this, I), W(this, R, !1), W(this, x), W(this, U), C(this, U, new URL(e)), this.synchronize();
  }
  /**
   * Returns the current time adjusted to server-time.
   *
   * @param clientCurrentTime The current time on the client side. Defaults to Date.now().
   * @returns The current adjusted time (or the client time if not synced yet).
   */
  // eslint-disable-next-line no-restricted-syntax
  now(e = Date.now()) {
    return !M(this, x) || !M(this, I) ? (console.warn("TrueTime is not yet synchronized"), e) : M(this, x) + (e - M(this, I));
  }
  /**
   * Synchronizes the client's time with the server's time.
   * If the client's time is already synchronized within an hour, this method does nothing.
   *
   * @returns {Promise<void>} A promise that resolves when the synchronization is complete.
   */
  async synchronize() {
    if (!(M(this, I) && // eslint-disable-next-line no-restricted-syntax
    Math.abs(Date.now() - M(this, I)) < 36e5 || M(this, R))) {
      C(this, R, !0);
      try {
        const e = await fetch(M(this, U));
        e.ok && e.headers.has("date") && (C(this, x, new Date(e.headers.get("date")).getTime()), C(this, I, Date.now()));
      } catch (e) {
        console.error(e);
      }
      C(this, R, !1);
    }
  }
  /**
   * Returns the timestamp of a performance mark with the specified name and detail.
   * PS: `performance.mark` must be called with `startTime: trueTime.now()`.
   *
   * @param markName - The name of the performance mark.
   * @param detail - Optional. The detail of the performance mark.
   * @returns The timestamp of the performance mark, or undefined if not found.
   * @throws ReferenceError if the performance mark is not found.
   */
  timestamp(e, r) {
    let s;
    if (r) {
      if (s = performance.getEntriesByName(e).find((i) => "detail" in i && i.detail === r), !s)
        throw new ReferenceError(
          `There is no performance entry named "${e}" with detail "${r}"`
        );
    } else
      s = performance.getEntriesByName(e).pop();
    return s ? s.startTime : void 0;
  }
}
I = /* @__PURE__ */ new WeakMap(), R = /* @__PURE__ */ new WeakMap(), x = /* @__PURE__ */ new WeakMap(), U = /* @__PURE__ */ new WeakMap();
const y = new Ee("https://api.tidal.com/v1/ping"), ke = (t, e, r) => {
  for (const s of t) {
    const i = s.addedNodes[0];
    s.type === "childList" && i.id === e && r();
  }
};
function Te() {
  const t = document.getElementById("tidal-player-root");
  if (!t)
    return Promise.reject("No player root");
  if ("video-one" in t.children)
    return Promise.resolve();
  const e = [];
  return "video-one" in t.children || e.push(
    new Promise((r) => {
      new MutationObserver(
        (s) => ke(s, "video-one", r)
      ).observe(t, { childList: !0 });
    })
  ), Promise.all(e);
}
const O = document.createElement("video"), Ie = (t) => {
  t.setAttribute("crossorigin", "anonymous"), t.setAttribute("playsinline", "playsinline");
};
Ie(O);
O.id = "video-one";
const H = "tidal-player-root";
function Ae() {
  let t = document.getElementById(H);
  if (!t) {
    const e = document.createElement("template");
    e.id = H, document.body.appendChild(e), t = document.getElementById(H);
  }
  return Oe();
}
function Oe() {
  const t = document.getElementById(
    H
  );
  return t && (O.id in t.children || t.appendChild(O)), Te();
}
function Fe() {
  return new Promise(
    (t) => document.addEventListener(
      "click",
      () => {
        O.readyState === HTMLMediaElement.HAVE_NOTHING && !O.src && O.load(), t();
      },
      { once: !0 }
    )
  );
}
function Ne(t) {
  return new Promise((e) => setTimeout(() => e(), t));
}
function _e(t, e) {
  return new Promise((r) => {
    t.addEventListener(e, (s) => r(s), !1);
  });
}
class Be extends EventTarget {
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore - Setter
  #e;
  hasEventSender() {
    return !!this.#e;
  }
  set eventSender(e) {
    this.#e = e;
  }
  get eventSender() {
    return this.#e;
  }
}
class Ce extends EventTarget {
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore - Setter
  #e;
  async dispatchAuthorized() {
    const e = await this.#e.getCredentials();
    e?.token ? this.dispatchEvent(
      new CustomEvent("authorized", {
        detail: {
          credentials: e
        }
      })
    ) : this.dispatchEvent(
      new CustomEvent("unauthenticated", { detail: { credentials: e } })
    );
  }
  set credentialsProvider(e) {
    this.#e = e, this.dispatchAuthorized().catch(console.error), this.#e.bus((r) => {
      switch (r.detail.type) {
        case "CredentialsUpdatedMessage":
          this.dispatchAuthorized().catch(console.error);
          break;
        default:
          console.warn("Unhandled event from credentials provider: ", r);
          break;
      }
    });
  }
  get credentialsProvider() {
    return this.#e;
  }
}
class Q extends Error {
  errorCode = null;
  errorId = null;
  referenceId;
  constructor(e, r, s) {
    super(`${e}: ${r} (${s})`), this.errorId = e, this.errorCode = r, this.referenceId = s;
  }
  toJSON() {
    return {
      errorCode: this.errorCode,
      errorId: this.errorId,
      referenceId: this.referenceId
    };
  }
  toString() {
    return JSON.stringify(this.toJSON());
  }
}
async function N() {
  if (S.credentialsProvider) {
    const t = await S.credentialsProvider.getCredentials();
    return !!(t.token && t.userId);
  }
  return await _e(S, "authorized"), N();
}
const S = new Ce(), q = new Be();
async function Le() {
  const t = await N(), e = async () => {
    const { Pushkin: r } = await Promise.resolve().then(() => Et);
    return r.refresh();
  };
  if (t) {
    k({
      gatherEvents: !0
    });
    try {
      await Promise.all([e()]);
    } catch (r) {
      console.error(r);
    }
  } else
    k({
      gatherEvents: !1
    });
}
function Re() {
  k({
    gatherEvents: !0
  });
}
S.addEventListener("authorized", () => {
  Le().then().catch(console.error);
});
S.addEventListener("unauthenticated", () => {
  Re();
});
async function Z() {
  return S.credentialsProvider ? (await S.credentialsProvider.getCredentials()).token ?? null : null;
}
class xe extends EventTarget {
  dispatchError(e) {
    this.dispatchEvent(
      new CustomEvent("error", {
        detail: e.toJSON()
      })
    );
  }
}
const b = new xe();
class De {
  #e = /* @__PURE__ */ new Map();
  #r = /* @__PURE__ */ new Map();
  #s = /* @__PURE__ */ new Map();
  #t = /* @__PURE__ */ new Map();
  /**
   * Clones a session to re-use the media product and playback content for a new session.
   *
   * @param from - streamingSessionId to clone
   * @param to - target streamingSessionId
   */
  clone(e, r) {
    const s = this.getMediaProductTransition(e), i = this.getStreamInfo(e), c = this.getPlaybackInfo(e);
    if (!s || !i || !c)
      throw new Error(
        `Cannot clone ${e}. Missing mediaProductTransition or streamInfo.`
      );
    this.saveMediaProductTransition(r, {
      ...s,
      playbackContext: {
        ...s.playbackContext,
        playbackSessionId: r
      }
    }), this.saveStreamInfo(r, {
      ...i,
      streamingSessionId: r
    }), this.savePlaybackInfo(r, {
      ...c,
      streamingSessionId: r
    });
  }
  deleteMediaProductTransition(e) {
    this.#e.delete(e);
  }
  /**
   * Removed cached playbackInfo, streamInfo and mediaProductTransition for session.
   * For re-using playbackInfo etc for other sessions make sure to run clone before deletion!
   *
   * @see {@link StreamingSessionStore.clone}
   */
  deleteSession(e) {
    this.deleteMediaProductTransition(e), this.#r.delete(e), this.#s.delete(e), this.deleteStreamInfo(e);
  }
  deleteStartedStreamInfo(e) {
    this.#s.delete(e);
  }
  deleteStreamInfo(e) {
    this.#t.delete(e);
  }
  getMediaProductTransition(e) {
    return e ? this.#e.get(e) : void 0;
  }
  getPlaybackInfo(e) {
    return e ? this.#r.get(e) : void 0;
  }
  getStreamInfo(e) {
    return e ? this.#t.get(e) : void 0;
  }
  hasMediaProductTransition(e) {
    return this.#e.has(e);
  }
  hasPlaybackInfo(e) {
    return this.#r.has(e);
  }
  hasStartedStreamInfo(e) {
    return this.#s.has(e);
  }
  hasStreamInfo(e) {
    return this.#t.has(e);
  }
  overwriteDuration(e, r) {
    const s = this.#e.get(e);
    s && (s.playbackContext = {
      ...s.playbackContext,
      actualDuration: r
    }, this.saveMediaProductTransition(e, s));
  }
  saveMediaProductTransition(e, r) {
    this.#e.set(
      e,
      r
    );
  }
  savePlaybackInfo(e, r) {
    this.#r.set(e, r);
  }
  saveStreamInfo(e, r) {
    this.#t.set(e, r);
  }
  setStartedStreamInfo(e) {
    return this.#s.set(e, !0);
  }
}
const P = new De();
class We {
  activePlayer;
  preloadPlayer;
  get preloadedMediaProduct() {
    return P.getMediaProductTransition(
      this.preloadedStreamingSessionId
    )?.mediaProduct ?? void 0;
  }
  get preloadedStreamingSessionId() {
    return this.preloadPlayer?.preloadedStreamingSessionId ?? void 0;
  }
}
const l = new We();
let K;
const Ve = [
  {
    itemTypes: ["track", "video", "demo"],
    player: "shaka",
    qualities: ["HIGH", "LOSSLESS", "LOW", "HI_RES_LOSSLESS"]
  }
];
let _ = Ve;
const v = {
  browser: void 0,
  native: void 0,
  shaka: void 0
};
function ze(t) {
  _ = t;
}
async function Ue() {
  await Promise.all(
    _.map((t) => {
      const e = v[t.player];
      return e ? e.reset() : Promise.resolve();
    })
  ), l.activePlayer = void 0, l.preloadPlayer = void 0;
}
function X(t) {
  t.name !== "nativePlayer" && v.native && v.native.abandon(), l.activePlayer = t;
}
async function He(t) {
  X(t), await Promise.all(
    _.filter((e) => v[e.player] !== l.activePlayer).map(async (e) => {
      const r = v[e.player];
      return r && await r.reset(), Promise.resolve();
    })
  ), await l.activePlayer?.skipToPreloadedMediaProduct(), await l.activePlayer?.play();
}
function ne() {
  b.removeEventListener("ended", K);
}
function Qe(t) {
  l.activePlayer && t.name === l.activePlayer.name || (K = () => {
    He(t).then().catch(console.error);
  }, b.addEventListener("ended", K, { once: !0 }));
}
async function ie() {
  const { default: t } = await import("./nativePlayer-jjyNdflz.js");
  return v.native || (v.native = new t()), v.native;
}
async function $e() {
  const { default: t } = await import("./browserPlayer-D_8g6rO-.js");
  return v.browser || (v.browser = new t()), v.browser;
}
async function Ge() {
  const { default: t } = await import("./shakaPlayer-Ds-smGCb.js");
  return v.shaka || (v.shaka = new t()), v.shaka;
}
async function ae(t, e) {
  const r = _.filter((i) => i.itemTypes.includes(t)).filter(
    (i) => t === "track" && i.qualities && e ? i.qualities.includes(e) : !0
  );
  r.length === 0 && console.error(
    `No player found to handle audio quality "${String(
      e
    )}" for product type "${t}" in player config: `,
    _
  );
  const { player: s } = r[0];
  switch (s) {
    case "browser":
      return $e();
    case "native":
      return ie();
    case "shaka":
      return Ge();
    default:
      throw new Error("No player found.");
  }
}
async function qe() {
  await Promise.all(
    _.map((t) => {
      const e = v[t.player];
      return e ? e.unloadPreloadedMediaProduct() : Promise.resolve();
    })
  );
}
function Bt() {
  return l.activePlayer?.getPosition() ?? 0;
}
function Ct() {
  return T("loudnessNormalizationMode");
}
function Lt() {
  return l.activePlayer?.currentMediaProduct ?? null;
}
function Rt() {
  return l.preloadedMediaProduct ?? null;
}
async function xt() {
  const { outputDevices: t } = await import("./output-devices-WcqZ7YGX.js");
  return [...t.outputDevices];
}
function Dt() {
  const t = l.activePlayer;
  if (t) {
    const e = P.getMediaProductTransition(
      t.currentStreamingSessionId
    );
    if (e)
      return e.playbackContext;
  }
}
function Wt() {
  return l.activePlayer?.playbackState ?? "IDLE";
}
function Vt() {
  return "0.9.1";
}
function zt() {
  return T("streamingWifiAudioQuality");
}
function Ut() {
  return T("desiredVolumeLevel");
}
class je {
  #e;
  constructor() {
    this.#e = /* @__PURE__ */ new Map();
  }
  async #r(e, r) {
    const s = new TextEncoder().encode(
      `${e}-${r}`
    ), i = await crypto.subtle.digest("SHA-1", s);
    return Array.from(new Uint8Array(i)).map((o) => o.toString(16).padStart(2, "0")).join("");
  }
  /**
   * Delete a logged event by name and streamingSessionId.
   */
  async delete({
    name: e,
    streamingSessionId: r
  }) {
    const s = await this.#r(
      r,
      e
    );
    this.#e.delete(s);
  }
  /**
   * Get a logged event by name and streamingSessionId.
   */
  async get({
    name: e,
    streamingSessionId: r
  }) {
    const s = await this.#r(
      r,
      e
    );
    return this.#e.get(s);
  }
  /**
   * Adds or updates an event.
   */
  async put(e) {
    const r = await this.#r(
      e.streamingSessionId,
      e.name
    );
    this.#e.set(r, e);
  }
}
const J = new je();
async function B(t, e) {
  return T("gatherEvents") ? async (r) => {
    try {
      const s = await J.get({
        name: t,
        streamingSessionId: r.streamingSessionId
      }), i = Object.assign(
        {},
        e,
        s ? s.payload : {},
        r
      );
      for (const c of Object.keys(r)) {
        const d = c;
        Array.isArray(r[d]) && (i[d] = [
          ...e[d] || [],
          ...s && s.payload[d] || [],
          ...r[d] || []
        ]);
      }
      return await J.put({
        name: t,
        payload: i,
        streamingSessionId: r.streamingSessionId
      }), {
        name: t,
        payload: i,
        streamingSessionId: r.streamingSessionId
      };
    } catch (s) {
      console.error(s);
    }
  } : (r) => Promise.resolve(void 0);
}
const Ke = {
  endReason: "COMPLETE",
  endTimestamp: 0,
  errorCode: null,
  errorMessage: null,
  startTimestamp: 0,
  streamingSessionId: ""
}, Je = await B(
  "drm_license_fetch",
  Ke
);
function Ht(t) {
  return Je(t);
}
const Ye = {
  endReason: "COMPLETE",
  endTimestamp: 0,
  errorCode: null,
  errorMessage: null,
  startTimestamp: 0,
  streamingSessionId: ""
}, Xe = await B(
  "playback_info_fetch",
  Ye
);
function $(t) {
  return Xe(t);
}
function Qt(t) {
  switch (t) {
    case "bluetooth":
      return "BLUETOOTH";
    case "builtIn":
      return "BUILT_IN";
    case "displayPort":
      return "DISPLAY_PORT";
    case "hdmi":
      return "HDMI";
    case "systemDefault":
      return "SYSTEM_DEFAULT";
    case "usb":
      return "USB";
    // Never happens (cannot be chose by user)
    case "airplay":
    case "windowsCommunication":
    default:
      return;
  }
}
const Ze = {
  actualAssetPresentation: "FULL",
  actualAudioMode: "STEREO",
  actualProductId: null,
  actualQuality: "LOSSLESS",
  actualStartTimestamp: 0,
  actualStreamType: "ON_DEMAND",
  adaptations: [],
  cdm: "WIDEVINE",
  cdmVersion: null,
  endReason: "COMPLETE",
  endTimestamp: 0,
  errorCode: null,
  errorMessage: null,
  hasAds: !1,
  idealStartTimestamp: 0,
  outputDevice: null,
  productType: "TRACK",
  stalls: [],
  streamingSessionId: ""
}, et = await B(
  "playback_statistics",
  Ze
);
function ee(t) {
  return et(t);
}
const tt = {
  streamingSessionId: "",
  timestamp: 0
}, rt = await B(
  "streaming_session_end",
  tt
);
function st(t) {
  return rt(t);
}
const nt = {
  "Amazon Silk": "amazon_silk",
  "Android Browser": "android",
  Bada: "bada",
  BlackBerry: "blackberry",
  Chrome: "chrome",
  Chromium: "chromium",
  Electron: "electron",
  Epiphany: "epiphany",
  Firefox: "firefox",
  Focus: "focus",
  Generic: "generic",
  "Google Search": "google_search",
  Googlebot: "googlebot",
  "Internet Explorer": "ie",
  "K-Meleon": "k_meleon",
  Maxthon: "maxthon",
  "Microsoft Edge": "edge",
  "MZ Browser": "mz",
  "NAVER Whale Browser": "naver",
  Opera: "opera",
  "Opera Coast": "opera_coast",
  PhantomJS: "phantomjs",
  Puffin: "puffin",
  QupZilla: "qupzilla",
  QQ: "qq",
  QQLite: "qqlite",
  Safari: "safari",
  Sailfish: "sailfish",
  "Samsung Internet for Android": "samsung_internet",
  SeaMonkey: "seamonkey",
  Sleipnir: "sleipnir",
  Swing: "swing",
  Tizen: "tizen",
  "UC Browser": "uc",
  Vivaldi: "vivaldi",
  "WebOS Browser": "webos",
  WeChat: "wechat",
  "Yandex Browser": "yandex",
  Roku: "roku"
}, oe = {
  amazon_silk: "Amazon Silk",
  android: "Android Browser",
  bada: "Bada",
  blackberry: "BlackBerry",
  chrome: "Chrome",
  chromium: "Chromium",
  electron: "Electron",
  epiphany: "Epiphany",
  firefox: "Firefox",
  focus: "Focus",
  generic: "Generic",
  googlebot: "Googlebot",
  google_search: "Google Search",
  ie: "Internet Explorer",
  k_meleon: "K-Meleon",
  maxthon: "Maxthon",
  edge: "Microsoft Edge",
  mz: "MZ Browser",
  naver: "NAVER Whale Browser",
  opera: "Opera",
  opera_coast: "Opera Coast",
  phantomjs: "PhantomJS",
  puffin: "Puffin",
  qupzilla: "QupZilla",
  qq: "QQ Browser",
  qqlite: "QQ Browser Lite",
  safari: "Safari",
  sailfish: "Sailfish",
  samsung_internet: "Samsung Internet for Android",
  seamonkey: "SeaMonkey",
  sleipnir: "Sleipnir",
  swing: "Swing",
  tizen: "Tizen",
  uc: "UC Browser",
  vivaldi: "Vivaldi",
  webos: "WebOS Browser",
  wechat: "WeChat",
  yandex: "Yandex Browser"
}, m = {
  tablet: "tablet",
  mobile: "mobile",
  desktop: "desktop",
  tv: "tv"
}, g = {
  WindowsPhone: "Windows Phone",
  Windows: "Windows",
  MacOS: "macOS",
  iOS: "iOS",
  Android: "Android",
  WebOS: "WebOS",
  BlackBerry: "BlackBerry",
  Bada: "Bada",
  Tizen: "Tizen",
  Linux: "Linux",
  ChromeOS: "Chrome OS",
  PlayStation4: "PlayStation 4",
  Roku: "Roku"
}, E = {
  EdgeHTML: "EdgeHTML",
  Blink: "Blink",
  Trident: "Trident",
  Presto: "Presto",
  Gecko: "Gecko",
  WebKit: "WebKit"
};
class n {
  /**
   * Get first matched item for a string
   * @param {RegExp} regexp
   * @param {String} ua
   * @return {Array|{index: number, input: string}|*|boolean|string}
   */
  static getFirstMatch(e, r) {
    const s = r.match(e);
    return s && s.length > 0 && s[1] || "";
  }
  /**
   * Get second matched item for a string
   * @param regexp
   * @param {String} ua
   * @return {Array|{index: number, input: string}|*|boolean|string}
   */
  static getSecondMatch(e, r) {
    const s = r.match(e);
    return s && s.length > 1 && s[2] || "";
  }
  /**
   * Match a regexp and return a constant or undefined
   * @param {RegExp} regexp
   * @param {String} ua
   * @param {*} _const Any const that will be returned if regexp matches the string
   * @return {*}
   */
  static matchAndReturnConst(e, r, s) {
    if (e.test(r))
      return s;
  }
  static getWindowsVersionName(e) {
    switch (e) {
      case "NT":
        return "NT";
      case "XP":
        return "XP";
      case "NT 5.0":
        return "2000";
      case "NT 5.1":
        return "XP";
      case "NT 5.2":
        return "2003";
      case "NT 6.0":
        return "Vista";
      case "NT 6.1":
        return "7";
      case "NT 6.2":
        return "8";
      case "NT 6.3":
        return "8.1";
      case "NT 10.0":
        return "10";
      default:
        return;
    }
  }
  /**
   * Get macOS version name
   *    10.5 - Leopard
   *    10.6 - Snow Leopard
   *    10.7 - Lion
   *    10.8 - Mountain Lion
   *    10.9 - Mavericks
   *    10.10 - Yosemite
   *    10.11 - El Capitan
   *    10.12 - Sierra
   *    10.13 - High Sierra
   *    10.14 - Mojave
   *    10.15 - Catalina
   *
   * @example
   *   getMacOSVersionName("10.14") // 'Mojave'
   *
   * @param  {string} version
   * @return {string} versionName
   */
  static getMacOSVersionName(e) {
    const r = e.split(".").splice(0, 2).map((s) => parseInt(s, 10) || 0);
    if (r.push(0), r[0] === 10)
      switch (r[1]) {
        case 5:
          return "Leopard";
        case 6:
          return "Snow Leopard";
        case 7:
          return "Lion";
        case 8:
          return "Mountain Lion";
        case 9:
          return "Mavericks";
        case 10:
          return "Yosemite";
        case 11:
          return "El Capitan";
        case 12:
          return "Sierra";
        case 13:
          return "High Sierra";
        case 14:
          return "Mojave";
        case 15:
          return "Catalina";
        default:
          return;
      }
  }
  /**
   * Get Android version name
   *    1.5 - Cupcake
   *    1.6 - Donut
   *    2.0 - Eclair
   *    2.1 - Eclair
   *    2.2 - Froyo
   *    2.x - Gingerbread
   *    3.x - Honeycomb
   *    4.0 - Ice Cream Sandwich
   *    4.1 - Jelly Bean
   *    4.4 - KitKat
   *    5.x - Lollipop
   *    6.x - Marshmallow
   *    7.x - Nougat
   *    8.x - Oreo
   *    9.x - Pie
   *
   * @example
   *   getAndroidVersionName("7.0") // 'Nougat'
   *
   * @param  {string} version
   * @return {string} versionName
   */
  static getAndroidVersionName(e) {
    const r = e.split(".").splice(0, 2).map((s) => parseInt(s, 10) || 0);
    if (r.push(0), !(r[0] === 1 && r[1] < 5)) {
      if (r[0] === 1 && r[1] < 6) return "Cupcake";
      if (r[0] === 1 && r[1] >= 6) return "Donut";
      if (r[0] === 2 && r[1] < 2) return "Eclair";
      if (r[0] === 2 && r[1] === 2) return "Froyo";
      if (r[0] === 2 && r[1] > 2) return "Gingerbread";
      if (r[0] === 3) return "Honeycomb";
      if (r[0] === 4 && r[1] < 1) return "Ice Cream Sandwich";
      if (r[0] === 4 && r[1] < 4) return "Jelly Bean";
      if (r[0] === 4 && r[1] >= 4) return "KitKat";
      if (r[0] === 5) return "Lollipop";
      if (r[0] === 6) return "Marshmallow";
      if (r[0] === 7) return "Nougat";
      if (r[0] === 8) return "Oreo";
      if (r[0] === 9) return "Pie";
    }
  }
  /**
   * Get version precisions count
   *
   * @example
   *   getVersionPrecision("1.10.3") // 3
   *
   * @param  {string} version
   * @return {number}
   */
  static getVersionPrecision(e) {
    return e.split(".").length;
  }
  /**
   * Calculate browser version weight
   *
   * @example
   *   compareVersions('1.10.2.1',  '1.8.2.1.90')    // 1
   *   compareVersions('1.010.2.1', '1.09.2.1.90');  // 1
   *   compareVersions('1.10.2.1',  '1.10.2.1');     // 0
   *   compareVersions('1.10.2.1',  '1.0800.2');     // -1
   *   compareVersions('1.10.2.1',  '1.10',  true);  // 0
   *
   * @param {String} versionA versions versions to compare
   * @param {String} versionB versions versions to compare
   * @param {boolean} [isLoose] enable loose comparison
   * @return {Number} comparison result: -1 when versionA is lower,
   * 1 when versionA is bigger, 0 when both equal
   */
  /* eslint consistent-return: 1 */
  static compareVersions(e, r, s = !1) {
    const i = n.getVersionPrecision(e), c = n.getVersionPrecision(r);
    let d = Math.max(i, c), o = 0;
    const a = n.map([e, r], (h) => {
      const f = d - n.getVersionPrecision(h), w = h + new Array(f + 1).join(".0");
      return n.map(w.split("."), (p) => new Array(20 - p.length).join("0") + p).reverse();
    });
    for (s && (o = d - Math.min(i, c)), d -= 1; d >= o; ) {
      if (a[0][d] > a[1][d])
        return 1;
      if (a[0][d] === a[1][d]) {
        if (d === o)
          return 0;
        d -= 1;
      } else if (a[0][d] < a[1][d])
        return -1;
    }
  }
  /**
   * Array::map polyfill
   *
   * @param  {Array} arr
   * @param  {Function} iterator
   * @return {Array}
   */
  static map(e, r) {
    const s = [];
    let i;
    if (Array.prototype.map)
      return Array.prototype.map.call(e, r);
    for (i = 0; i < e.length; i += 1)
      s.push(r(e[i]));
    return s;
  }
  /**
   * Array::find polyfill
   *
   * @param  {Array} arr
   * @param  {Function} predicate
   * @return {Array}
   */
  static find(e, r) {
    let s, i;
    if (Array.prototype.find)
      return Array.prototype.find.call(e, r);
    for (s = 0, i = e.length; s < i; s += 1) {
      const c = e[s];
      if (r(c, s))
        return c;
    }
  }
  /**
   * Object::assign polyfill
   *
   * @param  {Object} obj
   * @param  {Object} ...objs
   * @return {Object}
   */
  static assign(e, ...r) {
    const s = e;
    let i, c;
    if (Object.assign)
      return Object.assign(e, ...r);
    for (i = 0, c = r.length; i < c; i += 1) {
      const d = r[i];
      typeof d == "object" && d !== null && Object.keys(d).forEach((a) => {
        s[a] = d[a];
      });
    }
    return e;
  }
  /**
   * Get short version/alias for a browser name
   *
   * @example
   *   getBrowserAlias('Microsoft Edge') // edge
   *
   * @param  {string} browserName
   * @return {string}
   */
  static getBrowserAlias(e) {
    return nt[e];
  }
  /**
   * Get short version/alias for a browser name
   *
   * @example
   *   getBrowserAlias('edge') // Microsoft Edge
   *
   * @param  {string} browserAlias
   * @return {string}
   */
  static getBrowserTypeByAlias(e) {
    return oe[e] || "";
  }
}
const u = /version\/(\d+(\.?_?\d+)+)/i, it = [
  /* Googlebot */
  {
    test: [/googlebot/i],
    describe(t) {
      const e = {
        name: "Googlebot"
      }, r = n.getFirstMatch(/googlebot\/(\d+(\.\d+))/i, t) || n.getFirstMatch(u, t);
      return r && (e.version = r), e;
    }
  },
  /* Opera < 13.0 */
  {
    test: [/opera/i],
    describe(t) {
      const e = {
        name: "Opera"
      }, r = n.getFirstMatch(u, t) || n.getFirstMatch(/(?:opera)[\s/](\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  /* Opera > 13.0 */
  {
    test: [/opr\/|opios/i],
    describe(t) {
      const e = {
        name: "Opera"
      }, r = n.getFirstMatch(/(?:opr|opios)[\s/](\S+)/i, t) || n.getFirstMatch(u, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/SamsungBrowser/i],
    describe(t) {
      const e = {
        name: "Samsung Internet for Android"
      }, r = n.getFirstMatch(u, t) || n.getFirstMatch(/(?:SamsungBrowser)[\s/](\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/Whale/i],
    describe(t) {
      const e = {
        name: "NAVER Whale Browser"
      }, r = n.getFirstMatch(u, t) || n.getFirstMatch(/(?:whale)[\s/](\d+(?:\.\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/MZBrowser/i],
    describe(t) {
      const e = {
        name: "MZ Browser"
      }, r = n.getFirstMatch(/(?:MZBrowser)[\s/](\d+(?:\.\d+)+)/i, t) || n.getFirstMatch(u, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/focus/i],
    describe(t) {
      const e = {
        name: "Focus"
      }, r = n.getFirstMatch(/(?:focus)[\s/](\d+(?:\.\d+)+)/i, t) || n.getFirstMatch(u, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/swing/i],
    describe(t) {
      const e = {
        name: "Swing"
      }, r = n.getFirstMatch(/(?:swing)[\s/](\d+(?:\.\d+)+)/i, t) || n.getFirstMatch(u, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/coast/i],
    describe(t) {
      const e = {
        name: "Opera Coast"
      }, r = n.getFirstMatch(u, t) || n.getFirstMatch(/(?:coast)[\s/](\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/opt\/\d+(?:.?_?\d+)+/i],
    describe(t) {
      const e = {
        name: "Opera Touch"
      }, r = n.getFirstMatch(/(?:opt)[\s/](\d+(\.?_?\d+)+)/i, t) || n.getFirstMatch(u, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/yabrowser/i],
    describe(t) {
      const e = {
        name: "Yandex Browser"
      }, r = n.getFirstMatch(/(?:yabrowser)[\s/](\d+(\.?_?\d+)+)/i, t) || n.getFirstMatch(u, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/ucbrowser/i],
    describe(t) {
      const e = {
        name: "UC Browser"
      }, r = n.getFirstMatch(u, t) || n.getFirstMatch(/(?:ucbrowser)[\s/](\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/Maxthon|mxios/i],
    describe(t) {
      const e = {
        name: "Maxthon"
      }, r = n.getFirstMatch(u, t) || n.getFirstMatch(/(?:Maxthon|mxios)[\s/](\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/epiphany/i],
    describe(t) {
      const e = {
        name: "Epiphany"
      }, r = n.getFirstMatch(u, t) || n.getFirstMatch(/(?:epiphany)[\s/](\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/puffin/i],
    describe(t) {
      const e = {
        name: "Puffin"
      }, r = n.getFirstMatch(u, t) || n.getFirstMatch(/(?:puffin)[\s/](\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/sleipnir/i],
    describe(t) {
      const e = {
        name: "Sleipnir"
      }, r = n.getFirstMatch(u, t) || n.getFirstMatch(/(?:sleipnir)[\s/](\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/k-meleon/i],
    describe(t) {
      const e = {
        name: "K-Meleon"
      }, r = n.getFirstMatch(u, t) || n.getFirstMatch(/(?:k-meleon)[\s/](\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/micromessenger/i],
    describe(t) {
      const e = {
        name: "WeChat"
      }, r = n.getFirstMatch(/(?:micromessenger)[\s/](\d+(\.?_?\d+)+)/i, t) || n.getFirstMatch(u, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/qqbrowser/i],
    describe(t) {
      const e = {
        name: /qqbrowserlite/i.test(t) ? "QQ Browser Lite" : "QQ Browser"
      }, r = n.getFirstMatch(/(?:qqbrowserlite|qqbrowser)[/](\d+(\.?_?\d+)+)/i, t) || n.getFirstMatch(u, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/msie|trident/i],
    describe(t) {
      const e = {
        name: "Internet Explorer"
      }, r = n.getFirstMatch(/(?:msie |rv:)(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/\sedg\//i],
    describe(t) {
      const e = {
        name: "Microsoft Edge"
      }, r = n.getFirstMatch(/\sedg\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/edg([ea]|ios)/i],
    describe(t) {
      const e = {
        name: "Microsoft Edge"
      }, r = n.getSecondMatch(/edg([ea]|ios)\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/vivaldi/i],
    describe(t) {
      const e = {
        name: "Vivaldi"
      }, r = n.getFirstMatch(/vivaldi\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/seamonkey/i],
    describe(t) {
      const e = {
        name: "SeaMonkey"
      }, r = n.getFirstMatch(/seamonkey\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/sailfish/i],
    describe(t) {
      const e = {
        name: "Sailfish"
      }, r = n.getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/silk/i],
    describe(t) {
      const e = {
        name: "Amazon Silk"
      }, r = n.getFirstMatch(/silk\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/phantom/i],
    describe(t) {
      const e = {
        name: "PhantomJS"
      }, r = n.getFirstMatch(/phantomjs\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/slimerjs/i],
    describe(t) {
      const e = {
        name: "SlimerJS"
      }, r = n.getFirstMatch(/slimerjs\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/blackberry|\bbb\d+/i, /rim\stablet/i],
    describe(t) {
      const e = {
        name: "BlackBerry"
      }, r = n.getFirstMatch(u, t) || n.getFirstMatch(/blackberry[\d]+\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/(web|hpw)[o0]s/i],
    describe(t) {
      const e = {
        name: "WebOS Browser"
      }, r = n.getFirstMatch(u, t) || n.getFirstMatch(/w(?:eb)?[o0]sbrowser\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/bada/i],
    describe(t) {
      const e = {
        name: "Bada"
      }, r = n.getFirstMatch(/dolfin\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/tizen/i],
    describe(t) {
      const e = {
        name: "Tizen"
      }, r = n.getFirstMatch(/(?:tizen\s?)?browser\/(\d+(\.?_?\d+)+)/i, t) || n.getFirstMatch(u, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/qupzilla/i],
    describe(t) {
      const e = {
        name: "QupZilla"
      }, r = n.getFirstMatch(/(?:qupzilla)[\s/](\d+(\.?_?\d+)+)/i, t) || n.getFirstMatch(u, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/firefox|iceweasel|fxios/i],
    describe(t) {
      const e = {
        name: "Firefox"
      }, r = n.getFirstMatch(/(?:firefox|iceweasel|fxios)[\s/](\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/electron/i],
    describe(t) {
      const e = {
        name: "Electron"
      }, r = n.getFirstMatch(/(?:electron)\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/MiuiBrowser/i],
    describe(t) {
      const e = {
        name: "Miui"
      }, r = n.getFirstMatch(/(?:MiuiBrowser)[\s/](\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/chromium/i],
    describe(t) {
      const e = {
        name: "Chromium"
      }, r = n.getFirstMatch(/(?:chromium)[\s/](\d+(\.?_?\d+)+)/i, t) || n.getFirstMatch(u, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/chrome|crios|crmo/i],
    describe(t) {
      const e = {
        name: "Chrome"
      }, r = n.getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  {
    test: [/GSA/i],
    describe(t) {
      const e = {
        name: "Google Search"
      }, r = n.getFirstMatch(/(?:GSA)\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  /* Android Browser */
  {
    test(t) {
      const e = !t.test(/like android/i), r = t.test(/android/i);
      return e && r;
    },
    describe(t) {
      const e = {
        name: "Android Browser"
      }, r = n.getFirstMatch(u, t);
      return r && (e.version = r), e;
    }
  },
  /* PlayStation 4 */
  {
    test: [/playstation 4/i],
    describe(t) {
      const e = {
        name: "PlayStation 4"
      }, r = n.getFirstMatch(u, t);
      return r && (e.version = r), e;
    }
  },
  /* Safari */
  {
    test: [/safari|applewebkit/i],
    describe(t) {
      const e = {
        name: "Safari"
      }, r = n.getFirstMatch(u, t);
      return r && (e.version = r), e;
    }
  },
  /* Something else */
  {
    test: [/.*/i],
    describe(t) {
      const e = /^(.*)\/(.*) /, r = /^(.*)\/(.*)[ \t]\((.*)/, i = t.search("\\(") !== -1 ? r : e;
      return {
        name: n.getFirstMatch(i, t),
        version: n.getSecondMatch(i, t)
      };
    }
  }
], at = [
  /* Roku */
  {
    test: [/Roku\/DVP/],
    describe(t) {
      const e = n.getFirstMatch(/Roku\/DVP-(\d+\.\d+)/i, t);
      return {
        name: g.Roku,
        version: e
      };
    }
  },
  /* Windows Phone */
  {
    test: [/windows phone/i],
    describe(t) {
      const e = n.getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i, t);
      return {
        name: g.WindowsPhone,
        version: e
      };
    }
  },
  /* Windows */
  {
    test: [/windows /i],
    describe(t) {
      const e = n.getFirstMatch(/Windows ((NT|XP)( \d\d?.\d)?)/i, t), r = n.getWindowsVersionName(e);
      return {
        name: g.Windows,
        version: e,
        versionName: r
      };
    }
  },
  /* Firefox on iPad */
  {
    test: [/Macintosh(.*?) FxiOS(.*?)\//],
    describe(t) {
      const e = {
        name: g.iOS
      }, r = n.getSecondMatch(/(Version\/)(\d[\d.]+)/, t);
      return r && (e.version = r), e;
    }
  },
  /* macOS */
  {
    test: [/macintosh/i],
    describe(t) {
      const e = n.getFirstMatch(/mac os x (\d+(\.?_?\d+)+)/i, t).replace(/[_\s]/g, "."), r = n.getMacOSVersionName(e), s = {
        name: g.MacOS,
        version: e
      };
      return r && (s.versionName = r), s;
    }
  },
  /* iOS */
  {
    test: [/(ipod|iphone|ipad)/i],
    describe(t) {
      const e = n.getFirstMatch(/os (\d+([_\s]\d+)*) like mac os x/i, t).replace(/[_\s]/g, ".");
      return {
        name: g.iOS,
        version: e
      };
    }
  },
  /* Android */
  {
    test(t) {
      const e = !t.test(/like android/i), r = t.test(/android/i);
      return e && r;
    },
    describe(t) {
      const e = n.getFirstMatch(/android[\s/-](\d+(\.\d+)*)/i, t), r = n.getAndroidVersionName(e), s = {
        name: g.Android,
        version: e
      };
      return r && (s.versionName = r), s;
    }
  },
  /* WebOS */
  {
    test: [/(web|hpw)[o0]s/i],
    describe(t) {
      const e = n.getFirstMatch(/(?:web|hpw)[o0]s\/(\d+(\.\d+)*)/i, t), r = {
        name: g.WebOS
      };
      return e && e.length && (r.version = e), r;
    }
  },
  /* BlackBerry */
  {
    test: [/blackberry|\bbb\d+/i, /rim\stablet/i],
    describe(t) {
      const e = n.getFirstMatch(/rim\stablet\sos\s(\d+(\.\d+)*)/i, t) || n.getFirstMatch(/blackberry\d+\/(\d+([_\s]\d+)*)/i, t) || n.getFirstMatch(/\bbb(\d+)/i, t);
      return {
        name: g.BlackBerry,
        version: e
      };
    }
  },
  /* Bada */
  {
    test: [/bada/i],
    describe(t) {
      const e = n.getFirstMatch(/bada\/(\d+(\.\d+)*)/i, t);
      return {
        name: g.Bada,
        version: e
      };
    }
  },
  /* Tizen */
  {
    test: [/tizen/i],
    describe(t) {
      const e = n.getFirstMatch(/tizen[/\s](\d+(\.\d+)*)/i, t);
      return {
        name: g.Tizen,
        version: e
      };
    }
  },
  /* Linux */
  {
    test: [/linux/i],
    describe() {
      return {
        name: g.Linux
      };
    }
  },
  /* Chrome OS */
  {
    test: [/CrOS/],
    describe() {
      return {
        name: g.ChromeOS
      };
    }
  },
  /* Playstation 4 */
  {
    test: [/PlayStation 4/],
    describe(t) {
      const e = n.getFirstMatch(/PlayStation 4[/\s](\d+(\.\d+)*)/i, t);
      return {
        name: g.PlayStation4,
        version: e
      };
    }
  }
], ot = [
  /* Googlebot */
  {
    test: [/googlebot/i],
    describe() {
      return {
        type: "bot",
        vendor: "Google"
      };
    }
  },
  /* Huawei */
  {
    test: [/huawei/i],
    describe(t) {
      const e = n.getFirstMatch(/(can-l01)/i, t) && "Nova", r = {
        type: m.mobile,
        vendor: "Huawei"
      };
      return e && (r.model = e), r;
    }
  },
  /* Nexus Tablet */
  {
    test: [/nexus\s*(?:7|8|9|10).*/i],
    describe() {
      return {
        type: m.tablet,
        vendor: "Nexus"
      };
    }
  },
  /* iPad */
  {
    test: [/ipad/i],
    describe() {
      return {
        type: m.tablet,
        vendor: "Apple",
        model: "iPad"
      };
    }
  },
  /* Firefox on iPad */
  {
    test: [/Macintosh(.*?) FxiOS(.*?)\//],
    describe() {
      return {
        type: m.tablet,
        vendor: "Apple",
        model: "iPad"
      };
    }
  },
  /* Amazon Kindle Fire */
  {
    test: [/kftt build/i],
    describe() {
      return {
        type: m.tablet,
        vendor: "Amazon",
        model: "Kindle Fire HD 7"
      };
    }
  },
  /* Another Amazon Tablet with Silk */
  {
    test: [/silk/i],
    describe() {
      return {
        type: m.tablet,
        vendor: "Amazon"
      };
    }
  },
  /* Tablet */
  {
    test: [/tablet(?! pc)/i],
    describe() {
      return {
        type: m.tablet
      };
    }
  },
  /* iPod/iPhone */
  {
    test(t) {
      const e = t.test(/ipod|iphone/i), r = t.test(/like (ipod|iphone)/i);
      return e && !r;
    },
    describe(t) {
      const e = n.getFirstMatch(/(ipod|iphone)/i, t);
      return {
        type: m.mobile,
        vendor: "Apple",
        model: e
      };
    }
  },
  /* Nexus Mobile */
  {
    test: [/nexus\s*[0-6].*/i, /galaxy nexus/i],
    describe() {
      return {
        type: m.mobile,
        vendor: "Nexus"
      };
    }
  },
  /* Mobile */
  {
    test: [/[^-]mobi/i],
    describe() {
      return {
        type: m.mobile
      };
    }
  },
  /* BlackBerry */
  {
    test(t) {
      return t.getBrowserName(!0) === "blackberry";
    },
    describe() {
      return {
        type: m.mobile,
        vendor: "BlackBerry"
      };
    }
  },
  /* Bada */
  {
    test(t) {
      return t.getBrowserName(!0) === "bada";
    },
    describe() {
      return {
        type: m.mobile
      };
    }
  },
  /* Windows Phone */
  {
    test(t) {
      return t.getBrowserName() === "windows phone";
    },
    describe() {
      return {
        type: m.mobile,
        vendor: "Microsoft"
      };
    }
  },
  /* Android Tablet */
  {
    test(t) {
      const e = Number(String(t.getOSVersion()).split(".")[0]);
      return t.getOSName(!0) === "android" && e >= 3;
    },
    describe() {
      return {
        type: m.tablet
      };
    }
  },
  /* Android Mobile */
  {
    test(t) {
      return t.getOSName(!0) === "android";
    },
    describe() {
      return {
        type: m.mobile
      };
    }
  },
  /* desktop */
  {
    test(t) {
      return t.getOSName(!0) === "macos";
    },
    describe() {
      return {
        type: m.desktop,
        vendor: "Apple"
      };
    }
  },
  /* Windows */
  {
    test(t) {
      return t.getOSName(!0) === "windows";
    },
    describe() {
      return {
        type: m.desktop
      };
    }
  },
  /* Linux */
  {
    test(t) {
      return t.getOSName(!0) === "linux";
    },
    describe() {
      return {
        type: m.desktop
      };
    }
  },
  /* PlayStation 4 */
  {
    test(t) {
      return t.getOSName(!0) === "playstation 4";
    },
    describe() {
      return {
        type: m.tv
      };
    }
  },
  /* Roku */
  {
    test(t) {
      return t.getOSName(!0) === "roku";
    },
    describe() {
      return {
        type: m.tv
      };
    }
  }
], ct = [
  /* EdgeHTML */
  {
    test(t) {
      return t.getBrowserName(!0) === "microsoft edge";
    },
    describe(t) {
      if (/\sedg\//i.test(t))
        return {
          name: E.Blink
        };
      const r = n.getFirstMatch(/edge\/(\d+(\.?_?\d+)+)/i, t);
      return {
        name: E.EdgeHTML,
        version: r
      };
    }
  },
  /* Trident */
  {
    test: [/trident/i],
    describe(t) {
      const e = {
        name: E.Trident
      }, r = n.getFirstMatch(/trident\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  /* Presto */
  {
    test(t) {
      return t.test(/presto/i);
    },
    describe(t) {
      const e = {
        name: E.Presto
      }, r = n.getFirstMatch(/presto\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  /* Gecko */
  {
    test(t) {
      const e = t.test(/gecko/i), r = t.test(/like gecko/i);
      return e && !r;
    },
    describe(t) {
      const e = {
        name: E.Gecko
      }, r = n.getFirstMatch(/gecko\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  },
  /* Blink */
  {
    test: [/(apple)?webkit\/537\.36/i],
    describe() {
      return {
        name: E.Blink
      };
    }
  },
  /* WebKit */
  {
    test: [/(apple)?webkit/i],
    describe(t) {
      const e = {
        name: E.WebKit
      }, r = n.getFirstMatch(/webkit\/(\d+(\.?_?\d+)+)/i, t);
      return r && (e.version = r), e;
    }
  }
];
class te {
  /**
   * Create instance of Parser
   *
   * @param {String} UA User-Agent string
   * @param {Boolean} [skipParsing=false] parser can skip parsing in purpose of performance
   * improvements if you need to make a more particular parsing
   * like {@link Parser#parseBrowser} or {@link Parser#parsePlatform}
   *
   * @throw {Error} in case of empty UA String
   *
   * @constructor
   */
  constructor(e, r = !1) {
    if (e == null || e === "")
      throw new Error("UserAgent parameter can't be empty");
    this._ua = e, this.parsedResult = {}, r !== !0 && this.parse();
  }
  /**
   * Get UserAgent string of current Parser instance
   * @return {String} User-Agent String of the current <Parser> object
   *
   * @public
   */
  getUA() {
    return this._ua;
  }
  /**
   * Test a UA string for a regexp
   * @param {RegExp} regex
   * @return {Boolean}
   */
  test(e) {
    return e.test(this._ua);
  }
  /**
   * Get parsed browser object
   * @return {Object}
   */
  parseBrowser() {
    this.parsedResult.browser = {};
    const e = n.find(it, (r) => {
      if (typeof r.test == "function")
        return r.test(this);
      if (r.test instanceof Array)
        return r.test.some((s) => this.test(s));
      throw new Error("Browser's test function is not valid");
    });
    return e && (this.parsedResult.browser = e.describe(this.getUA())), this.parsedResult.browser;
  }
  /**
   * Get parsed browser object
   * @return {Object}
   *
   * @public
   */
  getBrowser() {
    return this.parsedResult.browser ? this.parsedResult.browser : this.parseBrowser();
  }
  /**
   * Get browser's name
   * @return {String} Browser's name or an empty string
   *
   * @public
   */
  getBrowserName(e) {
    return e ? String(this.getBrowser().name).toLowerCase() || "" : this.getBrowser().name || "";
  }
  /**
   * Get browser's version
   * @return {String} version of browser
   *
   * @public
   */
  getBrowserVersion() {
    return this.getBrowser().version;
  }
  /**
   * Get OS
   * @return {Object}
   *
   * @example
   * this.getOS();
   * {
   *   name: 'macOS',
   *   version: '10.11.12'
   * }
   */
  getOS() {
    return this.parsedResult.os ? this.parsedResult.os : this.parseOS();
  }
  /**
   * Parse OS and save it to this.parsedResult.os
   * @return {*|{}}
   */
  parseOS() {
    this.parsedResult.os = {};
    const e = n.find(at, (r) => {
      if (typeof r.test == "function")
        return r.test(this);
      if (r.test instanceof Array)
        return r.test.some((s) => this.test(s));
      throw new Error("Browser's test function is not valid");
    });
    return e && (this.parsedResult.os = e.describe(this.getUA())), this.parsedResult.os;
  }
  /**
   * Get OS name
   * @param {Boolean} [toLowerCase] return lower-cased value
   * @return {String} name of the OS — macOS, Windows, Linux, etc.
   */
  getOSName(e) {
    const { name: r } = this.getOS();
    return e ? String(r).toLowerCase() || "" : r || "";
  }
  /**
   * Get OS version
   * @return {String} full version with dots ('10.11.12', '5.6', etc)
   */
  getOSVersion() {
    return this.getOS().version;
  }
  /**
   * Get parsed platform
   * @return {{}}
   */
  getPlatform() {
    return this.parsedResult.platform ? this.parsedResult.platform : this.parsePlatform();
  }
  /**
   * Get platform name
   * @param {Boolean} [toLowerCase=false]
   * @return {*}
   */
  getPlatformType(e = !1) {
    const { type: r } = this.getPlatform();
    return e ? String(r).toLowerCase() || "" : r || "";
  }
  /**
   * Get parsed platform
   * @return {{}}
   */
  parsePlatform() {
    this.parsedResult.platform = {};
    const e = n.find(ot, (r) => {
      if (typeof r.test == "function")
        return r.test(this);
      if (r.test instanceof Array)
        return r.test.some((s) => this.test(s));
      throw new Error("Browser's test function is not valid");
    });
    return e && (this.parsedResult.platform = e.describe(this.getUA())), this.parsedResult.platform;
  }
  /**
   * Get parsed engine
   * @return {{}}
   */
  getEngine() {
    return this.parsedResult.engine ? this.parsedResult.engine : this.parseEngine();
  }
  /**
   * Get engines's name
   * @return {String} Engines's name or an empty string
   *
   * @public
   */
  getEngineName(e) {
    return e ? String(this.getEngine().name).toLowerCase() || "" : this.getEngine().name || "";
  }
  /**
   * Get parsed platform
   * @return {{}}
   */
  parseEngine() {
    this.parsedResult.engine = {};
    const e = n.find(ct, (r) => {
      if (typeof r.test == "function")
        return r.test(this);
      if (r.test instanceof Array)
        return r.test.some((s) => this.test(s));
      throw new Error("Browser's test function is not valid");
    });
    return e && (this.parsedResult.engine = e.describe(this.getUA())), this.parsedResult.engine;
  }
  /**
   * Parse full information about the browser
   * @returns {Parser}
   */
  parse() {
    return this.parseBrowser(), this.parseOS(), this.parsePlatform(), this.parseEngine(), this;
  }
  /**
   * Get parsed result
   * @return {ParsedResult}
   */
  getResult() {
    return n.assign({}, this.parsedResult);
  }
  /**
   * Check if parsed browser matches certain conditions
   *
   * @param {Object} checkTree It's one or two layered object,
   * which can include a platform or an OS on the first layer
   * and should have browsers specs on the bottom-laying layer
   *
   * @returns {Boolean|undefined} Whether the browser satisfies the set conditions or not.
   * Returns `undefined` when the browser is no described in the checkTree object.
   *
   * @example
   * const browser = Bowser.getParser(window.navigator.userAgent);
   * if (browser.satisfies({chrome: '>118.01.1322' }))
   * // or with os
   * if (browser.satisfies({windows: { chrome: '>118.01.1322' } }))
   * // or with platforms
   * if (browser.satisfies({desktop: { chrome: '>118.01.1322' } }))
   */
  satisfies(e) {
    const r = {};
    let s = 0;
    const i = {};
    let c = 0;
    if (Object.keys(e).forEach((o) => {
      const a = e[o];
      typeof a == "string" ? (i[o] = a, c += 1) : typeof a == "object" && (r[o] = a, s += 1);
    }), s > 0) {
      const o = Object.keys(r), a = n.find(o, (f) => this.isOS(f));
      if (a) {
        const f = this.satisfies(r[a]);
        if (f !== void 0)
          return f;
      }
      const h = n.find(
        o,
        (f) => this.isPlatform(f)
      );
      if (h) {
        const f = this.satisfies(r[h]);
        if (f !== void 0)
          return f;
      }
    }
    if (c > 0) {
      const o = Object.keys(i), a = n.find(o, (h) => this.isBrowser(h, !0));
      if (a !== void 0)
        return this.compareVersion(i[a]);
    }
  }
  /**
   * Check if the browser name equals the passed string
   * @param browserName The string to compare with the browser name
   * @param [includingAlias=false] The flag showing whether alias will be included into comparison
   * @returns {boolean}
   */
  isBrowser(e, r = !1) {
    const s = this.getBrowserName().toLowerCase();
    let i = e.toLowerCase();
    const c = n.getBrowserTypeByAlias(i);
    return r && c && (i = c.toLowerCase()), i === s;
  }
  compareVersion(e) {
    let r = [0], s = e, i = !1;
    const c = this.getBrowserVersion();
    if (typeof c == "string")
      return e[0] === ">" || e[0] === "<" ? (s = e.substr(1), e[1] === "=" ? (i = !0, s = e.substr(2)) : r = [], e[0] === ">" ? r.push(1) : r.push(-1)) : e[0] === "=" ? s = e.substr(1) : e[0] === "~" && (i = !0, s = e.substr(1)), r.indexOf(
        n.compareVersions(c, s, i)
      ) > -1;
  }
  isOS(e) {
    return this.getOSName(!0) === String(e).toLowerCase();
  }
  isPlatform(e) {
    return this.getPlatformType(!0) === String(e).toLowerCase();
  }
  isEngine(e) {
    return this.getEngineName(!0) === String(e).toLowerCase();
  }
  /**
   * Is anything? Check if the browser is called "anything",
   * the OS called "anything" or the platform called "anything"
   * @param {String} anything
   * @param [includingAlias=false] The flag showing whether alias will be included into comparison
   * @returns {Boolean}
   */
  is(e, r = !1) {
    return this.isBrowser(e, r) || this.isOS(e) || this.isPlatform(e);
  }
  /**
   * Check if any of the given values satisfies this.is(anything)
   * @param {String[]} anythings
   * @returns {Boolean}
   */
  some(e = []) {
    return e.some((r) => this.is(r));
  }
}
/*!
 * Bowser - a browser detector
 * https://github.com/lancedikson/bowser
 * MIT License | (c) Dustin Diaz 2012-2015
 * MIT License | (c) Denis Demchenko 2015-2019
 */
class dt {
  /**
   * Creates a {@link Parser} instance
   *
   * @param {String} UA UserAgent string
   * @param {Boolean} [skipParsing=false] Will make the Parser postpone parsing until you ask it
   * explicitly. Same as `skipParsing` for {@link Parser}.
   * @returns {Parser}
   * @throws {Error} when UA is not a String
   *
   * @example
   * const parser = Bowser.getParser(window.navigator.userAgent);
   * const result = parser.getResult();
   */
  static getParser(e, r = !1) {
    if (typeof e != "string")
      throw new Error("UserAgent should be a string");
    return new te(e, r);
  }
  /**
   * Creates a {@link Parser} instance and runs {@link Parser.getResult} immediately
   *
   * @param UA
   * @return {ParsedResult}
   *
   * @example
   * const result = Bowser.parse(window.navigator.userAgent);
   */
  static parse(e) {
    return new te(e).getResult();
  }
  static get BROWSER_MAP() {
    return oe;
  }
  static get ENGINE_MAP() {
    return E;
  }
  static get OS_MAP() {
    return g;
  }
  static get PLATFORMS_MAP() {
    return m;
  }
}
const L = dt.parse(navigator.userAgent), lt = {
  abTestGroup: null,
  abTestName: null,
  browser: L.browser.name ?? null,
  browserVersion: L.browser.version ?? null,
  hardwarePlatform: L.browser.name === "Electron" ? "DESKTOP" : "WEB",
  isOfflineModeStart: !1,
  mobileNetworkType: null,
  networkType: "WIFI",
  operatingSystem: L.os.name ?? null,
  operatingSystemVersion: L.os.version ?? null,
  screenHeight: window.screen.height,
  screenWidth: window.screen.width,
  sessionProductId: "",
  sessionProductType: "TRACK",
  sessionTags: [],
  sessionType: "PLAYBACK",
  startReason: "IMPLICIT",
  streamingSessionId: "",
  timestamp: 0
}, ut = await B(
  "streaming_session_start",
  lt
);
function Y(t) {
  return ut(t);
}
async function ht(t) {
  if (await N())
    return t();
}
async function ce(t) {
  const e = {
    consentCategory: "NECESSARY",
    ..."extras" in t && { extras: t.extras },
    group: t.group,
    name: t.name,
    payload: t.payload,
    version: t.version
  }, r = e.payload.streamingSessionId;
  e.name === "progress" && "streamingSessionId" in e.payload && delete e.payload.streamingSessionId, q.eventSender.sendEvent(e), r && await J.delete({
    name: e.name,
    streamingSessionId: r
  });
}
async function G(t) {
  return ht(async () => {
    for (const e of t)
      if (e) {
        const r = await e;
        r && await ce({
          group: "streaming_metrics",
          name: r.name,
          payload: r.payload,
          version: 2
        });
      }
  });
}
function de(t = !0) {
  const e = t && "crypto" in window && "getRandomValues" in crypto;
  return ("10000000-1000-4000-8000" + -1e11).replace(/[018]/g, (r) => {
    const s = e ? crypto.getRandomValues(new Uint8Array(1))[0] : Math.floor(256 * Math.random());
    return (r ^ (s ?? 0) & 15 >> r / 4).toString(16);
  });
}
const D = {
  BTS: "application/vnd.tidal.bts",
  DASH: "application/dash+xml",
  EMU: "application/vnd.tidal.emu",
  HLS: "application/vnd.apple.mpegurl"
};
function mt(t) {
  return JSON.parse(atob(t));
}
function ft(t, e) {
  if ("codecs" in t)
    switch (t.codecs) {
      case "aac":
        return "aac";
      case "aac+":
        return "aac+";
      case "flac":
        return "flac";
      case "mp3":
        return "mp3";
      case "mp4a.40.2":
        return "mp4a.40.2";
      case "mp4a.40.5":
        return "mp4a.40.5";
    }
  else if (e)
    switch (e) {
      case "HI_RES":
      case "HI_RES_LOSSLESS":
      case "LOSSLESS":
        return "flac";
      case "HIGH":
        return "mp4a.40.2";
      case "LOW":
        return "mp4a.40.5";
    }
  return "none";
}
function pt(t) {
  switch (t) {
    case "aac":
    case "aac+":
    case "mp4a.40.2":
    case "mp4a.40.5":
      return "aac";
    case "flac":
      return t;
    case "mp3":
      return "mp3";
    default:
      return;
  }
}
function gt(t) {
  const e = /codecs=(["'])?((?:.(?!\1|>))*.?)\1?/.exec(t);
  if (e === null)
    return;
  const r = e[2] ?? "";
  if (r.includes("mp4a") || r.includes("aac"))
    return "aac";
  if (r === "flac")
    return r;
}
function yt(t) {
  const r = /PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?/.exec(t);
  if (!r)
    throw new Error("Invalid duration format");
  const s = parseFloat(r[1] || "0"), i = parseFloat(r[2] || "0"), c = parseFloat(r[3] || "0");
  return s * 3600 + i * 60 + c;
}
function vt(t) {
  const r = /mediaPresentationDuration="([^"]+)"/.exec(t);
  if (r) {
    const s = r[1];
    if (s)
      return yt(s);
  }
}
function le(t) {
  const { prefetched: e, streamingSessionId: r } = t, s = "albumReplayGain" in t ? {
    albumPeakAmplitude: t.albumPeakAmplitude,
    albumReplayGain: t.albumReplayGain,
    trackPeakAmplitude: t.trackPeakAmplitude,
    trackReplayGain: t.trackReplayGain
  } : {}, i = "audioQuality" in t ? t.audioQuality : t.videoQuality, c = {
    id: String(
      "videoId" in t ? t.videoId : t.trackId
    ),
    type: "trackId" in t ? "track" : "video"
  };
  if (t.manifestMimeType === D.BTS || t.manifestMimeType === D.EMU) {
    const d = mt(t.manifest), o = d.urls[0], a = ft(
      d,
      "audioQuality" in t ? t.audioQuality : null
    );
    let h;
    return "keyId" in d && (h = d.keyId), "licenseSecurityToken" in t && (h = t.licenseSecurityToken), {
      ...s,
      bitDepth: "bitDepth" in t ? t.bitDepth ?? void 0 : void 0,
      codec: pt(a),
      prefetched: e,
      quality: i,
      sampleRate: "sampleRate" in t ? t.sampleRate ?? void 0 : void 0,
      securityToken: h,
      streamFormat: a,
      streamUrl: o,
      streamingSessionId: r,
      ...c,
      expires: t.expires
    };
  }
  if (t.manifestMimeType === D.DASH) {
    const d = `data:${t.manifestMimeType};base64,${t.manifest}`, o = atob(t.manifest);
    return {
      ...s,
      bitDepth: "bitDepth" in t ? t.bitDepth ?? void 0 : void 0,
      codec: gt(o),
      duration: vt(o),
      prefetched: e,
      quality: i,
      sampleRate: "sampleRate" in t ? t.sampleRate ?? void 0 : void 0,
      securityToken: t.licenseSecurityToken,
      streamUrl: d,
      streamingSessionId: r,
      ...c,
      expires: t.expires
    };
  }
  throw new TypeError(
    `${t.manifestMimeType} not supported as playback info manifest mime type.`
  );
}
const ue = async (t, e = {}, r = 3) => {
  let i, c;
  try {
    i = await fetch(t, e);
  } catch (a) {
    if (r === 0)
      throw a;
    c = null;
  }
  if (i?.ok)
    return i;
  const d = i && i.status >= 500 && i.status <= 599, o = c === null || d || i?.status === 429;
  if (r > 0 && o) {
    const a = 500 * Math.abs(4 - r);
    return console.debug(
      `Retrying request for ${Math.abs(
        4 - r
      )}th time in ${a}ms`
    ), await Ne(a), ue(t, e, r - 1);
  }
  if (!i)
    throw new Error("Retries exchaused. Cannot fetch playbackinfo.");
  return i;
};
function wt(t, e) {
  switch (e) {
    case 4010:
      return "PEMonthlyStreamQuotaExceeded";
    case 4032:
    case 4035:
      return "PEContentNotAvailableInLocation";
    case 4033:
      return "PEContentNotAvailableForSubscription";
  }
  return t >= 500 && t < 600 || t === 429 ? "PERetryable" : t >= 400 && t < 500 ? "PENotAllowed" : "EUnexpected";
}
async function bt(t) {
  const {
    accessToken: e,
    audioQuality: r,
    clientId: s,
    mediaProduct: i,
    prefetch: c,
    streamingSessionId: d
  } = t, o = T("apiUrl"), a = new URL(
    `${o}/${i.productType}s/${i.productId}/playbackinfo`
  ), h = a.searchParams;
  i.productType === "video" ? h.set("videoquality", "HIGH") : h.set("audioquality", r), h.set("playbackmode", "STREAM"), h.set("assetpresentation", "FULL");
  const f = new Headers();
  c && f.set("x-tidal-prefetch", "true"), s && f.set("x-tidal-token", s), e && f.set("authorization", "Bearer " + e), d && f.set("x-tidal-streamingsessionid", d);
  let w;
  try {
    w = await ue(
      a.toString(),
      {
        headers: f,
        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
        // @ts-ignore - It is a thing.
        importance: "high"
      },
      3
    );
  } catch {
    throw new Q("PENetwork", "NPBI0");
  }
  const p = await w.json();
  if ("status" in p) {
    const Pe = `A${p.subStatus}`;
    throw new Q(wt(p.status, p.subStatus), Pe);
  }
  const be = "trackId" in p, Se = "videoId" in p;
  if (!be && !Se)
    throw new Q("EUnexpected", "B9999");
  return {
    ...p,
    // eslint-disable-next-line no-restricted-syntax
    expires: Date.now() + 36e5,
    prefetched: c
  };
}
function St(t) {
  const { mediaProduct: e, streamingSessionId: r } = t;
  return {
    assetPresentation: "FULL",
    audioMode: "STEREO",
    audioQuality: "LOW",
    // eslint-disable-next-line no-restricted-syntax
    expires: Date.now() + 36e5,
    manifest: btoa(
      JSON.stringify({
        mimeType: D.HLS,
        urls: [
          `https://fsu.fa.tidal.com/storage/${e.productId}.m3u8`
        ]
      })
    ),
    manifestMimeType: D.EMU,
    prefetched: !1,
    streamType: "ON_DEMAND",
    streamingSessionId: r,
    trackId: e.productId
  };
}
async function he(t) {
  const { streamingSessionId: e } = t, r = [];
  performance.mark("streaming_metrics:playback_info_fetch:startTimestamp", {
    detail: e,
    startTime: y.now()
  });
  try {
    let s;
    if (t.mediaProduct.productType === "demo" ? s = St(t) : s = await bt(t), s === void 0)
      throw new Error("Playback info was fetched, but undefined.");
    $({
      endReason: "COMPLETE",
      streamingSessionId: e
    }).catch(console.error);
    const i = "adInfo" in s;
    return "trackId" in s ? ee({
      actualAssetPresentation: s.assetPresentation,
      actualAudioMode: s.audioMode,
      actualProductId: String(s.trackId),
      actualQuality: s.audioQuality,
      actualStreamType: void 0,
      hasAds: i,
      productType: "TRACK",
      streamingSessionId: e
    }).catch(console.error) : ee({
      actualAssetPresentation: s.assetPresentation,
      actualAudioMode: void 0,
      actualProductId: String(s.videoId),
      actualQuality: s.videoQuality,
      actualStreamType: s.streamType,
      hasAds: i,
      productType: "VIDEO",
      streamingSessionId: e
    }).catch(console.error), performance.mark("streaming_metrics:playback_info_fetch:endTimestamp", {
      detail: e,
      startTime: y.now()
    }), s;
  } catch (s) {
    throw performance.mark("streaming_metrics:playback_info_fetch:endTimestamp", {
      detail: e,
      startTime: y.now()
    }), $({
      endReason: "ERROR",
      errorCode: s.message,
      errorMessage: s.stack,
      streamingSessionId: e
    }).catch(console.error), r.push(
      st({
        streamingSessionId: e,
        timestamp: y.timestamp(
          "streaming_metrics:playback_info_fetch:endTimestamp"
        )
      })
    ), s;
  } finally {
    r.push(
      $({
        endTimestamp: y.timestamp(
          "streaming_metrics:playback_info_fetch:endTimestamp"
        ),
        startTimestamp: y.timestamp(
          "streaming_metrics:playback_info_fetch:startTimestamp"
        ),
        streamingSessionId: e
      })
    ), G(r).catch(console.error), performance.clearMarks(
      "streaming_metrics:playback_info_fetch:endTimestamp"
    ), performance.clearMarks(
      "streaming_metrics:playback_info_fetch:startTimestamp"
    );
  }
}
class me {
  /**
   * The current time position to restore playback from when connection is back online.
   */
  static #e = 0;
  static #r = !1;
  /**
   * The media product to restore when connection is back online.
   */
  static #s = null;
  static #t;
  static #n;
  static #i;
  static #a() {
    const { activePlayer: e } = l;
    e?.hasStarted() && (this.#s = e.currentMediaProduct, this.#i = this.#d.bind(this), b.addEventListener(
      "playback-state-change",
      this.#i
    ));
  }
  static #o() {
    const { activePlayer: e } = l;
    e?.hasStarted() && (e.playbackState !== "PLAYING" && this.#s && (b.dispatchEvent(
      new CustomEvent("load", {
        detail: {
          currentTime: this.#e,
          mediaProduct: this.#s
        }
      })
    ), this.#c()), b.removeEventListener(
      "playback-state-change",
      this.#i
    ));
  }
  static #c() {
    this.#s = null, this.#e = 0;
  }
  static #d() {
    const { activePlayer: e } = l;
    e && (this.#e = e.currentTime);
  }
  static disable() {
    this.#r && (this.#t && window.removeEventListener("offline", this.#t, !1), this.#n && window.removeEventListener("online", this.#n, !1), this.#n = void 0, this.#t = void 0, this.#r = !1);
  }
  static enable() {
    this.#r || (this.#n = () => this.#o(), this.#t = () => this.#a(), window.addEventListener("offline", this.#t, !1), window.addEventListener("online", this.#n, !1), this.#r = !0);
  }
}
const Pt = "streaming-privileges-revoked";
function Mt(t) {
  return new CustomEvent(Pt, {
    detail: t
  });
}
let F;
async function fe(t) {
  const e = T("apiUrl"), r = await fetch(e + "/rt/connect", {
    headers: new Headers({
      Authorization: "Bearer " + t,
      "Content-Type": "application/json"
    }),
    method: "POST"
  }), { url: s } = await r.json();
  return s;
}
function pe(t) {
  return new Promise((e) => {
    t.addEventListener("open", () => e(), { once: !0 });
  });
}
class j {
  #e;
  #r;
  #s;
  #t;
  constructor() {
    this.#t = void 0, this.#r = this.#n(), b.addEventListener("user-action", () => {
      this.connected && this.userAction().then().catch(console.error);
    });
  }
  /**
   * Call this method to ensure pushkin is running.
   */
  static async ensure() {
    await N() && (F ? F.connected || await F.reconnect() : F = new j());
  }
  /**
   * Call this method when credentials changes to re-setup pushkin with the new credentials
   */
  static async refresh() {
    if (!await N())
      return;
    const r = await Z();
    F && r && F.reconnect().catch(console.error);
  }
  async #n() {
    const e = await Z();
    if (!e)
      throw new Error("No access token to connect to Pushkin.");
    const r = await fe(e);
    r && (this.#t = new WebSocket(r), await pe(this.#t), this.#s = (s) => this.#o(s.data), this.#e = () => this.#a(), this.#t.addEventListener("message", this.#s, !1), this.#t.addEventListener("closed", this.#e, !1), window.addEventListener("online", () => this.#a(), {
      once: !0
    }));
  }
  async #i(e) {
    if (!this.#t)
      throw new Error(
        "No socket connected. Did you forget to call connect method in Pushkin service first?"
      );
    this.#t.readyState !== WebSocket.OPEN && await this.#r, this.#t.send(JSON.stringify(e));
  }
  #a() {
    this.#r = this.#n();
  }
  #o(e) {
    const r = JSON.parse(e);
    switch (r.type) {
      case "PRIVILEGED_SESSION_NOTIFICATION": {
        b.dispatchEvent(
          Mt(
            String(r.payload.clientDisplayName)
          )
        ), l.activePlayer?.pause();
        break;
      }
      case "RECONNECT":
        this.reconnect().catch(console.error);
        break;
      default:
        console.warn("Unhandled event from Pushkin: ", r);
        break;
    }
  }
  reconnect() {
    return this.#t ? (this.#s && this.#t.removeEventListener("message", this.#s), this.#e && this.#t.removeEventListener("closed", this.#e), this.#t.close(), this.#r = this.#n(), this.#r) : Promise.resolve();
  }
  /**
   * Call this method to tell Pushkin a user action happened, so it can
   * make good qualified guesses if you're the session with allowed playback-
   */
  async userAction() {
    this.connected || await this.reconnect();
    try {
      await this.#i({
        payload: {
          startedAt: y.now()
        },
        type: "USER_ACTION"
      });
    } catch (e) {
      console.error(e);
    }
  }
  get connected() {
    return this.#t && this.#t.readyState === WebSocket.OPEN;
  }
}
const Et = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  __proto__: null,
  Pushkin: j,
  fetchWebSocketURL: fe,
  socketOpen: pe
}, Symbol.toStringTag, { value: "Module" }));
async function kt() {
  ne(), me.disable(), await Ue();
}
function Tt(t, e) {
  return t === "FULL" && e.sourceType !== "PRIVATE_LINK_SHARING";
}
const It = {
  actions: [],
  actualAssetPresentation: "PREVIEW",
  actualAudioMode: "STEREO",
  actualProductId: "",
  actualQuality: "LOW",
  endAssetPosition: -1,
  endTimestamp: -1,
  isPostPaywall: !1,
  playbackSessionId: "",
  productType: "TRACK",
  requestedProductId: "",
  sourceId: "",
  sourceType: "",
  startAssetPosition: -1,
  startTimestamp: -1
}, ge = await B(
  "playback_session",
  It
);
async function At(t) {
  const e = await ge(t);
  if (!e) {
    console.error("reducedData is undefined");
    return;
  }
  const { name: r, streamingSessionId: s } = e, { extras: i, ...c } = e.payload;
  return {
    extras: i,
    name: r,
    payload: c,
    streamingSessionId: s
  };
}
function $t(t, e) {
  return ge({
    actions: [e],
    streamingSessionId: t
  });
}
function Ot(t) {
  switch (t) {
    case "demo":
      return "UC";
    case "video":
      return "VIDEO";
    case "track":
    default:
      return "TRACK";
  }
}
async function Gt(t) {
  const e = await N();
  for (const r of t)
    if (r) {
      const s = await r;
      s && await ce({
        ..."extras" in s && { extras: s.extras },
        group: e ? "play_log" : "play_log_open",
        name: s.name,
        payload: s.payload,
        version: 2
      });
    }
}
let V, A;
async function Ft(t, e = []) {
  if (!q.hasEventSender())
    throw new Error("Playback not allowed without an event sender.");
  if (ne(), !t)
    return qe();
  const r = de();
  l.preloadedStreamingSessionId && l.preloadedMediaProduct && l.preloadedMediaProduct.productId === t.productId && P.hasStartedStreamInfo(
    l.preloadedStreamingSessionId
  ) && P.clone(
    l.preloadedStreamingSessionId,
    r
  );
  const s = y.now();
  G([
    Y({
      sessionTags: e,
      startReason: "IMPLICIT",
      streamingSessionId: r,
      timestamp: s
    })
  ]).catch(console.error);
  const { clientId: i, token: c } = await S.credentialsProvider.getCredentials(), d = T("streamingWifiAudioQuality");
  let o;
  if (P.hasPlaybackInfo(r)) {
    const w = P.getPlaybackInfo(r);
    w && w.expires > y.now() && (o = w);
  }
  if (o === void 0)
    try {
      o = await he({
        accessToken: c,
        audioQuality: d,
        clientId: i,
        mediaProduct: t,
        prefetch: !1,
        streamingSessionId: r
      }), P.savePlaybackInfo(r, o);
    } catch (w) {
      throw console.error(
        "Fetching playback info for preloaded item failed. Try again or do a load when playback ends.",
        w
      ), w;
    }
  l.preloadPlayer = await ae(
    t.productType,
    "trackId" in o ? o.audioQuality : void 0
  );
  const a = le(o);
  P.saveStreamInfo(
    a.streamingSessionId,
    a
  ), At({
    actualAssetPresentation: o.assetPresentation,
    actualAudioMode: "audioMode" in o ? o.audioMode : null,
    actualProductId: String(
      "videoId" in o ? o.videoId : o.trackId
    ),
    actualQuality: a.quality,
    extras: t.extras,
    isPostPaywall: Tt(
      o.assetPresentation,
      t
    ),
    playbackSessionId: r,
    productType: Ot(
      t.productType
    ),
    requestedProductId: t.productId,
    sourceId: t.sourceId,
    sourceType: t.sourceType,
    startAssetPosition: 0,
    startTimestamp: s,
    streamingSessionId: r
  }).catch(console.error), G([
    Y({ streamingSessionId: r }),
    $({ streamingSessionId: r })
  ]).catch(console.error);
  const { activePlayer: h } = l;
  h && l.preloadPlayer && h.name === l.preloadPlayer.name ? await h.next({
    assetPosition: 0,
    mediaProduct: t,
    playbackInfo: o,
    streamInfo: a
  }) : h && await l.preloadPlayer.next({
    assetPosition: 0,
    mediaProduct: t,
    playbackInfo: o,
    streamInfo: a
  }), Qe(l.preloadPlayer);
}
function qt(t, e = []) {
  if (t !== void 0 && A && A.mediaProduct?.productId === t.productId && V && !V.signal.aborted)
    return A.promise;
  V = new AbortController();
  const r = Ft(t, e).then(() => {
    V.abort("done");
  });
  return A = {
    mediaProduct: t,
    promise: r
  }, r;
}
async function Nt(t, e = 0, r = !1) {
  if (!q.hasEventSender())
    throw new Error("Playback not allowed without an event sender.");
  if (await y.synchronize(), j.ensure().catch(console.error), A && A.mediaProduct?.productId === t.productId && await A.promise, l.activePlayer?.nextItem && l.activePlayer.nextItem.mediaProduct.productId === t.productId) {
    const p = l.activePlayer;
    return performance.mark(
      "streaming_metrics:playback_statistics:idealStartTimestamp",
      {
        detail: l.preloadedStreamingSessionId,
        startTime: y.now()
      }
    ), await p.reset({ keepPreload: !0 }), t.referenceId && l.preloadedStreamingSessionId && p.overwriteMediaProduct(
      l.preloadedStreamingSessionId,
      t
    ), p.skipToPreloadedMediaProduct();
  }
  const s = kt(), i = de();
  G([
    Y({
      sessionProductId: t.productId,
      sessionProductType: t.productType === "track" ? "TRACK" : "VIDEO",
      sessionType: "PLAYBACK",
      startReason: "EXPLICIT",
      streamingSessionId: i,
      timestamp: y.now()
    })
  ]).catch(console.error), performance.mark(
    "streaming_metrics:playback_statistics:idealStartTimestamp",
    {
      detail: i,
      startTime: y.now()
    }
  );
  const { clientId: c, token: d } = await S.credentialsProvider.getCredentials(), o = T("streamingWifiAudioQuality");
  let a = null;
  try {
    a = await he({
      accessToken: d,
      audioQuality: o,
      clientId: c,
      mediaProduct: t,
      prefetch: r,
      streamingSessionId: i
    });
  } catch (p) {
    p instanceof Q && b.dispatchError(p), document.location.href.includes("localhost") && console.error(p);
  }
  if (!a)
    return;
  const h = le(a);
  P.saveStreamInfo(
    h.streamingSessionId,
    h
  ), await s, me.enable();
  const f = "trackId" in a ? a.audioQuality : void 0, w = await ae(
    t.productType,
    f
  );
  return X(w), w.load(
    {
      assetPosition: e,
      mediaProduct: t,
      playbackInfo: a,
      streamInfo: h
    },
    "explicit"
  );
}
b.addEventListener("load", (t) => {
  if (t instanceof CustomEvent) {
    const { currentTime: e, mediaProduct: r } = t.detail;
    Nt(r, e).then().catch(console.error);
  }
});
function jt() {
  return l.activePlayer?.pause();
}
async function Kt() {
  await y.synchronize();
  const { activePlayer: t } = l;
  return t ? (b.dispatchEvent(new CustomEvent("user-action")), t.play()) : Promise.reject(new Error("No active player"));
}
async function Jt(t) {
  const { activePlayer: e } = l;
  return e?.seek(t);
}
function Yt(t) {
  try {
    new URL(t);
  } catch (e) {
    throw e;
  }
  k({
    apiUrl: t
  });
}
function Xt(t) {
  S.credentialsProvider = t;
}
function Zt(t) {
  q.eventSender = t;
}
function er(t) {
  k({
    loudnessNormalizationMode: t
  }), l.activePlayer?.updateVolumeLevel();
}
const ye = "active-device-changed";
function tr(t) {
  return new CustomEvent(ye, {
    detail: t
  });
}
function ve(t, e) {
  return new Promise((r) => {
    t.addEventListener(e, (s) => r(s), { once: !0 });
  });
}
async function rr(t) {
  const { outputDevices: e } = await import("./output-devices-WcqZ7YGX.js"), r = ve(b, ye), s = [...e.outputDevices].find(
    (c) => c.id === t
  );
  if (!s)
    throw new ReferenceError("Output device does not exist: " + t);
  e.activeDevice = s;
  const i = await r;
  if (i instanceof CustomEvent && i.detail === t)
    return t;
  throw new Error("Could not set new device.");
}
const we = "active-device-mode-changed";
function sr(t) {
  return new CustomEvent(we, {
    detail: t
  });
}
async function nr(t = "shared") {
  const { outputDevices: e } = await import("./output-devices-WcqZ7YGX.js"), r = ve(b, we);
  e.deviceMode = t;
  const s = await r;
  if (s instanceof CustomEvent && s.detail === t)
    return t;
  throw new Error("Could not set new device mode.");
}
function ir(t) {
  k({
    streamingWifiAudioQuality: t
  });
}
function ar(t) {
  k({
    desiredVolumeLevel: t
  });
}
async function or() {
  const t = await ie();
  X(t);
}
y.synchronize().then().catch(console.error);
function cr() {
  const { activePlayer: t } = l;
  if (!t)
    return null;
  switch (t.name) {
    case "browserPlayer":
      return t.mediaElement;
    case "shakaPlayer":
      return t.mediaElement;
    default:
      return null;
  }
}
function dr(t) {
  l.activePlayer = void 0, t.outputDevices === !0 && k({ outputDevicesEnabled: !0 }), ze(t.players);
}
function _t() {
  const t = localStorage.getItem("ssuid");
  try {
    t && indexedDB.deleteDatabase("streaming-sessions-" + t);
  } catch (e) {
    console.warn(`DB streaming-sessions-${t} could not be deleted`), console.error(e);
  }
}
Ae().then().catch(console.error);
Fe().then().catch(console.error);
_t();
export {
  Bt as A,
  dt as B,
  Ct as C,
  Lt as D,
  Rt as E,
  xt as F,
  Dt as G,
  Wt as H,
  Vt as I,
  zt as J,
  Ut as K,
  jt as L,
  Kt as M,
  kt as N,
  Jt as O,
  Q as P,
  Yt as Q,
  Xt as R,
  Zt as S,
  er as T,
  qt as U,
  rr as V,
  nr as W,
  ir as X,
  ar as Y,
  or as Z,
  l as a,
  sr as b,
  tr as c,
  Oe as d,
  b as e,
  S as f,
  T as g,
  G as h,
  Ht as i,
  Me as j,
  Gt as k,
  y as l,
  O as m,
  At as n,
  st as o,
  ee as p,
  Ot as q,
  Tt as r,
  P as s,
  Nt as t,
  $t as u,
  Qt as v,
  Ne as w,
  de as x,
  cr as y,
  dr as z
};
//# sourceMappingURL=index-CDrZVEhq.js.map