@tidal-music/player
Version:
Player logic for TIDAL
3,190 lines • 78.5 kB
JavaScript
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