From 5322c99183c938642500615477dbb2e85ee74ae2 Mon Sep 17 00:00:00 2001
From: booploops <49113086+booploops@users.noreply.github.com>
Date: Wed, 2 Mar 2022 17:42:43 -0800
Subject: [PATCH 01/37] big ass front end cleanup
---
src/renderer/audio/audio.js | 27 +-
src/renderer/index.js | 4522 +----------------
src/renderer/{js => lib}/bootbox.min.js | 0
src/renderer/{js => lib}/bootstrap.min.js | 0
src/renderer/{js => lib}/fast-plural-rules.js | 0
.../{js => lib}/jquery-3.2.1.slim.min.js | 0
src/renderer/{js => lib}/less.js | 0
src/renderer/{js => lib}/notyf.min.js | 0
src/renderer/{js => lib}/popper.min.js | 0
src/renderer/lib/resonance-audio.min.js | 181 +
src/renderer/{js => lib}/showdown.min.js | 0
src/renderer/{js => lib}/smoothscroll.js | 0
src/renderer/{js => lib}/sortable.min.js | 0
src/renderer/{js => lib}/velocity.min.js | 0
src/renderer/{js => lib}/vue-horizontal.js | 0
.../{js => lib}/vue-observe-visibility.min.js | 0
src/renderer/{js => lib}/vue.dev.js | 0
src/renderer/{js => lib}/vue.js | 0
.../{js => lib}/vuedraggable.umd.min.js | 0
src/renderer/{js => lib}/vuex.min.js | 0
src/renderer/main/app.js | 28 +
src/renderer/main/cidercache.js | 24 +
src/renderer/main/ciderfrontapi.js | 16 +
src/renderer/main/events.js | 74 +
src/renderer/main/gamepad.js | 326 ++
src/renderer/main/vueapp.js | 4070 +++++++++++++++
src/renderer/main/vuex-store.js | 20 +
.../wsapi_interop.js} | 16 +-
src/renderer/views/main.ejs | 45 +-
29 files changed, 4787 insertions(+), 4562 deletions(-)
rename src/renderer/{js => lib}/bootbox.min.js (100%)
rename src/renderer/{js => lib}/bootstrap.min.js (100%)
rename src/renderer/{js => lib}/fast-plural-rules.js (100%)
rename src/renderer/{js => lib}/jquery-3.2.1.slim.min.js (100%)
rename src/renderer/{js => lib}/less.js (100%)
rename src/renderer/{js => lib}/notyf.min.js (100%)
rename src/renderer/{js => lib}/popper.min.js (100%)
create mode 100644 src/renderer/lib/resonance-audio.min.js
rename src/renderer/{js => lib}/showdown.min.js (100%)
rename src/renderer/{js => lib}/smoothscroll.js (100%)
rename src/renderer/{js => lib}/sortable.min.js (100%)
rename src/renderer/{js => lib}/velocity.min.js (100%)
rename src/renderer/{js => lib}/vue-horizontal.js (100%)
rename src/renderer/{js => lib}/vue-observe-visibility.min.js (100%)
rename src/renderer/{js => lib}/vue.dev.js (100%)
rename src/renderer/{js => lib}/vue.js (100%)
rename src/renderer/{js => lib}/vuedraggable.umd.min.js (100%)
rename src/renderer/{js => lib}/vuex.min.js (100%)
create mode 100644 src/renderer/main/app.js
create mode 100644 src/renderer/main/cidercache.js
create mode 100644 src/renderer/main/ciderfrontapi.js
create mode 100644 src/renderer/main/events.js
create mode 100644 src/renderer/main/gamepad.js
create mode 100644 src/renderer/main/vueapp.js
create mode 100644 src/renderer/main/vuex-store.js
rename src/renderer/{js/WSAPI_Interop.js => main/wsapi_interop.js} (92%)
diff --git a/src/renderer/audio/audio.js b/src/renderer/audio/audio.js
index bb604e45..8f24d107 100644
--- a/src/renderer/audio/audio.js
+++ b/src/renderer/audio/audio.js
@@ -1,4 +1,4 @@
-var CiderAudio = {
+const CiderAudio = {
context : null,
source : null,
audioNodes : {
@@ -304,7 +304,7 @@ var CiderAudio = {
CiderAudio.audioNodes.analogWarmth = []
- for (i = 0; i < WARMTH_FREQUENCIES.length; i++) {
+ for (let i = 0; i < WARMTH_FREQUENCIES.length; i++) {
CiderAudio.audioNodes.analogWarmth[i] = CiderAudio.context.createBiquadFilter();
CiderAudio.audioNodes.analogWarmth[i].type = 'peaking'; // 'peaking';
CiderAudio.audioNodes.analogWarmth[i].frequency.value = WARMTH_FREQUENCIES[i];
@@ -312,7 +312,7 @@ var CiderAudio = {
CiderAudio.audioNodes.analogWarmth[i].gain.value = WARMTH_GAIN[i] * app.cfg.audio.maikiwiAudio.analogWarmth_value;
}
- for (i = 1; i < WARMTH_FREQUENCIES.length; i ++) {
+ for (let i = 1; i < WARMTH_FREQUENCIES.length; i ++) {
CiderAudio.audioNodes.analogWarmth[i-1].connect(CiderAudio.audioNodes.analogWarmth[i]);
}
@@ -345,7 +345,7 @@ var CiderAudio = {
CiderAudio.audioNodes.llpw = []
if (app.cfg.audio.maikiwiAudio.ciderPPE_value === 0.55) {
- for (i = 0; i < c_LLPW_FREQUENCIES.length; i++) {
+ for (let i = 0; i < c_LLPW_FREQUENCIES.length; i++) {
CiderAudio.audioNodes.llpw[i] = CiderAudio.context.createBiquadFilter();
CiderAudio.audioNodes.llpw[i].type = 'peaking'; // 'peaking';
CiderAudio.audioNodes.llpw[i].frequency.value = c_LLPW_FREQUENCIES[i];
@@ -354,7 +354,7 @@ var CiderAudio = {
}
- for (i = 1; i < c_LLPW_FREQUENCIES.length; i ++) {
+ for (let i = 1; i < c_LLPW_FREQUENCIES.length; i ++) {
CiderAudio.audioNodes.llpw[i-1].connect(CiderAudio.audioNodes.llpw[i]);
}
@@ -373,7 +373,7 @@ var CiderAudio = {
}
else if (app.cfg.audio.maikiwiAudio.ciderPPE_value === 0.5) {
- for (i = 0; i < LLPW_FREQUENCIES.length; i++) {
+ for (let i = 0; i < LLPW_FREQUENCIES.length; i++) {
CiderAudio.audioNodes.llpw[i] = CiderAudio.context.createBiquadFilter();
CiderAudio.audioNodes.llpw[i].type = 'peaking'; // 'peaking';
CiderAudio.audioNodes.llpw[i].frequency.value = LLPW_FREQUENCIES[i];
@@ -382,7 +382,7 @@ var CiderAudio = {
}
- for (i = 1; i < LLPW_FREQUENCIES.length; i ++) {
+ for (let i = 1; i < LLPW_FREQUENCIES.length; i ++) {
CiderAudio.audioNodes.llpw[i-1].connect(CiderAudio.audioNodes.llpw[i]);
}
@@ -408,7 +408,7 @@ var CiderAudio = {
let VIBRANTBASSQ = app.cfg.audio.maikiwiAudio.vibrantBass.Q;
CiderAudio.audioNodes.vibrantbassNode = []
- for (i = 0; i < VIBRANTBASSBANDS.length; i++) {
+ for (let i = 0; i < VIBRANTBASSBANDS.length; i++) {
CiderAudio.audioNodes.vibrantbassNode[i] = CiderAudio.context.createBiquadFilter();
CiderAudio.audioNodes.vibrantbassNode[i].type = 'peaking'; // 'peaking';
CiderAudio.audioNodes.vibrantbassNode[i].frequency.value = VIBRANTBASSBANDS[i];
@@ -416,7 +416,7 @@ var CiderAudio = {
CiderAudio.audioNodes.vibrantbassNode[i].gain.value = VIBRANTBASSGAIN[i] * app.cfg.audio.maikiwiAudio.vibrantBass.multiplier;
}
- for (i = 1; i < VIBRANTBASSBANDS.length; i ++) {
+ for (let i = 1; i < VIBRANTBASSBANDS.length; i ++) {
CiderAudio.audioNodes.vibrantbassNode[i-1].connect(CiderAudio.audioNodes.vibrantbassNode[i]);
}
@@ -683,7 +683,7 @@ var CiderAudio = {
CiderAudio.audioNodes.audioBands = [];
- for (i = 0; i < BANDS.length; i++) {
+ for (let i = 0; i < BANDS.length; i++) {
CiderAudio.audioNodes.audioBands[i] = CiderAudio.context.createBiquadFilter();
CiderAudio.audioNodes.audioBands[i].type = 'peaking'; // 'peaking';
CiderAudio.audioNodes.audioBands[i].frequency.value = BANDS[i];
@@ -694,14 +694,11 @@ var CiderAudio = {
// Dynamic-ish loading
CiderAudio.hierarchical_loading();
- for (i = 1; i < BANDS.length; i ++) {
+ for (let i = 1; i < BANDS.length; i ++) {
CiderAudio.audioNodes.audioBands[i-1].connect(CiderAudio.audioNodes.audioBands[i]);
}
CiderAudio.audioNodes.audioBands[BANDS.length-1].connect(CiderAudio.context.destination);
}
}
-if (app.cfg.advanced.AudioContext){
- CiderAudio.init()
-
-}
\ No newline at end of file
+export {CiderAudio}
\ No newline at end of file
diff --git a/src/renderer/index.js b/src/renderer/index.js
index 538a4c6b..eb5d268e 100644
--- a/src/renderer/index.js
+++ b/src/renderer/index.js
@@ -1,29 +1,3 @@
-Vue.use(VueHorizontal);
-Vue.use(VueObserveVisibility);
-
-const CiderCache = {
- async getCache(file) {
- let cache = await ipcRenderer.sendSync("get-cache", file)
- if (isJson(cache)) {
- cache = JSON.parse(cache)
- if (Object.keys(cache).length === 0) {
- cache = false
- }
- } else {
- cache = false
- }
- return cache
- },
- async putCache(file, data) {
- console.log(`Caching ${file}`)
- ipcRenderer.invoke("put-cache", {
- file: file,
- data: JSON.stringify(data)
- })
- return true
- }
-}
-
var notyf = new Notyf();
const MusicKitObjects = {
@@ -39,20 +13,7 @@ const MusicKitObjects = {
}
}
-const CiderFrontAPI = {
- Objects: {
- MenuEntry: function () {
- this.id = ""
- this.name = ""
- this.onClick = () => {
- }
- }
- },
- AddMenuEntry(entry) {
- app.pluginMenuEntries.push(entry)
- app.pluginInstalled = true
- }
-}
+
const MusicKitTools = {
getHeader() {
@@ -70,4412 +31,6 @@ Array.prototype.limit = function (n) {
return this.slice(0, n);
};
-const store = new Vuex.Store({
- state: {
- library: {
- // songs: ipcRenderer.sendSync("get-library-songs"),
- // albums: ipcRenderer.sendSync("get-library-albums"),
- // recentlyAdded: ipcRenderer.sendSync("get-library-recentlyAdded"),
- // playlists: ipcRenderer.sendSync("get-library-playlists")
- },
- artwork: {
- playerLCD: ""
- }
- },
- mutations: {
- setLCDArtwork(state, artwork) {
- state.artwork.playerLCD = artwork
- }
- }
-})
-const app = new Vue({
- el: "#app",
- store: store,
- data: {
- version: ipcRenderer.sendSync("get-version"),
- appMode: "player",
- ipcRenderer: ipcRenderer,
- cfg: ipcRenderer.sendSync("getStore"),
- isDev: ipcRenderer.sendSync("is-dev"),
- drawertest: false,
- platform: "",
- mk: {},
- quickPlayQuery: "",
- pluginInstalled: false,
- pluginMenuEntries: [],
- lz: ipcRenderer.sendSync("get-i18n", "en_US"),
- lzListing: ipcRenderer.sendSync("get-i18n-listing"),
- search: {
- term: "",
- hints: [],
- showHints: false,
- results: {},
- resultsSocial: {},
- limit: 10
- },
- fullscreenLyrics: false,
- playerLCD: {
- playbackDuration: 0,
- desiredDuration: 0,
- userInteraction: false
- },
- drawer: {
- open: false,
- panel: ""
- },
- browsepage: [],
- listennow: [],
- madeforyou: [],
- radio: {
- personal: []
- },
- mklang: 'en',
- webview: {
- url: "",
- title: "",
- loading: false
- },
- showingPlaylist: [],
- appleCurator: [],
- artistPage: {
- data: {},
- },
- library: {
- backgroundNotification: {
- show: false,
- message: "",
- total: 0,
- progress: 0
- },
- songs: {
- sortingOptions: {
- "albumName": "0",
- "artistName": "0",
- "name": "0",
- "genre": "0",
- "releaseDate": "0",
- "durationInMillis": "0",
- "dateAdded": "0"
- },
- sorting: "name",
- sortOrder: "asc",
- listing: [],
- meta: { total: 0, progress: 0 },
- search: "",
- displayListing: [],
- downloadState: 0 // 0 = not started, 1 = in progress, 2 = complete, 3 = empty library
- },
- albums: {
- sortingOptions: {
- "albumName": "0",
- "artistName": "0",
- "name": "0",
- "genre": "0"
- },
- viewAs: 'covers',
- sorting: ["dateAdded", "name"], // [0] = recentlyadded page, [1] = albums page
- sortOrder: ["desc", "asc"], // [0] = recentlyadded page, [1] = albums page
- listing: [],
- meta: { total: 0, progress: 0 },
- search: "",
- displayListing: [],
- downloadState: 0 // 0 = not started, 1 = in progress, 2 = complete, 3 = empty library
- },
- artists: {
- sortingOptions: {
- "artistName": "0",
- "name": "0",
- "genre": "0",
- "releaseDate": "0"
- },
- viewAs: 'covers',
- sorting: ["dateAdded", "name"], // [0] = recentlyadded page, [1] = albums page
- sortOrder: ["desc", "asc"], // [0] = recentlyadded page, [1] = albums page
- listing: [],
- meta: { total: 0, progress: 0 },
- search: "",
- displayListing: [],
- downloadState: 0 // 0 = not started, 1 = in progress, 2 = complete, 3 = empty library
- },
- },
- playlists: {
- listing: [],
- details: {},
- loadingState: 0, // 0 loading, 1 loaded, 2 error
- id: "",
- trackMapping: {}
- },
- webremoteurl: "",
- webremoteqr: "",
- mxmtoken: "",
- mkIsReady: false,
- playerReady: false,
- animateBackground: false,
- currentArtUrl: '',
- currentArtUrlRaw: '',
- lyricon: false,
- currentTrackID: '',
- currentTrackIDBG: '',
- lyrics: [],
- currentLyricsLine: 0,
- lyriccurrenttime: 0,
- richlyrics: [],
- lyricsMediaItem: {},
- lyricsDebug: {
- current: 0,
- start: 0,
- end: 0
- },
- v3: {
- requestBody: {
- platform: "web"
- }
- },
- tmpHeight: '',
- tmpWidth: '',
- tmpVar: [],
- notification: false,
- chrome: {
- appliedTheme: {
- location: "",
- info: {}
- },
- desiredPageTransition: "wpfade_transform",
- hideUserInfo: ipcRenderer.sendSync("is-dev") || false,
- artworkReady: false,
- userinfo: {
- "id": "",
- "attributes": {
- "name": "Cider User",
- "handle": "CiderUser",
- "artwork": { "url": "./assets/logocut.png" }
- }
- },
- menuOpened: false,
- maximized: false,
- drawerOpened: false,
- drawerState: "queue",
- topChromeVisible: true,
- progresshover: false,
- windowControlPosition: "right",
- contentAreaScrolling: true,
- showCursor: false
- },
- collectionList: {
- response: {},
- title: "",
- type: ""
- },
- prevButtonBackIndicator: false,
- currentSongInfo: {},
- page: "",
- pageHistory: [],
- songstest: false,
- hangtimer: null,
- selectedMediaItems: [],
- routes: ["browse", "listen_now", "radio"],
- musicBaseUrl: "https://api.music.apple.com/",
- modals: {
- addToPlaylist: false,
- spatialProperties: false,
- qrcode: false,
- equalizer: false,
- audioSettings: false,
- pluginMenu: false,
- audioControls: false,
- showPlaylist: false,
- castMenu: false
- },
- socialBadges: {
- badgeMap: {},
- version: "",
- mediaItems: [],
- mediaItemDLState: 0 // 0 = not started, 1 = in progress, 2 = complete
- },
- menuPanel: {
- visible: false,
- event: null,
- content: {
- name: "",
- items: {},
- headerItems: {}
- }
- },
- pauseButtonTimer: null,
- activeCasts: []
- },
- watch: {
- cfg: {
- handler: function (val, oldVal) {
- console.log(`cfg changed from ${oldVal} to ${val}`);
- ipcRenderer.send("setStore", val);
- },
- deep: true
- },
- page: () => {
- document.getElementById("app-content").scrollTo(0, 0);
- app.resetState()
- },
- showingPlaylist: () => {
- if (!app.modals.showPlaylist) {
- document.getElementById("app-content").scrollTo(0, 0);
- app.resetState()
- }
- },
- artistPage: () => {
- document.getElementById("app-content").scrollTo(0, 0);
- app.resetState()
- }
- },
- methods: {
- simulateController() {
- this.chrome.showCursor = true
- let cursorPos = [0, 0];
- let intTabIndex = 0
- let self = this
- const cursorSpeedPvt = 8
- const cursorSize = 16
- let scrollSpeed = 8
- let buttonPressDelay = 500
- let stickDeadZone = 0.2
- let scrollGroup = null
- let scrollGroupY = null
- let elementFocusEnabled = true
-
- let cursorSpeed = cursorSpeedPvt
-
- let lastButtonPress = {
-
- }
-
- var sounds = {
- Confirm: new Audio("./sounds/confirm.ogg"),
- Menu: new Audio("./sounds/btn1.ogg"),
- Hover: new Audio("./sounds/hover.ogg")
- }
-
- let element = document.elementFromPoint(0, 0)
- let elementType = 0
-
- function appLoop() {
- var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
- if (!gamepads) {
- return;
- }
-
- var gp = gamepads[0];
-
- // LEFT STICK
- if (gp.axes[0] > stickDeadZone) {
- cursorPos[0] += (gp.axes[0] * cursorSpeed)
- } else if (gp.axes[0] < -stickDeadZone) {
- cursorPos[0] += (gp.axes[0] * cursorSpeed)
- }
-
- if (gp.axes[1] > stickDeadZone) {
- cursorPos[1] += (gp.axes[1] * cursorSpeed)
- } else if (gp.axes[1] < -stickDeadZone) {
- cursorPos[1] += (gp.axes[1] * cursorSpeed)
- }
-
- if (cursorPos[0] < cursorSize) {
- cursorPos[0] = cursorSize
- }
- if (cursorPos[1] < cursorSize) {
- cursorPos[1] = cursorSize
- }
- if (cursorPos[0] > window.innerWidth - cursorSize) {
- cursorPos[0] = window.innerWidth - cursorSize
- }
- if (cursorPos[1] > window.innerHeight - cursorSize) {
- cursorPos[1] = window.innerHeight - cursorSize
- }
-
-
- // RIGHT STICK.
- if (scrollGroupY) {
- if (gp.axes[3] > stickDeadZone) {
- $(scrollGroupY).scrollTop($(scrollGroupY).scrollTop() + (gp.axes[3] * scrollSpeed))
- elementFocusEnabled = false
- } else if (gp.axes[3] < -stickDeadZone) {
- $(scrollGroupY).scrollTop($(scrollGroupY).scrollTop() + (gp.axes[3] * scrollSpeed))
- elementFocusEnabled = false
- } else {
- elementFocusEnabled = true
- }
- }
-
-
-
- if (scrollGroup) {
- if (gp.axes[2] > stickDeadZone) {
- $(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + (gp.axes[2] * scrollSpeed))
- elementFocusEnabled = false
- } else if (gp.axes[2] < -stickDeadZone) {
- $(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + (gp.axes[2] * scrollSpeed))
- elementFocusEnabled = false
- } else {
- elementFocusEnabled = true
- }
- }
-
-
- $(".cursor").css({
- top: cursorPos[1] + "px",
- left: cursorPos[0] + "px",
- display: "block"
- })
-
- // A BUTTON
- if (gp.buttons[0].pressed) {
- if (!lastButtonPress["A"]) {
- lastButtonPress["A"] = 0
- }
- if (Date.now() - lastButtonPress["A"] > buttonPressDelay) {
- lastButtonPress["A"] = Date.now()
- sounds.Confirm.play()
- if (elementType == 0) {
- document.activeElement.dispatchEvent(new Event("click"))
- document.activeElement.dispatchEvent(new Event("controller-click"))
- } else {
- element.dispatchEvent(new Event("click"))
- element.dispatchEvent(new Event("controller-click"))
- }
- }
- }
-
- // B BUTTON
- if (gp.buttons[1].pressed) {
-
- if (!lastButtonPress["B"]) {
- lastButtonPress["B"] = 0
- }
- if (Date.now() - lastButtonPress["B"] > buttonPressDelay) {
- lastButtonPress["B"] = Date.now()
- if (elementType == 0) {
- document.activeElement.dispatchEvent(new Event("contextmenu"))
- setTimeout(() => {
- if ($(".menu-option").length > 0) {
- let bounds = $(".menu-option")[0].getBoundingClientRect()
- cursorPos[0] = bounds.left + (bounds.width / 2)
- cursorPos[1] = bounds.top + (bounds.height / 2)
- }
- }, 100)
- } else {
- element.dispatchEvent(new Event("contextmenu"))
- }
- }
-
- }
-
- // right bumper
- if (gp.buttons[5].pressed) {
- if (!lastButtonPress["RB"]) {
- lastButtonPress["RB"] = 0
- }
- if (Date.now() - lastButtonPress["RB"] > buttonPressDelay) {
- lastButtonPress["RB"] = Date.now()
- app.navigateForward()
-
- }
- }
-
- // left bumper
- if (gp.buttons[4].pressed) {
- if (!lastButtonPress["LB"]) {
- lastButtonPress["LB"] = 0
- }
- if (Date.now() - lastButtonPress["LB"] > buttonPressDelay) {
- lastButtonPress["LB"] = Date.now()
- app.navigateBack()
-
- }
- }
-
-
-
- // cursor hover
- if (elementFocusEnabled) {
- element = document.elementFromPoint(cursorPos[0], cursorPos[1])
- }
-
- if (element) {
-
- let closest = element.closest("[tabindex], input, button, a")
-
- // VERT SCROLL
- let scrollGroupCloY = element.closest(`[scrollaxis="y"]`)
- if (scrollGroupCloY) {
- scrollGroupY = scrollGroupCloY
- }
-
-
- // HOZ SCROLL
- let scrollGroupClo = element.closest(".v-hl-container")
-
- if (scrollGroupClo) {
- if (scrollGroupClo.classList.contains("v-hl-container")) {
- scrollGroup = scrollGroupClo
- scrollGroup.style["scroll-snap-type"] = "unset"
- } else {
- scrollGroup.style["scroll-snap-type"] = ""
- scrollGroup = null
- }
- }
-
- if (closest) {
- elementType = 0
- closest.focus()
- } else {
- if (closest) {
- closest.blur()
- }
- elementType = 1
- element.focus()
- }
- cursorSpeed = cursorSpeedPvt
- if (!element.classList.contains("app-chrome")
- && !element.classList.contains("app-content")) {
- cursorSpeed = cursorSpeedPvt
- }
- // console.log($._data($(element), "events"))
- } else {
- cursorSpeed = 12
- }
- // console.log(gp.axes[0], gp.axes[1])
- start = requestAnimationFrame(appLoop);
- }
-
- // controller pairing
- notyf.error("Press the button on your controller to pair it to Cider.")
- window.addEventListener("gamepadconnected", function (e) {
- console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
- e.gamepad.index, e.gamepad.id,
- e.gamepad.buttons.length, e.gamepad.axes.length);
- notyf.success("Pairing successful!")
- appLoop()
- }, { once: true });
-
- document.addEventListener("keydown", (e) => {
- sounds.Confirm.currentTime = 0
- sounds.Menu.currentTime = 0
- sounds.Hover.currentTime = 0
- let tabbable = $("[tabindex]")
- console.log(e.key)
- switch (e.key) {
- default:
- break;
- case "ArrowLeft":
- e.preventDefault()
-
- cursorPos[0] -= cursorSpeed
- break;
- case "ArrowRight":
- e.preventDefault()
-
- cursorPos[0] += cursorSpeed
- break;
- case "ArrowUp":
- e.preventDefault()
-
- cursorPos[1] -= cursorSpeed
- // sounds.Hover.play()
- // if(intTabIndex <= 0) {
- // intTabIndex = 0
- // }else{
- // intTabIndex--
- // }
- // $(tabbable[intTabIndex]).focus()
- // $("#app-content").scrollTop($(document.activeElement).offset().top)
- break;
- case "ArrowDown":
- e.preventDefault()
-
- cursorPos[1] += cursorSpeed
- // if(intTabIndex < tabbable.length) {
- // intTabIndex++
- // }else{
- // intTabIndex = tabbable.length
- // }
- // $(tabbable[intTabIndex]).focus()
- // $("#app-content").scrollTop($(document.activeElement).offset().top)
- break;
- case "c":
- app.resetState()
- break;
- case "x":
- // set cursorPos to the top right of the screen
- // sounds.Menu.play()
- if (elementType == 0) {
- document.activeElement.dispatchEvent(new Event("contextmenu"))
- } else {
- element.dispatchEvent(new Event("contextmenu"))
- }
-
- e.preventDefault()
- break;
- case "z":
- sounds.Confirm.play()
- if (elementType == 0) {
- document.activeElement.dispatchEvent(new Event("click"))
- document.activeElement.dispatchEvent(new Event("controller-click"))
- } else {
- element.dispatchEvent(new Event("click"))
- element.dispatchEvent(new Event("controller-click"))
- }
-
- e.preventDefault()
- break;
- }
-
- $(".cursor").css({
- top: cursorPos[1] + "px",
- left: cursorPos[0] + "px"
- })
- function lerp(a, b, n) {
- return (1 - n) * a + n * b
- }
-
-
- element = document.elementFromPoint(cursorPos[0], cursorPos[1])
-
- if (element) {
- let closest = element.closest("[tabindex], input, button, a")
- if (closest) {
- elementType = 0
- closest.focus()
- } else {
- elementType = 1
- element.focus()
- }
- }
- console.log(element)
- });
- },
- songLinkShare(amUrl) {
- notyf.open({ type: "info", className: "notyf-info", message: app.getLz('term.song.link.generate') })
- let self = this
- httpRequest = new XMLHttpRequest();
- httpRequest.open('GET', `https://api.song.link/v1-alpha.1/links?url=${amUrl}&userCountry=US`, true);
- httpRequest.send();
- httpRequest.onreadystatechange = function () {
- if (httpRequest.readyState === 4) {
- if (httpRequest.status === 200) {
- let response = JSON.parse(httpRequest.responseText);
- console.log(response);
- self.copyToClipboard(response.pageUrl)
- } else {
- console.log('There was a problem with the request.');
- notyf.error(app.getLz('term.requestError'))
- }
- }
- }
- },
- mainMenuVisibility(val) {
- if (val) {
- (this.mk.isAuthorized) ? this.chrome.menuOpened = !this.chrome.menuOpened : false;
- if (!this.mk.isAuthorized) {
- this.mk.authorize()
- }
- } else {
- setTimeout(() => {
- this.chrome.menuOpened = false
- }, 100)
- }
- },
- stringTemplateParser(expression, valueObj) {
- const templateMatcher = /{{\s?([^{}\s]*)\s?}}/g;
- let text = expression.replace(templateMatcher, (substring, value, index) => {
- value = valueObj[value];
- return value;
- });
- return text
- // stringTemplateParser('my name is {{name}} and age is {{age}}', {name: 'Tom', age:100})
- },
- async setLz(lang) {
- if (lang == "") {
- lang = this.cfg.general.language
- }
- this.lz = ipcRenderer.sendSync("get-i18n", lang)
- this.mklang = await this.MKJSLang()
- },
- getLz(message, options = {}) {
- if (this.lz[message]) {
- if (options["count"]) {
- if (typeof this.lz[message] === "object") {
- let type = window.fastPluralRules.getPluralFormNameForCardinalByLocale(this.cfg.general.language.replace("_", "-"), options["count"]);
- return this.lz[message][type] ?? ((this.lz[message])[Object.keys(this.lz[message])[0]] ?? this.lz[message])
- } else {
- // fallback English plural forms ( old i18n )
- if (options["count"] > 1) {
- return this.lz[message + "s"] ?? this.lz[message]
- } else {
- return this.lz[message] ?? this.lz[message + "s"]
- }
- }
- } else if (typeof this.lz[message] === "object") {
- return (this.lz[message])[Object.keys(this.lz[message])[0]]
- }
- return this.lz[message]
- } else {
- return message
- }
- },
- setLzManual() {
- app.$data.library.songs.sortingOptions = {
- "albumName": app.getLz('term.sortBy.album'),
- "artistName": app.getLz('term.sortBy.artist'),
- "name": app.getLz('term.sortBy.name'),
- "genre": app.getLz('term.sortBy.genre'),
- "releaseDate": app.getLz('term.sortBy.releaseDate'),
- "durationInMillis": app.getLz('term.sortBy.duration'),
- "dateAdded": app.getLz('term.sortBy.dateAdded')
- }
-
- app.$data.library.albums.sortingOptions = {
- "albumName": app.getLz('term.sortBy.album'),
- "artistName": app.getLz('term.sortBy.artist'),
- "name": app.getLz('term.sortBy.name'),
- "genre": app.getLz('term.sortBy.genre')
- }
-
- app.$data.library.artists.sortingOptions = {
- "artistName": app.getLz('term.sortBy.artist'),
- "name": app.getLz('term.sortBy.name'),
- "genre": app.getLz('term.sortBy.genre'),
- "releaseDate": app.getLz('term.sortBy.releaseDate')
- }
- },
- async showSocialListeningTo() {
- let contentIds = Object.keys(app.socialBadges.badgeMap)
- app.showCollection({ data: this.socialBadges.mediaItems }, "Friends Listening To", "albums")
- if (this.socialBadges.mediaItemDLState == 1 || this.socialBadges.mediaItemDLState == 2) {
- return
- }
- this.socialBadges.mediaItemDLState = 2
- await asyncForEach(contentIds, async (item) => {
- try {
- let type = "albums"
- if (item.includes("pl.")) {
- type = "playlists"
- }
- if (item.includes("ra.")) {
- type = "stations"
- }
- let found = await app.mk.api.v3.music(`/v1/catalog/us/${type}/${item}`)
- this.socialBadges.mediaItems.push(found.data.data[0])
- } catch (e) {
-
- }
- })
- },
- async openAppleMusicURL(url) {
- let properties = MusicKit.formattedMediaURL(url)
- let item = {
- id: properties.contentId,
- attributes: {
- playParams: {
- id: properties.contentId,
- kind: properties.kind,
- }
- },
- type: properties.kind,
- kind: properties.kind
- }
- app.routeView(item)
- },
- saveFile(fileName, urlFile) {
- let a = document.createElement("a");
- a.style = "display: none";
- document.body.appendChild(a);
- a.href = urlFile;
- a.download = fileName;
- a.click();
- window.URL.revokeObjectURL(url);
- a.remove();
- },
- async showMenuPanel(data, event) {
- app.menuPanel.visible = true;
- app.menuPanel.content.name = data.name ?? "";
- app.menuPanel.content.items = data.items ?? {};
- app.menuPanel.content.headerItems = data.headerItems ?? {};
- if (event) {
- app.menuPanel.event = event;
- }
- },
- async getSvgIcon(url) {
- let response = await fetch(url);
- let data = await response.text();
- return data;
- },
- getSocialBadges(cb = () => {
- }) {
- let self = this
- try {
- app.mk.api.v3.music("/v1/social/badging-map").then(data => {
- self.socialBadges.badgeMap = data.data.results.badgingMap
- cb(data.data.results.badgingMap)
- })
- } catch (ex) {
- this.socialBadges.badgeMap = {}
- }
- },
- addFavorite(id, type) {
- this.cfg.home.favoriteItems.push({
- id: id,
- type: type
- });
- },
- modularUITest(val = false) {
- this.fullscreenLyrics = val;
- if (val) {
- document.querySelector("#app-main").classList.add("modular-fs")
- } else {
- document.querySelector("#app-main").classList.remove("modular-fs")
- }
- },
- navigateBack() {
- this.chrome.desiredPageTransition = "wpfade_transform_backwards"
- return new Promise((resolve, reject) => {
- history.back()
- setTimeout(() => {
-
-
- resolve(this.chrome.desiredPageTransition = "wpfade_transform")
- }, 100)
- })
- },
- navigateForward() {
- history.forward()
- },
- getHTMLStyle() {
- // document.querySelector("html").style.background = "#222";
- document.querySelector("body").classList.add("notransparency")
- },
- resetState() {
- this.menuPanel.visible = false;
- app.selectedMediaItems = [];
- this.chrome.contentAreaScrolling = true
- for (let key in app.modals) {
- app.modals[key] = false;
- }
- },
- promptAddToPlaylist() {
- app.modals.addToPlaylist = true;
- },
- async addSelectedToNewPlaylist() {
- let self = this
- let pl_items = []
- for (let i = 0; i < self.selectedMediaItems.length; i++) {
- if (self.selectedMediaItems[i].kind == "song" || self.selectedMediaItems[i].kind == "songs") {
- self.selectedMediaItems[i].kind = "songs"
- pl_items.push({
- id: self.selectedMediaItems[i].id,
- type: self.selectedMediaItems[i].kind
- })
- } else if ((self.selectedMediaItems[i].kind == "album" || self.selectedMediaItems[i].kind == "albums") && self.selectedMediaItems[i].isLibrary != true) {
- self.selectedMediaItems[i].kind = "albums"
- let res = await self.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/albums/${self.selectedMediaItems[i].id}/tracks`);
- let ids = res.data.data.map(function (i) {
- return { id: i.id, type: i.type }
- })
- pl_items = pl_items.concat(ids)
- } else if (self.selectedMediaItems[i].kind == "library-song" || self.selectedMediaItems[i].kind == "library-songs") {
- self.selectedMediaItems[i].kind = "library-songs"
- pl_items.push({
- id: self.selectedMediaItems[i].id,
- type: self.selectedMediaItems[i].kind
- })
- } else if ((self.selectedMediaItems[i].kind == "library-album" || self.selectedMediaItems[i].kind == "library-albums") || (self.selectedMediaItems[i].kind == "album" && self.selectedMediaItems[i].isLibrary == true)) {
- self.selectedMediaItems[i].kind = "library-albums"
- let res = await self.mk.api.v3.music(`/v1/me/library/albums/${self.selectedMediaItems[i].id}/tracks`);
- let ids = res.data.data.map(function (i) {
- return { id: i.id, type: i.type }
- })
- pl_items = pl_items.concat(ids)
- } else {
- pl_items.push({
- id: self.selectedMediaItems[i].id,
- type: self.selectedMediaItems[i].kind
- })
- }
- }
- this.modals.addToPlaylist = false
- app.newPlaylist(app.getLz('term.newPlaylist'), pl_items)
- },
- async addSelectedToPlaylist(playlist_id) {
- let self = this
- let pl_items = []
- for (let i = 0; i < self.selectedMediaItems.length; i++) {
- if (self.selectedMediaItems[i].kind == "song" || self.selectedMediaItems[i].kind == "songs") {
- self.selectedMediaItems[i].kind = "songs"
- pl_items.push({
- id: self.selectedMediaItems[i].id,
- type: self.selectedMediaItems[i].kind
- })
- } else if ((self.selectedMediaItems[i].kind == "album" || self.selectedMediaItems[i].kind == "albums") && self.selectedMediaItems[i].isLibrary != true) {
- self.selectedMediaItems[i].kind = "albums"
- let res = await self.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/albums/${self.selectedMediaItems[i].id}/tracks`);
- let ids = res.data.data.map(function (i) {
- return { id: i.id, type: i.type }
- })
- pl_items = pl_items.concat(ids)
- } else if (self.selectedMediaItems[i].kind == "library-song" || self.selectedMediaItems[i].kind == "library-songs") {
- self.selectedMediaItems[i].kind = "library-songs"
- pl_items.push({
- id: self.selectedMediaItems[i].id,
- type: self.selectedMediaItems[i].kind
- })
- } else if ((self.selectedMediaItems[i].kind == "library-album" || self.selectedMediaItems[i].kind == "library-albums") || (self.selectedMediaItems[i].kind == "album" && self.selectedMediaItems[i].isLibrary == true)) {
- self.selectedMediaItems[i].kind = "library-albums"
- let res = await self.mk.api.v3.music(`/v1/me/library/albums/${self.selectedMediaItems[i].id}/tracks`);
- let ids = res.data.data.map(function (i) {
- return { id: i.id, type: i.type }
- })
- pl_items = pl_items.concat(ids)
- } else {
- pl_items.push({
- id: self.selectedMediaItems[i].id,
- type: self.selectedMediaItems[i].kind
- })
- }
-
- }
- this.modals.addToPlaylist = false
- await app.mk.api.v3.music(
- `/v1/me/library/playlists/${playlist_id}/tracks`, {}, {
- fetchOptions: {
- method: "POST",
- body: JSON.stringify({
- data: pl_items
- })
- }
- }
- ).then(() => {
- if (this.page == 'playlist_' + this.showingPlaylist.id) {
- this.getPlaylistFromID(this.showingPlaylist.id, true)
- }
- })
- },
- async init() {
- let self = this
- if (this.cfg.visual.theme != "default.less" && this.cfg.visual.theme != "") {
- this.setTheme(this.cfg.visual.theme)
- }
- this.setLz(this.cfg.general.language)
- this.setLzManual()
- clearTimeout(this.hangtimer)
- this.mk = MusicKit.getInstance()
- let needsReload = (typeof localStorage["music.ampwebplay.media-user-token"] == "undefined")
- this.mk.authorize().then(() => {
- self.mkIsReady = true
- if (needsReload) {
- document.location.reload()
- }
- })
- this.$forceUpdate()
- if (this.isDev) {
- this.mk.privateEnabled = true
- // Hide UserInfo if Dev mode
- } else {
- // Get Hide User from Settings
- this.chrome.hideUserInfo = !this.cfg.visual.showuserinfo
- this.mk.privateEnabled = this.cfg.general.privateEnabled
- }
- if (this.cfg.visual.hw_acceleration == "disabled") {
- document.body.classList.add("no-gpu")
- }
- this.mk._services.timing.mode = 0
- this.platform = ipcRenderer.sendSync('cider-platform');
-
- this.mklang = await this.MKJSLang()
-
- try {
- // Set profile name
- this.chrome.userinfo = (await app.mk.api.v3.music(`/v1/me/social-profile`)).data.data[0]
- } catch (err) {
- }
-
- this.mk._bag.features['seamless-audio-transitions'] = this.cfg.audio.seamless_audio
-
- // API Fallback
- if (!this.chrome.userinfo) {
- this.chrome.userinfo = {
- "id": "",
- "attributes": {
- "name": "Cider User",
- "handle": "CiderUser",
- "artwork": { "url": "./assets/logocut.png" }
- }
- }
- }
- MusicKitInterop.init()
- // Set the volume
-
- // Check the value of this.cfg.audio.muted
- if (!this.cfg.audio.muted) {
- // Set the mk.volume to the last stored volume data
- this.mk.volume = this.cfg.audio.volume
- } else if (this.cfg.audio.muted) {
- // Set mk.volume to -1 (setting to 0 wont work, so temp solution setting to -1)
- this.mk.volume = -1;
- }
- // ipcRenderer.invoke('getStoreValue', 'audio.volume').then((value) => {
- // self.mk.volume = value
- // })
-
- // load cached library
- let librarySongs = await CiderCache.getCache("library-songs")
- let libraryAlbums = await CiderCache.getCache("library-albums")
- if (librarySongs) {
- this.library.songs.listing = librarySongs
- this.library.songs.displayListing = this.library.songs.listing
- }
- if (libraryAlbums) {
- this.library.albums.listing = libraryAlbums
- this.library.albums.displayListing = this.library.albums.listing
- }
-
- window.onbeforeunload = function (e) {
- window.localStorage.setItem("currentTrack", JSON.stringify(app.mk.nowPlayingItem))
- window.localStorage.setItem("currentTime", JSON.stringify(app.mk.currentPlaybackTime))
- window.localStorage.setItem("currentQueue", JSON.stringify(app.mk.queue.items))
- };
-
- if (typeof MusicKit.PlaybackBitrate[app.cfg.audio.quality] !== "string") {
- app.mk.bitrate = MusicKit.PlaybackBitrate[app.cfg.audio.quality]
- } else {
- app.mk.bitrate = 256
- app.cfg.audio.quality = "HIGH"
- }
-
- switch (this.cfg.general.resumeOnStartupBehavior) {
- default:
- case "local":
- // load last played track
- try {
- let lastItem = window.localStorage.getItem("currentTrack")
- let time = window.localStorage.getItem("currentTime")
- let queue = window.localStorage.getItem("currentQueue")
- if (lastItem != null) {
- lastItem = JSON.parse(lastItem)
- let kind = lastItem.attributes.playParams.kind;
- let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
- app.mk.setQueue({
- [truekind]: [lastItem.attributes.playParams.id],
- parameters: { l: app.mklang }
- })
- app.mk.mute()
- setTimeout(() => {
- app.mk.play().then(() => {
- app.mk.pause().then(() => {
- if (time != null) {
- app.mk.seekToTime(time)
- }
- app.mk.unmute()
- if (queue != null) {
- queue = JSON.parse(queue)
- if (queue && queue.length > 0) {
- let ids = queue.map(e => (e.playParams ? e.playParams.id : (e.attributes.playParams ? e.attributes.playParams.id : '')))
- let i = 0;
- if (ids.length > 0) {
- for (id of ids) {
- if (!(i == 0 && ids[0] == lastItem.attributes.playParams.id)) {
- try {
- app.mk.playLater({ songs: [id] })
- } catch (err) {
- }
- }
- i++;
- }
- }
- }
- }
-
- })
-
- })
- }, 1500)
-
- }
-
- } catch (e) {
- console.log(e)
- }
- break;
- case "history":
- let history = await app.mk.api.v3.music(`/v1/me/recent/played/tracks`, { l: app.mklang })
- if (history.data.data.length > 0) {
- let lastItem = history.data.data[0]
- let kind = lastItem.attributes.playParams.kind;
- let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
- app.mk.setQueue({
- [truekind]: [lastItem.attributes.playParams.id],
- parameters: { l: app.mklang }
- })
- app.mk.mute()
- setTimeout(() => {
- app.mk.play().then(() => {
- app.mk.pause().then(() => {
- app.mk.unmute()
- })
- })
- }, 1500)
- }
-
- break;
- case "disabled":
-
- break;
- }
-
- MusicKit.getInstance().videoContainerElement = document.getElementById("apple-music-video-player")
-
- ipcRenderer.on('theme-update', (event, arg) => {
- less.refresh(true, true, true)
- self.setTheme(self.cfg.visual.theme, true)
- })
-
- ipcRenderer.on('SoundCheckTag', (event, tag) => {
- // let replaygain = self.parseSCTagToRG(tag)
- let soundcheck = tag.split(" ")
- let numbers = []
- for (item of soundcheck) {
- numbers.push(parseInt(item, 16))
-
- }
- numbers.shift()
- let peak = Math.max(numbers[6], numbers[7]) / 32768.0
- let gain = Math.pow(10, ((-7.63 - (Math.log10(peak) * 20)) / 20))// EBU R 128 Compliant
- console.debug(`[Cider][MaikiwiSoundCheck] Peak Gain: ${Math.log10(peak) * 20} | Adjusting '${Math.log10(gain) * 20}' dB`)
- try {
- //CiderAudio.audioNodes.gainNode.gain.value = (Math.min(Math.pow(10, (replaygain.gain / 20)), (1 / replaygain.peak)))
- CiderAudio.audioNodes.gainNode.gain.value = gain
- } catch (e) {
- }
- })
-
- ipcRenderer.on('play', function (_event, mode, id) {
- if (mode !== 'url') {
- self.mk.setQueue({ [mode]: id, parameters: { l: self.mklang } }).then(() => {
- app.mk.play()
- })
-
- } else {
- app.openAppleMusicURL(id)
- }
- });
-
- this.mk.addEventListener(MusicKit.Events.playbackStateDidChange, () => {
- ipcRenderer.send('wsapi-updatePlaybackState', wsapi.getAttributes());
- })
-
- this.mk.addEventListener(MusicKit.Events.playbackTimeDidChange, (a) => {
- self.lyriccurrenttime = self.mk.currentPlaybackTime
- this.currentSongInfo = a
- self.playerLCD.playbackDuration = (self.mk.currentPlaybackTime)
- // wsapi
- ipcRenderer.send('wsapi-updatePlaybackState', wsapi.getAttributes());
- })
-
- this.mk.addEventListener(MusicKit.Events.nowPlayingItemDidChange, (a) => {
- if (self.$refs.queue) {
- self.$refs.queue.updateQueue();
- }
- this.currentSongInfo = a
-
-
- if (app.cfg.audio.normalization) {
- // get unencrypted audio previews to get SoundCheck's normalization tag
- try {
- let previewURL = null
- try {
- previewURL = app.mk.nowPlayingItem.previewURL
- } catch (e) {
- }
- if (previewURL == null && ((app.mk.nowPlayingItem?._songId ?? (app.mk.nowPlayingItem["songId"] ?? app.mk.nowPlayingItem.relationships.catalog.data[0].id)) != -1)) {
- app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/songs/${app.mk.nowPlayingItem?._songId ?? (app.mk.nowPlayingItem["songId"] ?? app.mk.nowPlayingItem.relationships.catalog.data[0].id)}`).then((response) => {
- previewURL = response.data.data[0].attributes.previews[0].url
- if (previewURL)
- ipcRenderer.send('getPreviewURL', previewURL)
- })
- } else {
- if (previewURL)
- ipcRenderer.send('getPreviewURL', previewURL)
- }
-
- } catch (e) {
- }
- }
-
-
- try {
- a = a.item.attributes;
- } catch (_) {
- }
- let type = (self.mk.nowPlayingItem != null) ? self.mk.nowPlayingItem["type"] ?? '' : '';
-
- if (type.includes("musicVideo") || type.includes("uploadedVideo") || type.includes("music-movie")) {
- document.getElementById("apple-music-video-container").style.display = "block";
- // app.chrome.topChromeVisible = false
- } else {
- document.getElementById("apple-music-video-container").style.display = "none";
- // app.chrome.topChromeVisible = true
- }
- self.chrome.artworkReady = false
- self.lyrics = []
- self.richlyrics = []
- app.getCurrentArtURL();
- // app.getNowPlayingArtwork(42);
- app.getNowPlayingArtworkBG(32);
- app.loadLyrics();
-
- // Playback Notifications
- if (this.cfg.general.playbackNotifications && !document.hasFocus() && a.artistName && a.artwork && a.name) {
- if (this.notification) {
- this.notification.close()
- }
- this.notification = new Notification(a.name, {
- body: a.artistName,
- icon: a.artwork.url.replace('/{w}x{h}bb', '/512x512bb').replace('/2000x2000bb', '/35x35bb'),
- silent: true,
- });
- }
-
- })
-
-
- this.mk.addEventListener(MusicKit.Events.playbackVolumeDidChange, (_a) => {
- this.cfg.audio.volume = this.mk.volume
- })
-
- this.refreshPlaylists(this.isDev)
- document.body.removeAttribute("loading")
- if (window.location.hash != "") {
- this.appRoute(window.location.hash)
- } else {
- this.page = "home"
- }
-
- this.mediaKeyFixes()
-
- setTimeout(() => {
- this.getSocialBadges()
- this.getBrowsePage();
- this.$forceUpdate()
- }, 500)
- ipcRenderer.invoke("renderer-ready", true)
- document.querySelector("#LOADER").remove()
- if (this.cfg.general.themeUpdateNotification) {
- this.checkForThemeUpdates()
- }
- },
- async checkForThemeUpdates() {
- let self = this
- const themes = ipcRenderer.sendSync("get-themes")
- await asyncForEach(themes, async (theme) => {
- if (theme.commit != "") {
- await fetch(`https://api.github.com/repos/${theme.github_repo}/commits`)
- .then(res => res.json())
- .then(res => {
- if (res[0].sha != theme.commit) {
- const notify = notyf.open({ className: "notyf-info", type: "info", message: `[Themes] ${theme.name} has an update available.` })
- notify.on("click", () => {
- app.appRoute("themes-github")
- notyf.dismiss(notify)
- })
- }
- })
- }
- })
- },
- async setTheme(theme = "", onlyPrefs = false) {
- console.log(theme)
- if (this.cfg.visual.theme == "") {
- this.cfg.visual.theme = "default.less"
- }
- if (theme == "") {
- theme = this.cfg.visual.theme
- } else {
- this.cfg.visual.theme = ""
- this.cfg.visual.theme = theme
- }
- const info = {}
- try {
- const infoResponse = await fetch("themes/" + app.cfg.visual.theme.replace("index.less", "theme.json"))
- this.chrome.appliedTheme.info = await infoResponse.json()
- } catch (e) {
- e = null
- console.warn("failed to get theme.json")
- this.chrome.appliedTheme.info = {}
- }
-
-
- if (!onlyPrefs) {
- document.querySelector("#userTheme").href = `themes/${this.cfg.visual.theme}`
- document.querySelectorAll(`[id*='less']`).forEach(el => {
- el.remove()
- });
- less.refresh()
- }
- },
- getThemeDirective(directive = "") {
- let directives = {}
- if (typeof this.chrome.appliedTheme.info.directives == "object") {
- directives = this.chrome.appliedTheme.info.directives
- }
- if (directives[directive]) {
- return this.chrome.appliedTheme.info.directives[directive].value
- } else if (this.cfg.visual.directives[directive]) {
- return this.cfg.visual.directives.windowLayout
- } else {
- return ""
- }
- },
- unauthorize() {
- bootbox.confirm(app.getLz('term.confirmLogout'), function (result) {
- if (result) {
- app.mk.unauthorize()
- document.location.reload()
- }
- });
- },
- getAppClasses() {
- let classes = {}
- if (this.cfg.advanced.experiments.includes('compactui')) {
- classes.compact = true
- }
- if (this.cfg.visual.window_background_style == "none") {
- classes.simplebg = true
- }
-
- if (this.getThemeDirective('windowLayout') == 'twopanel') {
- classes.twopanel = true
- }
- return classes
- },
- invokeDrawer(panel) {
- if (this.drawer.panel == panel && this.drawer.open) {
- if (panel == "lyrics") {
- this.lyricon = false
- }
- this.drawer.panel = ""
- this.drawer.open = false
- } else {
- if (panel == "lyrics") {
- this.lyricon = true
- } else {
- this.lyricon = false
- }
- this.drawer.open = true
- this.drawer.panel = panel
- }
- },
- select_removeMediaItem(id) {
- this.selectedMediaItems.filter(item => item.guid == id).forEach(item => {
- this.selectedMediaItems.splice(this.selectedMediaItems.indexOf(item), 1)
- })
- },
- select_hasMediaItem(id) {
- let found = this.selectedMediaItems.find(item => item.guid == id)
- if (found) {
- return true
- } else {
- return false
- }
- },
- select_selectMediaItem(id, kind, index, guid, library) {
- if (!this.select_hasMediaItem(guid)) {
- this.selectedMediaItems.push({
- id: id,
- kind: kind,
- index: index,
- guid: guid,
- isLibrary: library
- })
- }
- },
- getPlaylistFolderChildren(id) {
- return this.playlists.listing.filter(playlist => {
- if (playlist.parent == id) {
- return playlist
- }
- })
- },
- async refreshPlaylists(localOnly = false, trackMap = true) {
- let self = this
- let newListing = []
- let trackMapping = {}
- const cachedPlaylist = await CiderCache.getCache("library-playlists")
- const cachedTrackMapping = await CiderCache.getCache("library-playlists-tracks")
-
- if (cachedPlaylist) {
- console.log("using cached playlists")
- this.playlists.listing = cachedPlaylist
- self.sortPlaylists()
- } else {
- console.log("playlist has no cache")
- }
-
- if(cachedTrackMapping) {
- console.log("using cached track mapping")
- this.playlists.trackMapping = cachedTrackMapping
- }
- if (localOnly) {
- return
- }
-
- this.library.backgroundNotification.message = "Building playlist cache..."
- this.library.backgroundNotification.show = true
-
- async function deepScan(parent = "p.playlistsroot") {
- console.log(`scanning ${parent}`)
- const playlistData = await app.mk.api.v3.music(`/v1/me/library/playlist-folders/${parent}/children/`)
- await asyncForEach(playlistData.data.data, async (playlist) => {
- playlist.parent = parent
- playlist.children = []
- playlist.tracks = []
- try {
- if (trackMap) {
- let tracks = await app.mk.api.v3.music(playlist.href + "/tracks").catch(e => {
- // no tracks
- e = null
- })
- tracks.data.data.forEach(track => {
- if (!trackMapping[track.id]) {
- trackMapping[track.id] = []
- }
- trackMapping[track.id].push(playlist.id)
-
- if (typeof track.attributes.playParams.catalogId == "string") {
- if (!trackMapping[track.attributes.playParams.catalogId]) {
- trackMapping[track.attributes.playParams.catalogId] = []
- }
- trackMapping[track.attributes.playParams.catalogId].push(playlist.id)
- }
- })
- }
- } catch (e) { }
- if (playlist.type == "library-playlist-folders") {
- try {
- await deepScan(playlist.id).catch(e => { })
- } catch (e) {
-
- }
- }
- newListing.push(playlist)
- })
- }
-
- await deepScan()
-
- this.library.backgroundNotification.show = false
- this.playlists.listing = newListing
- self.sortPlaylists()
- if (trackMap) {
- CiderCache.putCache("library-playlists-tracks", trackMapping)
- this.playlists.trackMapping = trackMapping
- }
- CiderCache.putCache("library-playlists", newListing)
- },
- sortPlaylists() {
- this.playlists.listing.sort((a, b) => {
- if (a.type === "library-playlist-folders" && b.type !== "library-playlist-folders") {
- return -1
- } else if (a.type !== "library-playlist-folders" && b.type === "library-playlist-folders") {
- return 1
- } else {
- return 0
- }
- })
- },
- playlistHeaderContextMenu(event) {
- let menu = {
- items: [{
- name: app.getLz('term.createNewPlaylist'),
- action: () => {
- this.newPlaylist()
- }
- },
- {
- name: app.getLz('term.createNewPlaylistFolder'),
- action: () => {
- this.newPlaylistFolder()
- }
- }
- ]
- }
- this.showMenuPanel(menu, event)
- },
- async editPlaylistFolder(id, name = app.getLz('term.newPlaylist')) {
- let self = this
- this.mk.api.v3.music(
- `/v1/me/library/playlist-folders/${id}`, {}, {
- fetchOptions: {
- method: "PATCH",
- body: JSON.stringify({
- attributes: { name: name }
- })
- }
- }
- ).then(res => {
- self.refreshPlaylists()
- })
- },
- async editPlaylist(id, name = app.getLz('term.newPlaylist')) {
- let self = this
- this.mk.api.v3.music(
- `/v1/me/library/playlists/${id}`, {}, {
- fetchOptions: {
- method: "PATCH",
- body: JSON.stringify({
- attributes: { name: name }
- })
- }
- }
- ).then(res => {
- self.refreshPlaylists()
- })
- },
- copyToClipboard(str) {
- if (navigator.userAgent.includes('Darwin') || navigator.appVersion.indexOf("Mac") != -1) {
- this.darwinShare(str)
- } else {
- notyf.success(app.getLz('term.share.success'))
- navigator.clipboard.writeText(str).then(r => console.log("Copied to clipboard."))
- }
- },
- newPlaylist(name = app.getLz('term.newPlaylist'), tracks = []) {
- let self = this
- let request = {
- name: name
- }
- if (tracks.length > 0) {
- request.tracks = tracks
- }
- app.mk.api.v3.music(`/v1/me/library/playlists`, {}, {
- fetchOptions: {
- method: "POST",
- body: JSON.stringify({
- "attributes": { "name": name },
- "relationships": {
- "tracks": { "data": tracks },
- }
- })
- }
- }).then(res => {
- res = res.data.data[0]
- console.log(res)
- self.appRoute(`playlist_` + res.id);
- self.showingPlaylist = [];
- self.getPlaylistFromID(app.page.substring(9), true)
- self.playlists.listing.push({
- id: res.id,
- attributes: {
- name: name
- },
- parent: "p.playlistsroot"
- })
- self.sortPlaylists()
- setTimeout(() => {
- app.refreshPlaylists()
- }, 8000)
- })
- },
- deletePlaylist(id) {
- let self = this
- if (confirm(app.getLz('term.deletePlaylist'))) {
- app.mk.api.v3.music(`/v1/me/library/playlists/${id}`, {}, {
- fetchOptions: {
- method: "DELETE"
- }
- }).then(res => {
- // remove this playlist from playlists.listing if it exists
- let found = self.playlists.listing.find(item => item.id == id)
- if (found) {
- self.playlists.listing.splice(self.playlists.listing.indexOf(found), 1)
- }
- })
- }
- },
- async showCollection(response, title, type, requestBody = {}) {
- let self = this
- console.log(response)
- this.collectionList.requestBody = {}
- this.collectionList.response = response
- this.collectionList.title = title
- this.collectionList.type = type
- this.collectionList.requestBody = requestBody
- app.appRoute("collection-list")
- },
- async showArtistView(artist, title, view) {
- let response = (await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists/${artist}/view/${view}?l=${this.mklang}`, {}, { includeResponseMeta: !0 })).data
- console.log(response)
- await this.showCollection(response, title, "artists")
- },
- async showRecordLabelView(label, title, view) {
- let response = (await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/record-labels/${label}/view/${view}?l=${this.mklang}`)).data
- await this.showCollection(response, title, "record-labels")
- },
- async showSearchView(term, group, title) {
-
- let requestBody = {
- platform: "web",
- groups: group,
- types: "activities,albums,apple-curators,artists,curators,editorial-items,music-movies,music-videos,playlists,songs,stations,tv-episodes,uploaded-videos,record-labels",
- limit: 25,
- relate: {
- editorialItems: ["contents"]
- },
- include: {
- albums: ["artists"],
- songs: ["artists"],
- "music-videos": ["artists"]
- },
- extend: "artistUrl",
- fields: {
- artists: "url,name,artwork,hero",
- albums: "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url"
- },
- with: "serverBubbles,lyricHighlights",
- art: {
- "url": "cf"
- },
- omit: {
- resource: ["autos"]
- },
- groups: group,
- l: this.mklang
- }
- let response = await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/search?term=${term}`, requestBody, {
- includeResponseMeta: !0
- })
-
- console.log('searchres', response)
- let responseFormat = {
- data: response.data.results[group].data,
- next: response.data.results[group].next,
- groups: group
- }
- await this.showCollection(responseFormat, title, "search", requestBody)
- },
- async getPlaylistContinuous(response, transient = false) {
- response = response.data.data[0]
- let self = this
- let playlistId = response.id
- this.playlists.loadingState = (!transient) ? 0 : 1
- this.showingPlaylist = response
- if (!response.relationships?.tracks?.next) {
- this.playlists.loadingState = 1
- return
- }
-
- function getPlaylistTracks(next) {
- app.apiCall(app.musicBaseUrl + next, res => {
- if (self.showingPlaylist.id != playlistId) {
- return
- }
- self.showingPlaylist.relationships.tracks.data = self.showingPlaylist.relationships.tracks.data.concat(res.data)
- if (res.next) {
- getPlaylistTracks(res.next)
- } else {
- self.playlists.loadingState = 1
- }
- })
- }
-
- getPlaylistTracks(response.relationships.tracks.next)
-
- },
- async getPlaylistFromID(id, transient = false) {
- let self = this
- const params = {
- include: "tracks",
- platform: "web",
- "include[library-playlists]": "catalog,tracks",
- "fields[playlists]": "curatorName,playlistType,name,artwork,url,playParams",
- "include[library-songs]": "catalog,artists,albums,playParams,name,artwork,url",
- "fields[catalog]": "artistUrl,albumUrl,url",
- "fields[songs]": "artistUrl,albumUrl,playParams,name,artwork,url,artistName,albumName,durationInMillis",
- l: this.mklang
- }
- if (!transient) {
- this.playlists.loadingState = 0;
- }
- app.mk.api.v3.music(`/v1/me/library/playlists/${id}`, params).then(res => {
- self.getPlaylistContinuous(res, transient)
- }).catch((e) => {
- console.log(e);
- try {
- app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/playlists/${id}`, params).then(res => {
- self.getPlaylistContinuous(res, transient)
- })
- } catch (err) {
- console.log(err)
- }
- })
-
- },
- async getArtistFromID(id) {
- this.page = ""
- const artistData = await this.mkapi("artists", false, id, {
- "views": "featured-release,full-albums,appears-on-albums,featured-albums,featured-on-albums,singles,compilation-albums,live-albums,latest-release,top-music-videos,similar-artists,top-songs,playlists,more-to-hear,more-to-see",
- "extend": "artistBio,bornOrFormed,editorialArtwork,editorialVideo,isGroup,origin,hero",
- "extend[playlists]": "trackCount",
- "include[songs]": "albums",
- "fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url,trackCount",
- "limit[artists:top-songs]": 20,
- "art[url]": "f",
- l: this.mklang
- }, { includeResponseMeta: !0 })
- console.log(artistData.data.data[0])
- this.artistPage.data = artistData.data.data[0]
- this.page = "artist-page"
- },
- progressBarStyle() {
- let val = this.playerLCD.playbackDuration
- if (this.playerLCD.desiredDuration > 0) {
- val = this.playerLCD.desiredDuration
- }
- let min = 0
- let max = this.mk.currentPlaybackDuration
- let value = (val - min) / (max - min) * 100
- return {
- 'background': ('linear-gradient(to right, var(--songProgressColor) 0%, var(--songProgressColor) ' + value + '%, var(--songProgressBackground) ' + value + '%, var(--songProgressBackground) 100%)')
- }
- },
- async getRecursive(response) {
- // if response has a .next() property run it and keep running until .next is null or undefined
- // and then return the response concatenated with the results of the next() call
- function executeRequest() {
- if (response.next) {
- return response.next().then(executeRequest)
- } else {
- return response
- }
- }
-
- return executeRequest()
- },
- async getRecursive2(response, sendTo) {
- let returnData = {
- "data": [],
- "meta": {}
- }
- if (response.next) {
- console.log("has next")
- returnData.data.concat(response.data)
- returnData.meta = response.meta
- return await this.getRecursive(await response.next())
- } else {
- console.log("no next")
- returnData.data.concat(response.data)
- return returnData
- }
- },
- async getSearchHints() {
- if (this.search.term == "") {
- this.search.hints = []
- return
- }
- let hints = await (await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/search/hints?term=${this.search.term}`)).data.results
- this.search.hints = hints ? hints.terms : []
- },
- getSongProgress() {
- if (this.playerLCD.userInteraction) {
- return this.playerLCD.desiredDuration
- } else {
- return this.playerLCD.playbackDuration
- }
- },
- /**
- * Converts seconds to dd:hh:mm:ss
- * @param time (in seconds)
- * @param format (short, long)
- * @returns {string}
- */
- convertTime(time, format = 'short') {
- if (typeof time !== "number") {
- time = parseInt(time)
- }
-
- const timeGates = {
- 600: 15, // 10 Minutes
- 3600: 14, // Hour
- 36000: 12, // 10 Hours
- }
-
- const datetime = new Date(time * 1000)
-
- let returnTime = datetime.toISOString().substring(11, 19);
- for (let key in timeGates) {
- if (time < key) {
- returnTime = datetime.toISOString().substring(timeGates[key], 19)
- break
- }
- }
-
- // Add the days on the front
- let day;
- if (time >= 86400) {
- day = datetime.toISOString().substring(8, 10)
- day = parseInt(day) - 1
- returnTime = day + ":" + returnTime
- }
-
- if (format === 'long') {
- const longFormat = []
-
- // Seconds
- if (datetime.getSeconds() !== 0) {
- longFormat.push(`${datetime.getSeconds()} ${app.getLz('term.time.seconds')}`)
- }
-
- // Minutes
- if (time >= 60) {
- longFormat.push(`${datetime.getMinutes()} ${app.getLz('term.time.minute', options = { count: datetime.getMinutes() })}`)
- }
-
- // Hours
- if (time >= 3600) {
- longFormat.push(`${datetime.getHours()} ${app.getLz('term.time.hour', options = { count: datetime.getHours() })}`)
- }
-
- // Days
- if (time >= 86400) {
- longFormat.push(`${day} ${app.getLz('term.time.day', options = { count: day })}`)
- }
- returnTime = longFormat.reverse().join(', ')
- }
-
- return returnTime
- },
- hashCode(str) {
- let hash = 0,
- i, chr;
- if (str.length === 0) return hash;
- for (i = 0; i < str.length; i++) {
- chr = str.charCodeAt(i);
- hash = ((hash << 5) - hash) + chr;
- hash |= 0; // Convert to 32bit integer
- }
- return hash;
- },
- appRoute(route) {
- console.log(route)
- if (route == "" || route == "#" || route == "/") {
- return;
- }
- route = route.replace(/#/g, "")
- // if the route contains does not include a / then route to the page directly
- if (route.indexOf("/") == -1) {
- this.page = route
- window.location.hash = this.page
- // if (this.page == "settings") {
- // this.version
- // }
- return
- }
- let hash = route.split("/")
- let page = hash[0]
- let id = hash[1]
- let isLibrary = hash[2] ?? false
- console.log(`page: ${page} id: ${id} isLibrary: ${isLibrary}`)
- this.routeView({
- kind: page,
- id: id,
- attributes: {
- playParams: { kind: page, id: id, isLibrary: isLibrary }
- }
- })
- },
- routeView(item) {
- let kind = (item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')) : (item.type ?? ''));
- let id = (item.attributes.playParams ? (item.attributes.playParams.id ?? (item.id ?? '')) : (item.id ?? ''));
- ;
- let isLibrary = item.attributes.playParams ? (item.attributes.playParams.isLibrary ?? false) : false;
- console.log(kind, id, isLibrary)
- if (kind.includes("playlist") || kind.includes("album")) {
- app.showingPlaylist = [];
- }
- if (kind.toString().includes("apple-curator")) {
- kind = "appleCurator"
- app.getTypeFromID("appleCurator", (id), false, {
- platform: "web",
- include: "grouping,playlists",
- extend: "editorialArtwork",
- "art[url]": "f"
- });
- window.location.hash = `${kind}/${id}`
- document.querySelector("#app-content").scrollTop = 0
- } else if (kind.toString().includes("artist")) {
- app.getArtistInfo(id, isLibrary)
- window.location.hash = `${kind}/${id}${isLibrary ? "/" + isLibrary : ''}`
- document.querySelector("#app-content").scrollTop = 0
-
- } else if (kind.toString().includes("record-label") || kind.toString().includes("curator")) {
- if (kind.toString().includes("record-label")) {
- kind = "recordLabel"
- } else {
- kind = "curator"
- }
- app.page = (kind) + "_" + (id);
- app.getTypeFromID((kind), (id), (isLibrary), {
- extend: "editorialVideo",
- include: 'grouping,playlists',
- views: 'top-releases,latest-releases,top-artists'
- });
- window.location.hash = `${kind}/${id}`
- document.querySelector("#app-content").scrollTop = 0
- } else if (!kind.toString().includes("radioStation") && !kind.toString().includes("song") && !kind.toString().includes("musicVideo") && !kind.toString().includes("uploadedVideo") && !kind.toString().includes("music-movie")) {
- let params = {
- extend: "offers,editorialVideo",
- "views": "appears-on,more-by-artist,related-videos,other-versions,you-might-also-like,video-extras,audio-extras",
- }
- if (kind.includes("playlist")) {
- params["include"] = "tracks";
- }
- if (kind.includes("album")) {
- params["include[albums]"] = "artists"
- params["fields[artists]"] = "name,url"
- params["fields[albums]"] = "artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url,copyright"
- }
-
- if (this.cfg.advanced.experiments.includes('inline-playlists')) {
- let showModal = kind.toString().includes("album") || kind.toString().includes("playlist")
- if (app.page.includes("playlist") || app.page.includes("album")) {
- showModal = false
- }
- if (showModal) {
- app.modals.showPlaylist = true
- app.chrome.contentAreaScrolling = false
- } else {
- app.page = (kind) + "_" + (id);
- window.location.hash = `${kind}/${id}${isLibrary ? "/" + isLibrary : ''}`
- }
- } else {
- app.page = (kind) + "_" + (id);
- window.location.hash = `${kind}/${id}${isLibrary ? "/" + isLibrary : ''}`
- }
-
-
- app.getTypeFromID((kind), (id), (isLibrary), params);
- // document.querySelector("#app-content").scrollTop = 0
- } else {
- app.playMediaItemById((id), (kind), (isLibrary), item.attributes.url ?? '')
- }
- },
- prevButton() {
- if (!app.prevButtonBackIndicator && app.mk.nowPlayingItem && app.mk.currentPlaybackTime > 2) {
- app.prevButtonBackIndicator = true;
- try {
- clearTimeout(app.pauseButtonTimer)
- } catch (e) {
- }
- app.mk.seekToTime(0);
- app.pauseButtonTimer = setTimeout(() => {
- app.prevButtonBackIndicator = false
- }, 3000);
- } else {
- try {
- clearTimeout(app.pauseButtonTimer)
- } catch (e) {
- }
- app.prevButtonBackIndicator = false;
- app.skipToPreviousItem()
- }
- },
- async getNowPlayingItemDetailed(target) {
- try {
- let u = await app.mkapi(app.mk.nowPlayingItem.playParams.kind,
- (app.mk.nowPlayingItem.songId == -1),
- (app.mk.nowPlayingItem.songId != -1) ? app.mk.nowPlayingItem.songId : app.mk.nowPlayingItem["id"],
- { "include[songs]": "albums,artists", l: app.mklang });
- app.searchAndNavigate(u.data.data[0], target)
- } catch (e) {
- app.searchAndNavigate(app.mk.nowPlayingItem, target)
- }
- },
- async searchAndNavigate(item, target) {
- let self = this
- app.tmpVar = item;
- switch (target) {
- case "artist":
- let artistId = '';
- try {
- if (item.relationships.artists && item.relationships.artists.data.length > 0 && !item.relationships.artists.data[0].type.includes("library")) {
- if (item.relationships.artists.data[0].type === "artist" || item.relationships.artists.data[0].type === "artists") {
- artistId = item.relationships.artists.data[0].id
- }
- }
- if (artistId == '') {
- const url = (item.relationships.catalog.data[0].attributes.artistUrl);
- artistId = (url).substring(url.lastIndexOf('/') + 1)
- if (artistId.includes('viewCollaboration')) {
- artistId = artistId.substring(artistId.lastIndexOf('ids=') + 4, artistId.lastIndexOf('-'))
- }
- }
- } catch (_) {
- }
-
- if (artistId == "") {
- let artistQuery = (await app.mk.api.v3.music(`v1/catalog/${app.mk.storefrontId}/search?term=${item.attributes.artistName}`, {
- limit: 1,
- types: 'artists'
- })).data.results;
- try {
- if (artistQuery.artists.data.length > 0) {
- artistId = artistQuery.artists.data[0].id;
- console.log(artistId)
- }
- } catch (e) {
- }
- }
- console.log(artistId);
- if (artistId != "")
- self.appRoute(`artist/${artistId}`)
- break;
- case "album":
- let albumId = '';
- try {
- if ((item.type ?? item.playParams?.kind ?? "") == "albums") {
- albumId = item.id ?? ""
- } else if (item.relationships.albums && item.relationships.albums.data.length > 0 && !item.relationships.albums.data[0].type.includes("library")) {
- if (item.relationships.albums.data[0].type === "album" || item.relationships.albums.data[0].type === "albums") {
- albumId = item.relationships.albums.data[0].id
- }
- }
- if (albumId == '') {
- const url = (item.relationships.catalog.data[0].attributes.url);
- albumId = (url).substring(url.lastIndexOf('/') + 1)
- if (albumId.includes("?i=")) {
- albumId = albumId.substring(0, albumId.indexOf("?i="))
- }
- }
- } catch (_) {
- }
-
- if (albumId == "") {
- try {
- let albumQuery = (await app.mk.api.v3.music(`v1/catalog/${app.mk.storefrontId}/search?term=${(item.attributes.albumName ?? item.attributes.name ?? "") + " " + (item.attributes.artistName ?? "")}`, {
- limit: 1,
- types: 'albums'
- })).data.results;
- if (albumQuery.albums.data.length > 0) {
- albumId = albumQuery.albums.data[0].id;
- console.log(albumId)
- }
- } catch (e) {
- }
- }
- if (albumId != "") {
- self.appRoute(`album/${albumId}`)
- }
- break;
- case "recordLabel":
- let labelId = '';
- try {
- labelId = item.relationships['record-labels'].data[0].id
- } catch (_) {
- }
-
- if (labelId == "") {
- try {
- let labelQuery = (await app.mk.api.v3.music(`v1/catalog/${app.mk.storefrontId}/search?term=${item.attributes.recordLabel}`, {
- limit: 1,
- types: 'record-labels'
- })).data.results;
- if (labelQuery["record-labels"].data.length > 0) {
- labelId = labelQuery["record-labels"].data[0].id;
- console.log(labelId)
- }
- } catch (e) {
- }
- }
- if (labelId != "") {
- app.showingPlaylist = []
- await app.getTypeFromID("recordLabel", labelId, false, { views: 'top-releases,latest-releases,top-artists' });
- app.page = "recordLabel_" + labelId;
- }
-
- break;
- }
- },
- exitMV() {
- MusicKit.getInstance().stop()
- document.getElementById("apple-music-video-container").style.display = "none";
- },
- getArtistInfo(id, isLibrary) {
- this.getArtistFromID(id)
- //this.getTypeFromID("artist",id,isLibrary,query)
- },
- followArtistById(id, follow) {
- if (follow && !this.followingArtist(id)) {
- this.cfg.home.followedArtists.push(id)
- } else {
- let index = this.cfg.home.followedArtists.indexOf(id)
- if (index > -1) {
- this.cfg.home.followedArtists.splice(index, 1)
- }
- }
- },
- followingArtist(id) {
- console.log(`check for ${id}`)
- return this.cfg.home.followedArtists.includes(id)
- },
- playMediaItem(item) {
- let kind = (item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')) : (item.type ?? ''));
- let id = (item.attributes.playParams ? (item.attributes.playParams.id ?? (item.id ?? '')) : (item.id ?? ''));
- ;
- let isLibrary = item.attributes.playParams ? (item.attributes.playParams.isLibrary ?? false) : false;
- let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
- console.log(kind, id, isLibrary)
- app.mk.stop().then(() => {
- if (kind.includes("artist")) {
- app.mk.setStationQueue({ artist: 'a-' + id }).then(() => {
- app.mk.play()
- })
- }
- // else if (kind.includes("playlist") && (id.startsWith("p.") || id.startsWith("pl."))){
- // /* Randomize array in-place using Durstenfeld shuffle algorithm */
- // function shuffleArray(array) {
- // for (var i = array.length - 1; i > 0; i--) {
- // var j = Math.floor(Math.random() * (i + 1));
- // var temp = array[i];
- // array[i] = array[j];
- // array[j] = temp;
- // }
- // }
- // app.mk.clearQueue().then(function () { {
- // app.mk.setQueue({[truekind]: [item.attributes.playParams.id ?? item.id]}).then(function () {
- // app.mk.play().then(function (){
- // app.mk.clearQueue().then(function (){
- // var playlistId = id
- // const params = {
- // include: "tracks",
- // platform: "web",
- // "include[library-playlists]": "catalog,tracks",
- // "fields[playlists]": "curatorName,playlistType,name,artwork,url",
- // "include[library-songs]": "catalog,artists,albums",
- // "fields[catalog]": "artistUrl,albumUrl",
- // "fields[songs]": "artistUrl,albumUrl"
- // }
- // var playlistId = ''
-
- // try {
- // function getPlaylist(id, params, isLibrary){
- // if (isLibrary){
- // return app.mk.api.library.playlist(id, params)
- // } else { return app.mk.api.playlist(id, params)}
- // }
- // getPlaylist(id, params, isLibrary).then(res => {
- // let query = res.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
- // if (app.mk.shuffleMode == 1){shuffleArray(query); console.log('shf')}
- // app.mk.queue.append(query)
- // if (!res.relationships.tracks.next) {
- // return
- // } else {
- // getPlaylistTracks(res.relationships.tracks.next)
- // }
-
- // function getPlaylistTracks(next) {
- // app.apiCall(app.musicBaseUrl + next, res => {
- // if (res.id != playlistId) {
- // return
- // }
- // let query = res.data.map(item => new MusicKit.MediaItem(item))
- // if (app.mk.shuffleMode == 1){shuffleArray(query); console.log('shf')}
- // app.mk.queue.append(query)
-
- // if (res.next) {
- // getPlaylistTracks(res.next)
- // }
- // })
- // }
- // })
- // } catch (e) {}
-
-
- // })
- // })
- // })
- // }
- // })
- // }
- else {
- app.playMediaItemById((id), (kind), (isLibrary), item.attributes.url ?? '')
- }
- })
- },
- async getTypeFromID(kind, id, isLibrary = false, params = {}, params2 = {}) {
- let a;
- if (kind == "album" | kind == "albums") {
- params["include"] = "tracks,artists,record-labels,catalog";
- }
- params['l'] = this.mklang;
- try {
- a = await this.mkapi(kind.toString(), isLibrary, id.toString(), params, params2);
- } catch (e) {
- console.log(e);
- try {
- a = await this.mkapi(kind.toString(), !isLibrary, id.toString(), params, params2);
- } catch (err) {
- console.log(err);
- a = []
- } finally {
- if (kind == "appleCurator") {
- app.appleCurator = a.data.data[0]
- } else {
- this.getPlaylistContinuous(a, true)
- }
- }
- } finally {
- if (kind == "appleCurator") {
- app.appleCurator = a.data.data[0]
- } else {
- this.getPlaylistContinuous(a, true)
- }
- }
- ;
- },
- searchLibrarySongs() {
- let self = this
- let prefs = this.cfg.libraryPrefs.songs
- let albumAdded = self.library?.albums?.listing?.map(function (i) {
- return { [i.id]: i.attributes?.dateAdded }
- })
- let startTime = new Date().getTime()
-
- function sortSongs() {
- // sort this.library.songs.displayListing by song.attributes[self.library.songs.sorting] in descending or ascending order based on alphabetical order and numeric order
- // check if song.attributes[self.library.songs.sorting] is a number and if so, sort by number if not, sort by alphabetical order ignoring case
- self.library.songs.displayListing.sort((a, b) => {
- let aa = a.attributes[prefs.sort]
- let bb = b.attributes[prefs.sort]
- if (prefs.sort == "genre") {
- aa = a.attributes.genreNames[0]
- bb = b.attributes.genreNames[0]
- }
- if (prefs.sort == "dateAdded") {
- let albumida = a.relationships?.albums?.data[0]?.id ?? '1970-01-01T00:01:01Z'
- let albumidb = b.relationships?.albums?.data[0]?.id ?? '1970-01-01T00:01:01Z'
- aa = startTime - new Date(((albumAdded.find(i => i[albumida])) ?? [])[albumida] ?? '1970-01-01T00:01:01Z').getTime()
- bb = startTime - new Date(((albumAdded.find(i => i[albumidb])) ?? [])[albumidb] ?? '1970-01-01T00:01:01Z').getTime()
- }
- if (aa == null) {
- aa = ""
- }
- if (bb == null) {
- bb = ""
- }
- if (prefs.sortOrder == "asc") {
- if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) {
- return aa - bb
- } else {
- return aa.toString().toLowerCase().localeCompare(bb.toString().toLowerCase())
- }
- } else if (prefs.sortOrder == "desc") {
- if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) {
- return bb - aa
- } else {
- return bb.toString().toLowerCase().localeCompare(aa.toString().toLowerCase())
- }
- }
- })
- }
-
- if (this.library.songs.search == "") {
- this.library.songs.displayListing = this.library.songs.listing
- sortSongs()
- } else {
- this.library.songs.displayListing = this.library.songs.listing.filter(item => {
- let itemName = item.attributes.name.toLowerCase()
- let searchTerm = this.library.songs.search.toLowerCase()
- let artistName = ""
- let albumName = ""
- if (item.attributes.artistName != null) {
- artistName = item.attributes.artistName.toLowerCase()
- }
- if (item.attributes.albumName != null) {
- albumName = item.attributes.albumName.toLowerCase()
- }
-
- // remove any non-alphanumeric characters and spaces from search term and item name
- searchTerm = searchTerm.replace(/[^a-z0-9 ]/gi, "")
- itemName = itemName.replace(/[^a-z0-9 ]/gi, "")
- artistName = artistName.replace(/[^a-z0-9 ]/gi, "")
- albumName = albumName.replace(/[^a-z0-9 ]/gi, "")
-
- if (itemName.includes(searchTerm) || artistName.includes(searchTerm) || albumName.includes(searchTerm)) {
- return item
- }
- })
- sortSongs()
- }
- },
- getAlbumSort() {
- this.library.albums.sortOrder[1] = this.cfg.libraryPrefs.albums.sortOrder;
- this.library.albums.sorting[1] = this.cfg.libraryPrefs.albums.sort;
- },
- // make a copy of searchLibrarySongs except use Albums instead of Songs
- searchLibraryAlbums(index) {
- let self = this
-
- function sortAlbums() {
- // sort this.library.albums.displayListing by album.attributes[self.library.albums.sorting[index]] in descending or ascending order based on alphabetical order and numeric order
- // check if album.attributes[self.library.albums.sorting[index]] is a number and if so, sort by number if not, sort by alphabetical order ignoring case
- self.library.albums.displayListing.sort((a, b) => {
- let aa = a.attributes[self.library.albums.sorting[index]]
- let bb = b.attributes[self.library.albums.sorting[index]]
- if (self.library.albums.sorting[index] == "genre") {
- aa = a.attributes.genreNames[0]
- bb = b.attributes.genreNames[0]
- }
- if (aa == null) {
- aa = ""
- }
- if (bb == null) {
- bb = ""
- }
- if (self.library.albums.sortOrder[index] == "asc") {
- if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) {
- return aa - bb
- } else {
- return aa.toString().toLowerCase().localeCompare(bb.toString().toLowerCase())
- }
- } else if (self.library.albums.sortOrder[index] == "desc") {
- if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) {
- return bb - aa
- } else {
- return bb.toString().toLowerCase().localeCompare(aa.toString().toLowerCase())
- }
- }
- })
- }
-
- if (this.library.albums.search == "") {
- this.library.albums.displayListing = this.library.albums.listing
- sortAlbums()
- } else {
- this.library.albums.displayListing = this.library.albums.listing.filter(item => {
- let itemName = item.attributes.name.toLowerCase()
- let searchTerm = this.library.albums.search.toLowerCase()
- let artistName = ""
- let albumName = ""
- if (item.attributes.artistName != null) {
- artistName = item.attributes.artistName.toLowerCase()
- }
- if (item.attributes.albumName != null) {
- albumName = item.attributes.albumName.toLowerCase()
- }
-
- // remove any non-alphanumeric characters and spaces from search term and item name
- searchTerm = searchTerm.replace(/[^a-z0-9 ]/gi, "")
- itemName = itemName.replace(/[^a-z0-9 ]/gi, "")
- artistName = artistName.replace(/[^a-z0-9 ]/gi, "")
- albumName = albumName.replace(/[^a-z0-9 ]/gi, "")
-
- if (itemName.includes(searchTerm) || artistName.includes(searchTerm) || albumName.includes(searchTerm)) {
- return item
- }
- })
- sortAlbums()
- }
- },
- // make a copy of searchLibrarySongs except use Albums instead of Songs
- searchLibraryArtists(index) {
- let self = this
-
- function sortArtists() {
- // sort this.library.albums.displayListing by album.attributes[self.library.albums.sorting[index]] in descending or ascending order based on alphabetical order and numeric order
- // check if album.attributes[self.library.albums.sorting[index]] is a number and if so, sort by number if not, sort by alphabetical order ignoring case
- self.library.artists.displayListing.sort((a, b) => {
- let aa = a.attributes[self.library.artists.sorting[index]]
- let bb = b.attributes[self.library.artists.sorting[index]]
- if (self.library.artists.sorting[index] == "genre") {
- aa = a.attributes.genreNames[0]
- bb = b.attributes.genreNames[0]
- }
- if (aa == null) {
- aa = ""
- }
- if (bb == null) {
- bb = ""
- }
- if (self.library.artists.sortOrder[index] == "asc") {
- if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) {
- return aa - bb
- } else {
- return aa.toString().toLowerCase().localeCompare(bb.toString().toLowerCase())
- }
- } else if (self.library.artists.sortOrder[index] == "desc") {
- if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) {
- return bb - aa
- } else {
- return bb.toString().toLowerCase().localeCompare(aa.toString().toLowerCase())
- }
- }
- })
- }
-
- if (this.library.artists.search == "") {
- this.library.artists.displayListing = this.library.artists.listing
- sortArtists()
- } else {
- this.library.artists.displayListing = this.library.artists.listing.filter(item => {
- let itemName = item.attributes.name.toLowerCase()
- let searchTerm = this.library.artists.search.toLowerCase()
- let artistName = ""
- let albumName = ""
- // if (item.attributes.artistName != null) {
- // artistName = item.attributes.artistName.toLowerCase()
- // }
- // if (item.attributes.albumName != null) {
- // albumName = item.attributes.albumName.toLowerCase()
- // }
-
- // remove any non-alphanumeric characters and spaces from search term and item name
- searchTerm = searchTerm.replace(/[^a-z0-9 ]/gi, "")
- itemName = itemName.replace(/[^a-z0-9 ]/gi, "")
-
-
- if (itemName.includes(searchTerm) || artistName.includes(searchTerm) || albumName.includes(searchTerm)) {
- return item
- }
- })
- sortArtists()
- }
- },
- getSidebarItemClass(page) {
- if (this.page == page) {
- return ["active"]
- } else {
- return []
- }
- },
- async mkapi(method, library = false, term, params = {}, params2 = {}, attempts = 0) {
- if (method.includes(`recordLabel`)) {
- method = `record-labels`
- }
- if (method.includes(`appleCurator`)) {
- method = `apple-curators`
- }
- if (attempts > 3) {
- return
- }
- let truemethod = (!method.endsWith("s")) ? (method + "s") : method;
- try {
- if (library) {
- return await this.mk.api.v3.music(`v1/me/library/${truemethod}/${term.toString()}`, params, params2)
- } else {
- return await this.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/${truemethod}/${term.toString()}`, params, params2)
- }
- } catch (e) {
- console.log(e)
- return await this.mkapi(method, library, term, params, params2, attempts + 1)
- }
- },
- getLibraryGenres() {
- let genres = []
- genres = []
- this.library.songs.listing.forEach((item) => {
- item.attributes.genreNames.forEach((genre) => {
- if (!genres.includes(genre)) {
- genres.push(genre)
- }
- })
- })
- return genres
- },
- async getLibrarySongsFull(force = false) {
- let self = this
- let library = []
- let cacheId = "library-songs"
- let downloaded = null;
- if ((this.library.songs.downloadState == 2) && !force) {
- return
- }
- if (this.library.songs.downloadState == 1) {
- return
- }
- let librarySongs = await CiderCache.getCache(cacheId)
- if (librarySongs) {
- this.library.songs.listing = librarySongs
- this.searchLibrarySongs()
- }
- if (this.songstest) {
- return
- }
- this.library.songs.downloadState = 1
- this.library.backgroundNotification.show = true
- this.library.backgroundNotification.message = app.getLz('notification.updatingLibrarySongs')
-
- function downloadChunk() {
- const params = {
- "include[library-songs]": "catalog,artists,albums",
- "fields[artists]": "name,url,id",
- "fields[albums]": "name,url,id",
- platform: "web",
- "fields[catalog]": "artistUrl,albumUrl",
- "fields[songs]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url",
- limit: 100,
- l: self.mklang
- }
- const safeparams = {
- "platform": "web",
- "limit": 80
- }
- self.library.songs.downloadState = 1
- if (downloaded == null) {
- app.mk.api.v3.music(`/v1/me/library/songs/`, params).then((response) => {
- processChunk(response.data)
- }).catch((error) => {
- console.log('safe loading');
- app.mk.api.v3.music(`/v1/me/library/songs/`, safeparams).then((response) => {
- processChunk(response.data)
- }).catch((error) => {
- console.log('safe loading failed', error)
- app.library.songs.downloadState = 2
- app.library.backgroundNotification.show = false
- })
- })
- } else {
- if (downloaded.next != null) {
- app.mk.api.v3.music(downloaded.next, params).then((response) => {
- processChunk(response.data)
- }).catch((error) => {
- console.log('safe loading');
- app.mk.api.v3.music(downloaded.next, safeparams).then((response) => {
- processChunk(response.data)
- }).catch((error) => {
- console.log('safe loading failed', error)
- app.library.songs.downloadState = 2
- app.library.backgroundNotification.show = false
- })
- })
- } else {
- console.log("Download next", downloaded.next)
- }
- }
- }
-
- function processChunk(response) {
- downloaded = response
- library = library.concat(downloaded.data)
- self.library.backgroundNotification.show = true
- self.library.backgroundNotification.message = app.getLz('notification.updatingLibrarySongs')
- self.library.backgroundNotification.total = downloaded.meta.total
- self.library.backgroundNotification.progress = library.length
-
- if (downloaded.meta.total == 0) {
- self.library.songs.downloadState = 3
- return
- }
- if (typeof downloaded.next == "undefined") {
- console.log("downloaded.next is undefined")
- self.library.songs.listing = library
- self.library.songs.downloadState = 2
- self.library.backgroundNotification.show = false
- self.searchLibrarySongs()
- CiderCache.putCache(cacheId, library)
- }
- if (downloaded.meta.total > library.length || typeof downloaded.meta.next != "undefined") {
- console.log(`downloading next chunk - ${library.length} songs so far`)
- downloadChunk()
- } else {
- self.library.songs.listing = library
- self.library.songs.downloadState = 2
- self.library.backgroundNotification.show = false
- self.searchLibrarySongs()
- CiderCache.putCache(cacheId, library)
- // console.log(library)
- }
- }
-
- downloadChunk()
- },
- // copy the getLibrarySongsFull function except change Songs to Albums
- async getLibraryAlbumsFull(force = false, index) {
- let self = this
- let library = []
- let cacheId = "library-albums"
- let downloaded = null;
- if ((this.library.albums.downloadState == 2 || this.library.albums.downloadState == 1) && !force) {
- return
- }
- let libraryAlbums = await CiderCache.getCache(cacheId)
- if (libraryAlbums) {
- this.library.albums.listing = libraryAlbums
- this.searchLibraryAlbums(index)
- }
- if (this.songstest) {
- return
- }
- this.library.albums.downloadState = 1
- this.library.backgroundNotification.show = true
- this.library.backgroundNotification.message = app.getLz('notification.updatingLibraryAlbums')
-
- function downloadChunk() {
- self.library.albums.downloadState = 1
- const params = {
- "include[library-albums]": "catalog,artists,albums",
- "fields[artists]": "name,url,id",
- "fields[albums]": "name,url,id",
- platform: "web",
- "fields[catalog]": "artistUrl,albumUrl",
- "fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url",
- limit: 100,
- l: self.mklang
- }
- const safeparams = {
- platform: "web",
- limit: "60",
- "include[library-albums]": "artists",
- "include[library-artists]": "catalog",
- "include[albums]": "artists",
- "fields[artists]": "name,url",
- "fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url",
- "includeOnly": "catalog,artists"
- }
- if (downloaded == null) {
- app.mk.api.v3.music(`/v1/me/library/albums/`, params).then((response) => {
- processChunk(response.data)
- }).catch((error) => {
- console.log('safe loading');
- app.mk.api.v3.music(`/v1/me/library/albums/`, safeparams).then((response) => {
- processChunk(response.data)
- }).catch((error) => {
- console.log('safe loading failed', error)
- app.library.albums.downloadState = 2
- app.library.backgroundNotification.show = false
- })
- })
- } else {
- if (downloaded.next != null) {
- app.mk.api.v3.music(downloaded.next, params).then((response) => {
- processChunk(response.data)
- }).catch((error) => {
- console.log('safe loading');
- app.mk.api.v3.music(downloaded.next, safeparams).then((response) => {
- processChunk(response.data)
- }).catch((error) => {
- console.log('safe loading failed', error);
- app.library.albums.downloadState = 2
- app.library.backgroundNotification.show = false
- })
- })
- } else {
- console.log("Download next", downloaded.next)
- }
- }
- }
-
- function processChunk(response) {
- downloaded = response
- library = library.concat(downloaded.data)
- self.library.backgroundNotification.show = true
- self.library.backgroundNotification.message = app.getLz('notification.updatingLibraryAlbums')
- self.library.backgroundNotification.total = downloaded.meta.total
- self.library.backgroundNotification.progress = library.length
- if (downloaded.meta.total == 0) {
- self.library.albums.downloadState = 3
- return
- }
- if (typeof downloaded.next == "undefined") {
- console.log("downloaded.next is undefined")
- self.library.albums.listing = library
- self.library.albums.downloadState = 2
- self.library.backgroundNotification.show = false
- CiderCache.putCache(cacheId, library)
- self.searchLibraryAlbums(index)
- }
- if (downloaded.meta.total > library.length || typeof downloaded.meta.next != "undefined") {
- console.log(`downloading next chunk - ${library.length
- } albums so far`)
- downloadChunk()
- } else {
- self.library.albums.listing = library
- self.library.albums.downloadState = 2
- self.library.backgroundNotification.show = false
- CiderCache.putCache(cacheId, library)
- self.searchLibraryAlbums(index)
- // console.log(library)
- }
- }
-
- downloadChunk()
- },
- // copy the getLibrarySongsFull function except change Songs to Albums
- async getLibraryArtistsFull(force = false, index) {
- let self = this
- let library = []
- let cacheId = "library-artists"
- let downloaded = null;
- if ((this.library.artists.downloadState == 2 || this.library.artists.downloadState == 1) && !force) {
- return
- }
- let libraryArtists = await CiderCache.getCache(cacheId)
- if (libraryArtists) {
- this.library.artists.listing = libraryArtists
- this.searchLibraryArtists(index)
- }
- if (this.songstest) {
- return
- }
- this.library.artists.downloadState = 1
- this.library.backgroundNotification.show = true
- this.library.backgroundNotification.message = app.getLz('notification.updatingLibraryArtists')
-
- function downloadChunk() {
- self.library.artists.downloadState = 1
- const params = {
- include: "catalog",
- // "include[library-artists]": "catalog,artists,albums",
- // "fields[artists]": "name,url,id",
- // "fields[albums]": "name,url,id",
- platform: "web",
- // "fields[catalog]": "artistUrl,albumUrl",
- // "fields[artists]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url",
- limit: 100,
- l: self.mklang
- }
- const safeparams = {
- include: "catalog",
- platform: "web",
- limit: 50,
- }
- if (downloaded == null) {
- app.mk.api.v3.music(`/v1/me/library/artists/`, params).then((response) => {
- processChunk(response.data)
- }).catch((error) => {
- console.log('safe loading');
- app.mk.api.v3.music(`/v1/me/library/artists/`, safeparams).then((response) => {
- processChunk(response.data)
- }).catch((error) => {
- console.log('safe loading failed', error)
- app.library.artists.downloadState = 2
- app.library.backgroundNotification.show = false
- })
- })
-
- } else {
- if (downloaded.next != null) {
- app.mk.api.v3.music(downloaded.next, params).then((response) => {
- processChunk(response.data)
- }).catch((error) => {
- console.log('safe loading');
- app.mk.api.v3.music(downloaded.next, safeparams).then((response) => {
- processChunk(response.data)
- }).catch((error) => {
- console.log('safe loading failed', error)
- app.library.artists.downloadState = 2
- app.library.backgroundNotification.show = false
- })
- })
- } else {
- console.log("Download next", downloaded.next)
- }
-
- }
- }
-
- function processChunk(response) {
- downloaded = response
- library = library.concat(downloaded.data)
- self.library.backgroundNotification.show = true
- self.library.backgroundNotification.message = app.getLz('notification.updatingLibraryArtists')
- self.library.backgroundNotification.total = downloaded.meta.total
- self.library.backgroundNotification.progress = library.length
- if (downloaded.meta.total == 0) {
- self.library.albums.downloadState = 3
- return
- }
- if (typeof downloaded.next == "undefined") {
- console.log("downloaded.next is undefined")
- self.library.artists.listing = library
- self.library.artists.downloadState = 2
- self.library.artists.show = false
- CiderCache.putCache(cacheId, library)
- self.searchLibraryArtists(index)
- }
- if (downloaded.meta.total > library.length || typeof downloaded.meta.next != "undefined") {
- console.log(`downloading next chunk - ${library.length
- } artists so far`)
- downloadChunk()
- } else {
- self.library.artists.listing = library
- self.library.artists.downloadState = 2
- self.library.backgroundNotification.show = false
- CiderCache.putCache(cacheId, library)
- self.searchLibraryArtists(index)
- // console.log(library)
- }
- }
-
- downloadChunk()
- },
- getTotalTime() {
- try {
- if (app.showingPlaylist.relationships.tracks.data.length > 0) {
- const timeInSeconds = Math.round([].concat(...app.showingPlaylist.relationships.tracks.data).reduce((a, { attributes: { durationInMillis } }) => a + durationInMillis, 0) / 1000);
- return `${app.showingPlaylist.relationships.tracks.data.length} ${app.getLz('term.track', options = { count: app.showingPlaylist.relationships.tracks.data.length })}, ${this.convertTime(timeInSeconds, 'long')}`
- } else return ""
- } catch (err) {
- return ""
- }
- },
- async getLibrarySongs() {
- let response = await this.mkapi("songs", true, "", { limit: 100, l: this.mklang }, { includeResponseMeta: !0 })
- this.library.songs.listing = response.data.data
- this.library.songs.meta = response.data.meta
- },
- async getLibraryAlbums() {
- let response = await this.mkapi("albums", true, "", { limit: 100, l: this.mklang }, { includeResponseMeta: !0 })
- this.library.albums.listing = response.data.data
- this.library.albums.meta = response.data.meta
- },
- async getListenNow(attempt = 0) {
- if (this.listennow.timestamp > Date.now() - 120000) {
- return
- }
-
- if (attempt > 3) {
- return
- }
- try {
- this.listennow = (await this.mk.api.v3.music(`v1/me/recommendations?timezone=${encodeURIComponent(this.formatTimezoneOffset())}`, {
- name: "listen-now",
- with: "friendsMix,library,social",
- "art[social-profiles:url]": "c",
- "art[url]": "c,f",
- "omit[resource]": "autos",
- "relate[editorial-items]": "contents",
- extend: ["editorialCard", "editorialVideo"],
- "extend[albums]": ["artistUrl"],
- "extend[library-albums]": ["artistUrl", "editorialVideo"],
- "extend[playlists]": ["artistNames", "editorialArtwork", "editorialVideo"],
- "extend[library-playlists]": ["artistNames", "editorialArtwork", "editorialVideo"],
- "extend[social-profiles]": "topGenreNames",
- "include[albums]": "artists",
- "include[songs]": "artists",
- "include[music-videos]": "artists",
- "fields[albums]": ["artistName", "artistUrl", "artwork", "contentRating", "editorialArtwork", "editorialVideo", "name", "playParams", "releaseDate", "url"],
- "fields[artists]": ["name", "url"],
- "extend[stations]": ["airDate", "supportsAirTimeUpdates"],
- "meta[stations]": "inflectionPoints",
- types: "artists,albums,editorial-items,library-albums,library-playlists,music-movies,music-videos,playlists,stations,uploaded-audios,uploaded-videos,activities,apple-curators,curators,tv-shows,social-upsells",
- platform: "web",
- l: this.mklang
- }, {
- includeResponseMeta: !0,
- reload: !0
- })).data;
- this.listennow.timestamp = Date.now()
- console.log(this.listennow)
- } catch (e) {
- console.log(e)
- this.getListenNow(attempt + 1)
- }
- },
- async getBrowsePage(attempt = 0) {
- if (this.browsepage.timestamp > Date.now() - 120000) {
- return
- }
- if (attempt > 3) {
- return
- }
- try {
- let browse = await app.mk.api.v3.music(`/v1/editorial/${app.mk.storefrontId}/groupings`, {
- platform: "web",
- name: "music",
- "omit[resource:artists]": "relationships",
- "include[albums]": "artists",
- "include[songs]": "artists",
- "include[music-videos]": "artists",
- extend: "editorialArtwork,artistUrl",
- "fields[artists]": "name,url,artwork,editorialArtwork,genreNames,editorialNotes",
- "art[url]": "f",
- l: this.mklang
- });
- this.browsepage = browse.data.data[0];
- this.browsepage.timestamp = Date.now()
- console.log(this.browsepage)
- } catch (e) {
- console.log(e)
- this.getBrowsePage(attempt + 1)
- }
- },
- async getRadioStations(attempt = 0) {
- if (attempt > 3) {
- return
- }
- try {
- this.radio.personal = (await app.mk.api.v3.music(`/v1/me/recent/radio-stations`, {
- "platform": "web",
- "art[url]": "f",
- l: this.mklang
- })).data.data;
- } catch (e) {
- console.log(e)
- this.getRadioStations(attempt + 1)
- }
- },
- async getMadeForYou(attempt = 0) {
- if (attempt > 3) {
- return
- }
- try {
- mfu = await app.mk.api.v3.music("/v1/me/library/playlists?platform=web&extend=editorialVideo&fields%5Bplaylists%5D=lastModifiedDate&filter%5Bfeatured%5D=made-for-you&include%5Blibrary-playlists%5D=catalog&fields%5Blibrary-playlists%5D=artwork%2Cname%2CplayParams%2CdateAdded")
- this.madeforyou = mfu.data
- } catch (e) {
- console.log(e)
- this.getMadeForYou(attempt + 1)
- }
- },
- newPlaylistFolder(name = app.getLz('term.newPlaylistFolder')) {
- let self = this
- this.mk.api.v3.music(
- "/v1/me/library/playlist-folders/", {}, {
- fetchOptions: {
- method: "POST",
- body: JSON.stringify({
- attributes: { name: name }
- })
- }
- }
- ).then((res) => {
- let playlist = (res.data.data[0])
- self.playlists.listing.push({
- id: playlist.id,
- attributes: {
- name: playlist.attributes.name
- },
- type: "library-playlist-folders",
- parent: "p.playlistsroot"
- })
- self.sortPlaylists()
- setTimeout(() => {
- app.refreshPlaylists()
- }, 13000)
- })
- },
- showSearch() {
- this.page = "search"
- },
- loadLyrics() {
- const musicType = (MusicKit.getInstance().nowPlayingItem != null) ? MusicKit.getInstance().nowPlayingItem["type"] ?? '' : '';
- console.log("mt", musicType)
- if (musicType === "musicVideo") {
- this.loadYTLyrics();
- } else {
- if (app.cfg.lyrics.enable_mxm) {
- this.loadMXM();
- } else {
- this.loadAMLyrics();
- }
- }
- },
- loadAMLyrics() {
- const songID = (this.mk.nowPlayingItem != null) ? this.mk.nowPlayingItem["_songId"] ?? (this.mk.nowPlayingItem["songId"] ?? -1) : -1;
- // this.getMXM( trackName, artistName, 'en', duration);
- if (songID != -1) {
- this.mk.api.v3.music(`v1/catalog/${this.mk.storefrontId}/songs/${songID}/lyrics`)
- .then((response) => {
- this.lyricsMediaItem = response.data?.data[0]?.attributes["ttml"]
- this.parseTTML()
- })
- }
- },
- addToLibrary(id) {
- let self = this
- this.mk.addToLibrary(id).then((data) => {
- self.getLibrarySongsFull(true)
- })
- notyf.success(app.getLz('action.addToLibrary.success'));
- },
- removeFromLibrary(kind, id) {
- let self = this
- let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
- app.mk.api.v3.music(`v1/me/library/${truekind}/${id.toString()}`, {}, {
- fetchOptions: {
- method: "DELETE"
- }
- }).then((data) => {
- self.getLibrarySongsFull(true)
- })
- notyf.success(app.getLz('action.removeFromLibrary.success'))
- },
- async loadYTLyrics() {
- const track = (this.mk.nowPlayingItem != null) ? this.mk.nowPlayingItem.title ?? '' : '';
- const artist = (this.mk.nowPlayingItem != null) ? this.mk.nowPlayingItem.artistName ?? '' : '';
- const time = (this.mk.nowPlayingItem != null) ? (Math.round((this.mk.nowPlayingItem.attributes["durationInMillis"] ?? -1000) / 1000) ?? -1) : -1;
- ipcRenderer.invoke('getYTLyrics', track, artist).then((result) => {
- if (result.length > 0) {
- let ytid = result[0]['id']['videoId'];
- if (app.cfg.lyrics.enable_yt) {
- loadYT(ytid, app.cfg.lyrics.mxm_language ?? "en")
- } else {
- app.loadMXM()
- }
- } else {
- app.loadMXM()
- }
-
- function loadYT(id, lang) {
- let req = new XMLHttpRequest();
- let url = `https://www.youtube.com/watch?&v=${id}`;
- req.open('GET', url, true);
- req.onerror = function (e) {
- this.loadMXM();
- }
- req.onload = function () {
- // console.log(this.responseText);
- res = this.responseText;
- let captionurl1 = res.substring(res.indexOf(`{"playerCaptionsRenderer":{"baseUrl":"`) + (`{"playerCaptionsRenderer":{"baseUrl":"`).length);
- let captionurl = captionurl1.substring(0, captionurl1.indexOf(`"`));
- if (captionurl.includes("timedtext")) {
- let json = JSON.parse(`{"url": "${captionurl}"}`);
- let newurl = json.url + `&lang=${lang}&format=ttml`
-
- let req2 = new XMLHttpRequest();
-
- req2.open('GET', newurl, true);
- req2.onerror = function (e) {
- app.loadMXM();
- }
- req2.onload = function () {
- try {
- const ttmlLyrics = this.responseText;
- if (ttmlLyrics) {
- this.lyricsMediaItem = ttmlLyrics
- this.parseTTML()
- }
- } catch (e) {
- app.loadMXM();
- }
-
- }
- req2.send();
- } else {
-
- app.loadMXM();
-
- }
- }
- req.send();
- }
-
- })
-
- },
- loadMXM() {
- let attempt = 0;
- const track = encodeURIComponent((this.mk.nowPlayingItem != null) ? this.mk.nowPlayingItem.title ?? '' : '');
- const artist = encodeURIComponent((this.mk.nowPlayingItem != null) ? this.mk.nowPlayingItem.artistName ?? '' : '');
- const time = encodeURIComponent((this.mk.nowPlayingItem != null) ? (Math.round((this.mk.nowPlayingItem.attributes["durationInMillis"] ?? -1000) / 1000) ?? -1) : -1);
- const id = encodeURIComponent((this.mk.nowPlayingItem != null) ? app.mk.nowPlayingItem._songId ?? (app.mk.nowPlayingItem["songId"] ?? '') : '');
- let lrcfile = "";
- let richsync = [];
- const lang = app.cfg.lyrics.mxm_language // translation language
- function revisedRandId() {
- return Math.random().toString(36).replace(/[^a-z]+/g, '').slice(2, 10);
- }
-
- /* get token */
- function getToken(mode, track, artist, songid, lang, time, id) {
- if (attempt > 2) {
- app.loadAMLyrics();
- } else {
- attempt = attempt + 1;
- let url = "https://apic-desktop.musixmatch.com/ws/1.1/token.get?app_id=web-desktop-app-v1.0&t=" + revisedRandId();
- let req = new XMLHttpRequest();
- req.overrideMimeType("application/json");
- req.open('GET', url, true);
- req.setRequestHeader("authority", "apic-desktop.musixmatch.com");
- req.onload = function () {
- let jsonResponse = JSON.parse(this.responseText);
- let status2 = jsonResponse["message"]["header"]["status_code"];
- if (status2 == 200) {
- let token = jsonResponse["message"]["body"]["user_token"] ?? '';
- if (token != "" && token != "UpgradeOnlyUpgradeOnlyUpgradeOnlyUpgradeOnly") {
- console.log('200 token', mode);
- // token good
- app.mxmtoken = token;
-
- if (mode == 1) {
- getMXMSubs(track, artist, app.mxmtoken, lang, time, id);
- } else {
- getMXMTrans(songid, lang, app.mxmtoken);
- }
- } else {
- console.log('fake 200 token');
- getToken(mode, track, artist, songid, lang, time)
- }
- } else {
- console.log('token 4xx');
- getToken(mode, track, artist, songid, lang, time)
- }
-
- };
- req.onerror = function () {
- console.log('error');
- app.loadAMLyrics();
- };
- req.send();
- }
- }
-
- function getMXMSubs(track, artist, token, lang, time, id) {
- let usertoken = encodeURIComponent(token);
- let richsyncQuery = (app.cfg.lyrics.mxm_karaoke) ? "&optional_calls=track.richsync" : ""
- let timecustom = (!time || (time && time < 0)) ? '' : `&f_subtitle_length=${time}&q_duration=${time}&f_subtitle_length_max_deviation=40`;
- let itunesid = (id && id != "") ? `&track_itunes_id=${id}` : '';
- let url = "https://apic-desktop.musixmatch.com/ws/1.1/macro.subtitles.get?format=json&namespace=lyrics_richsynched" + richsyncQuery + "&subtitle_format=lrc&q_artist=" + artist + "&q_track=" + track + itunesid + "&usertoken=" + usertoken + timecustom + "&app_id=web-desktop-app-v1.0&t=" + revisedRandId();
- let req = new XMLHttpRequest();
- req.overrideMimeType("application/json");
- req.open('GET', url, true);
- req.setRequestHeader("authority", "apic-desktop.musixmatch.com");
- req.onload = function () {
- let jsonResponse = JSON.parse(this.responseText);
- console.log(jsonResponse);
- let status1 = jsonResponse["message"]["header"]["status_code"];
-
- if (status1 == 200) {
- let id = '';
- try {
- if (jsonResponse["message"]["body"]["macro_calls"]["matcher.track.get"]["message"]["header"]["status_code"] == 200 && jsonResponse["message"]["body"]["macro_calls"]["track.subtitles.get"]["message"]["header"]["status_code"] == 200) {
- id = jsonResponse["message"]["body"]["macro_calls"]["matcher.track.get"]["message"]["body"]["track"]["track_id"] ?? '';
- lrcfile = jsonResponse["message"]["body"]["macro_calls"]["track.subtitles.get"]["message"]["body"]["subtitle_list"][0]["subtitle"]["subtitle_body"];
-
- try {
- lrcrich = jsonResponse["message"]["body"]["macro_calls"]["track.richsync.get"]["message"]["body"]["richsync"]["richsync_body"];
- richsync = JSON.parse(lrcrich);
- app.richlyrics = richsync;
- } catch (_) {
- }
- }
-
- if (lrcfile == "") {
- app.loadAMLyrics()
- } else {
- if (richsync == [] || richsync.length == 0) {
- console.log("ok");
- // process lrcfile to json here
- app.lyricsMediaItem = lrcfile
- let u = app.lyricsMediaItem.split(/[\r\n]/);
- let preLrc = []
- for (var i = u.length - 1; i >= 0; i--) {
- let xline = (/(\[[0-9.:\[\]]*\])+(.*)/).exec(u[i])
- let end = (preLrc.length > 0) ? ((preLrc[preLrc.length - 1].startTime) ?? 99999) : 99999
- preLrc.push({
- startTime: app.toMS(xline[1].substring(1, xline[1].length - 2)) ?? 0,
- endTime: end,
- line: xline[2],
- translation: ''
- })
- }
- if (preLrc.length > 0)
- preLrc.push({
- startTime: 0,
- endTime: preLrc[preLrc.length - 1].startTime,
- line: "lrcInstrumental",
- translation: ''
- });
- app.lyrics = preLrc.reverse();
- } else {
- preLrc = richsync.map(function (item) {
- return {
- startTime: item.ts,
- endTime: item.te,
- line: item.x,
- translation: ''
- }
- })
- if (preLrc.length > 0)
- preLrc.unshift({
- startTime: 0,
- endTime: preLrc[0].startTime,
- line: "lrcInstrumental",
- translation: ''
- });
- app.lyrics = preLrc;
- }
- if (lrcfile != null && lrcfile != '' && lang != "disabled") {
- // load translation
- getMXMTrans(id, lang, token);
- } else {
- app.loadAMLyrics()
- }
- }
- } catch (e) {
- console.log(e);
- app.loadAMLyrics()
- }
- } else { //4xx rejected
- getToken(1, track, artist, '', lang, time);
- }
- }
- req.onerror = function () {
- console.log('error');
- app.loadAMLyrics();
- };
- req.send();
- }
-
- function getMXMTrans(id, lang, token) {
- if (lang != "disabled" && id != '') {
- let usertoken = encodeURIComponent(token);
- let url2 = "https://apic-desktop.musixmatch.com/ws/1.1/crowd.track.translations.get?translation_fields_set=minimal&selected_language=" + lang + "&track_id=" + id + "&comment_format=text&part=user&format=json&usertoken=" + usertoken + "&app_id=web-desktop-app-v1.0&t=" + revisedRandId();
- let req2 = new XMLHttpRequest();
- req2.overrideMimeType("application/json");
- req2.open('GET', url2, true);
- req2.setRequestHeader("authority", "apic-desktop.musixmatch.com");
- req2.onload = function () {
- let jsonResponse2 = JSON.parse(this.responseText);
- console.log(jsonResponse2);
- let status2 = jsonResponse2["message"]["header"]["status_code"];
- if (status2 == 200) {
- try {
- let preTrans = []
- let u = app.lyrics;
- let translation_list = jsonResponse2["message"]["body"]["translations_list"];
- if (translation_list.length > 0) {
- for (var i = 0; i < u.length - 1; i++) {
- preTrans[i] = ""
- for (var trans_line of translation_list) {
- if (u[i].line == " " + trans_line["translation"]["matched_line"] || u[i].line == trans_line["translation"]["matched_line"]) {
- u[i].translation = trans_line["translation"]["description"];
- break;
- }
- }
- }
- app.lyrics = u;
- }
- } catch (e) {
- /// not found trans -> ignore
- }
- } else { //4xx rejected
- getToken(2, '', '', id, lang, '');
- }
- }
- req2.send();
- }
-
- }
-
- if (track != "" & track != "No Title Found") {
- if (app.mxmtoken != null && app.mxmtoken != '') {
- getMXMSubs(track, artist, app.mxmtoken, lang, time, id)
- } else {
- getToken(1, track, artist, '', lang, time);
- }
- }
- },
- toMS(str) {
- let rawTime = str.match(/(\d+:)?(\d+:)?(\d+)(\.\d+)?/);
- let hours = (rawTime[2] != null) ? (rawTime[1].replace(":", "")) : 0;
- let minutes = (rawTime[2] != null) ? (hours * 60 + rawTime[2].replace(":", "") * 1) : ((rawTime[1] != null) ? rawTime[1].replace(":", "") : 0);
- let seconds = (rawTime[3] != null) ? (rawTime[3]) : 0;
- let milliseconds = (rawTime[4] != null) ? (rawTime[4].replace(".", "")) : 0
- return parseFloat(`${minutes * 60 + seconds * 1}.${milliseconds * 1}`);
- },
- parseTTML() {
- this.lyrics = [];
- let preLrc = [];
- let xml = this.stringToXml(this.lyricsMediaItem);
- let lyricsLines = xml.getElementsByTagName('p');
- let synced = true;
- let endTimes = [];
- if (xml.getElementsByTagName('tt')[0].getAttribute("itunes:timing") === "None") {
- synced = false;
- }
- endTimes.push(0);
- if (synced) {
- for (element of lyricsLines) {
- start = this.toMS(element.getAttribute('begin'))
- end = this.toMS(element.getAttribute('end'))
- if (start - endTimes[endTimes.length - 1] > 5 && endTimes[endTimes.length - 1] != 0) {
- preLrc.push({
- startTime: endTimes[endTimes.length - 1],
- endTime: start,
- line: "lrcInstrumental"
- });
- }
- preLrc.push({ startTime: start, endTime: end, line: element.textContent });
- endTimes.push(end);
- }
- // first line dot
- if (preLrc.length > 0)
- preLrc.unshift({ startTime: 0, endTime: preLrc[0].startTime, line: "lrcInstrumental" });
- } else {
- for (element of lyricsLines) {
- preLrc.push({ startTime: 9999999, endTime: 9999999, line: element.textContent });
- }
- }
- this.lyrics = preLrc;
-
- },
- parseLyrics() {
- let xml = this.stringToXml(this.lyricsMediaItem)
- let json = xmlToJson(xml);
- this.lyrics = json
- },
- stringToXml(st) {
- // string to xml
- let xml = (new DOMParser()).parseFromString(st, "text/xml");
- return xml;
-
- },
- getCurrentTime() {
- return parseFloat(this.hmsToSecondsOnly(this.parseTime(this.mk.nowPlayingItem.attributes.durationInMillis - app.mk.currentPlaybackTimeRemaining * 1000)));
- },
- seekTo(time) {
- this.mk.seekToTime(time);
- },
- parseTime(value) {
- let minutes = Math.floor(value / 60000);
- let seconds = ((value % 60000) / 1000).toFixed(0);
- return minutes + ":" + (seconds < 10 ? '0' : '') + seconds;
- },
- parseTimeDecimal(value) {
- let minutes = Math.floor(value / 60000);
- let seconds = ((value % 60000) / 1000).toFixed(0);
- return minutes + "." + (seconds < 10 ? '0' : '') + seconds;
- },
- hmsToSecondsOnly(str) {
- let p = str.split(':'),
- s = 0,
- m = 1;
-
- while (p.length > 0) {
- s += m * parseInt(p.pop(), 10);
- m *= 60;
- }
-
- return s;
- },
- getLyricBGStyle(start, end) {
- let currentTime = this.getCurrentTime();
- // let duration = this.mk.nowPlayingItem.attributes.durationInMillis
- let start2 = this.hmsToSecondsOnly(start)
- let end2 = this.hmsToSecondsOnly(end)
- // let currentProgress = ((100 * (currentTime)) / (end2))
- // check if currenttime is between start and end
- this.player.lyricsDebug.start = start2
- this.player.lyricsDebug.end = end2
- this.player.lyricsDebug.current = currentTime
- if (currentTime >= start2 && currentTime <= end2) {
- return {
- "--bgSpeed": `${(end2 - start2)}s`
- }
- } else {
- return {}
- }
- },
- playMediaItemById(id, kind, isLibrary, raurl = "") {
- let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
- console.log(id, truekind, isLibrary)
- try {
- if (truekind.includes("artist")) {
- app.mk.setStationQueue({ artist: 'a-' + id }).then(() => {
- app.mk.play()
- })
- } else if (truekind == "radioStations") {
- this.mk.setStationQueue({ url: raurl }).then(function (queue) {
- MusicKit.getInstance().play()
- });
- } else {
- this.mk.setQueue({
- [truekind]: [id],
- parameters: { l: this.mklang }
- }).then(function (queue) {
- MusicKit.getInstance().play()
- })
- }
- } catch (err) {
- console.log(err)
- this.playMediaItemById(id, kind, isLibrary, raurl)
- }
- },
- queueParentandplayChild(parent, childIndex, item) {
-
- /* Randomize array in-place using Durstenfeld shuffle algorithm */
- function shuffleArray(array) {
- for (var i = array.length - 1; i > 0; i--) {
- var j = Math.floor(Math.random() * (i + 1));
- var temp = array[i];
- array[i] = array[j];
- array[j] = temp;
- }
- }
-
- let kind = parent.substring(0, parent.indexOf(":"))
- let id = parent.substring(parent.indexOf(":") + 1, parent.length)
- let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
- console.log(truekind, id)
-
- try {
- if (app.library.songs.displayListing.length > childIndex && parent == "librarysongs") {
- console.log(item)
- if (item && ((app.library.songs.displayListing[childIndex].id != item.id))) {
- childIndex = app.library.songs.displayListing.indexOf(item)
- }
-
- let query = app.library.songs.displayListing.map(item => new MusicKit.MediaItem(item));
-
-
- app.mk.stop().then(() => {
- if (item) {
- app.mk.setQueue({
- [item.attributes.playParams.kind ?? item.type]: item.attributes.playParams.id ?? item.id,
- parameters: { l: app.mklang }
- }).then(function () {
- app.mk.play().then(() => {
- if (app.mk.shuffleMode == 1) {
- shuffleArray(query)
- } else {
- for (let i = 0; i < query.length; i++) {
- if (query[i].id == item.id) {
- query.splice(0, i + 1);
- break;
- }
- }
- }
- app.mk.queue.append(query)
- })
- })
- } else {
- app.mk.queue.splice(0, app.mk.queue._itemIDs.length)
- if (app.mk.shuffleMode == 1) {
- shuffleArray(query)
- }
- app.mk.queue.append(query)
- if (childIndex != -1) {
- app.mk.changeToMediaAtIndex(childIndex)
- } else {
- app.mk.play()
- }
-
- }
- })
- } else if (parent.startsWith('listitem-hr')) {
- app.mk.stop().then(() => {
- if (app.mk.shuffleMode == 1) {
- app.mk.setQueue({
- [item.attributes.playParams.kind ?? item.type]: item.attributes.playParams.id ?? item.id
- }).then(function () {
- app.mk.play().then(() => {
- let data = JSON.parse(parent.split('listitem-hr')[1] ?? '[]')
- let itemsToPlay = {}
- let u = data.map(x => x.id)
- try {
- data.splice(u.indexOf(item.attributes.playParams.id ?? item.id), 1)
- } catch (e) {
- }
- if (app.mk.shuffleMode == 1) {
- shuffleArray(data)
- }
- data.forEach(item => {
- if (!itemsToPlay[item.kind]) {
- itemsToPlay[item.kind] = []
- }
- itemsToPlay[item.kind].push(item.id)
- })
- // loop through itemsToPlay
- for (let kind in itemsToPlay) {
- let ids = itemsToPlay[kind]
- if (ids.length > 0) {
- app.mk.playLater({ [kind + "s"]: itemsToPlay[kind] })
- }
- }
- })
- })
- } else {
- let data = JSON.parse(parent.split('listitem-hr')[1] ?? '[]')
- let itemsToPlay = {}
- data.forEach(item => {
- if (!itemsToPlay[item.kind]) {
- itemsToPlay[item.kind] = []
- }
- itemsToPlay[item.kind].push(item.id)
- })
- // loop through itemsToPlay
- app.mk.queue.splice(0, app.mk.queue._itemIDs.length)
- let ind = 0;
- for (let kind in itemsToPlay) {
- let ids = itemsToPlay[kind]
- if (ids.length > 0) {
- if (app.mk.queue._itemIDs.length > 0) {
- app.mk.playLater({ [kind + "s"]: itemsToPlay[kind] }).then(function () {
- ind += 1;
- console.log(ind, Object.keys(itemsToPlay).length)
- if (ind >= Object.keys(itemsToPlay).length) {
- app.mk.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(item.attributes.playParams.id ?? item.id))
- }
- }
- )
- } else {
- app.mk.setQueue({ [kind + "s"]: itemsToPlay[kind] }).then(function () {
- ind += 1;
- console.log(ind, Object.keys(itemsToPlay).length)
- if (ind >= Object.keys(itemsToPlay).length) {
- app.mk.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(item.attributes.playParams.id ?? item.id))
- }
- }
- )
- }
- }
-
- }
- }
- })
- } else {
- app.mk.stop().then(() => {
- if (truekind == "playlists" && (id.startsWith("p.") || id.startsWith("pl.u"))) {
- app.mk.setQueue({
- [item.attributes.playParams.kind ?? item.type]: item.attributes.playParams.id ?? item.id,
- parameters: { l: app.mklang }
- }).then(function () {
- app.mk.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(item.id) ?? 1).then(function () {
- if ((app.showingPlaylist && app.showingPlaylist.id == id)) {
- let query = app.showingPlaylist.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
- let u = query;
- if (app.mk.shuffleMode == 1) {
- shuffleArray(u)
- } else {
- for (let i = 0; i < app.showingPlaylist.relationships.tracks.data.length; i++) {
- if (app.showingPlaylist.relationships.tracks.data[i].id == item.id) {
- u.splice(0, i + 1);
- break;
- }
- }
- }
- app.mk.queue.append(u)
- } else {
- app.getPlaylistFromID(id, true).then(function () {
- let query = app.showingPlaylist.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
- let u = query;
- if (app.mk.shuffleMode == 1) {
- shuffleArray(u)
- } else {
- for (let i = 0; i < app.showingPlaylist.relationships.tracks.data.length; i++) {
- if (app.showingPlaylist.relationships.tracks.data[i].id == item.id) {
- u.splice(0, i + 1);
- break;
- }
- }
- }
- app.mk.queue.append(u)
- })
- }
- })
-
- })
- } else {
- this.mk.setQueue({
- [truekind]: [id],
- parameters: { l: this.mklang }
- }).then(function (queue) {
- if (item && ((queue._itemIDs[childIndex] != item.id))) {
- childIndex = queue._itemIDs.indexOf(item.id)
- }
- if (childIndex != -1) {
- app.mk.changeToMediaAtIndex(childIndex)
- } else if (item) {
- app.mk.playNext({
- [item.attributes.playParams.kind ?? item.type]: item.attributes.playParams.id ?? item.id
- }).then(function () {
- app.mk.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(item.id) ?? 1)
- app.mk.play()
- })
- } else {
- app.mk.play()
- }
- })
- }
- })
- }
- } catch (err) {
- console.log(err)
- try {
- app.mk.stop()
- } catch (e) {
- }
- this.playMediaItemById(item.attributes.playParams.id ?? item.id, item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.url)
- }
-
- },
- friendlyTypes(type) {
- // use switch statement to return friendly name for media types "songs,artists,albums,playlists,music-videos,stations,apple-curators,curators"
- switch (type) {
- case "song":
- return app.getLz('term.songs')
- break;
- case "artist":
- return app.getLz('term.artists')
- break;
- case "album":
- return app.getLz('term.albums')
- break;
- case "playlist":
- return app.getLz('term.playlists')
- break;
- case "music_video":
- return app.getLz('term.musicVideos')
- break;
- case "station":
- return app.getLz('term.stations')
- break;
- case "apple-curator":
- return app.getLz('term.appleCurators')
- break;
- case "radio_show":
- return app.getLz('term.radioShows')
- break;
- case "record_label":
- return app.getLz('term.recordLabels')
- break;
- case "radio_episode":
- return app.getLz('podcast.episodes')
- break;
- case "video_extra":
- return app.getLz('term.videoExtras')
- break;
- case "curator":
- return app.getLz('term.curators')
- break;
- case "top":
- return app.getLz('term.top')
- break;
- default:
- return type
- break;
- }
- },
- async searchQuery(term = this.search.term) {
- let self = this
- if (term == "") {
- return
- }
- //this.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/search?term=${this.search.term}`
- this.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/search?term=${encodeURIComponent(this.search.term)}`, {
- types: "activities,albums,apple-curators,artists,curators,editorial-items,music-movies,music-videos,playlists,songs,stations,tv-episodes,uploaded-videos,record-labels",
- "relate[editorial-items]": "contents",
- "include[editorial-items]": "contents",
- "include[albums]": "artists",
- "include[artists]": "artists",
- "include[songs]": "artists,albums",
- "include[music-videos]": "artists",
- "extend": "artistUrl",
- "fields[artists]": "url,name,artwork,hero",
- "fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url",
- "with": "serverBubbles,lyricHighlights",
- "art[url]": "c,f",
- "omit[resource]": "autos",
- "platform": "web",
- limit: 25,
- l: this.mklang
- }).then(function (results) {
- results.data.results["meta"] = results.data.meta
- self.search.results = results.data.results
- })
-
- await app.mk.api.v3.music(`v1/social/${app.mk.storefrontId}/search?term=${app.search.term}`, {
- types: ["playlists", "social-profiles"],
- limit: 25,
- with: ["serverBubbles", "lyricSnippet"],
- "art[url]": "f",
- "art[social-profiles:url]": "c"
- }, { includeResponseMeta: !0 }).then(function (results) {
- results.data.results["meta"] = results.data.meta
- self.search.resultsSocial = results.data.results
- })
- },
- async inLibrary(items = []) {
- let types = []
-
- for (let item of items) {
- let type = item.type
- if (type.slice(-1) != "s") {
- type += "s"
- }
- type = type.replace("library-", "")
- let id = item.attributes.playParams.catalogId ?? item.attributes.playParams.id ?? item.id
-
- let index = types.findIndex(function (type) {
- return type.type == this
- }, type)
- if (index == -1) {
- types.push({ type: type, id: [id] })
- } else {
- types[index].id.push(id)
- }
- }
- types2 = types.map(function (item) {
- return {
- [`ids[${item.type}]`]: [item.id]
- }
- })
- types2 = types2.reduce(function (result, item) {
- var key = Object.keys(item)[0]; //first property: a, b, c
- result[key] = item[key];
- return result;
- }, {});
- return (await this.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}`, {
- ...{
- "omit[resource]": "autos",
- relate: "library",
- fields: "inLibrary"
- },
- ...types2
- })).data.data
- },
- isInLibrary(playParams) {
- let self = this
- let id = ""
- // ugly code to check if current playback item is in library
- if (typeof playParams == "undefined") {
- return true
- }
- if (playParams["isLibrary"]) {
- return true
- } else if (playParams["catalogId"]) {
- id = playParams["catalogId"]
- } else if (playParams["id"]) {
- id = playParams["id"]
- }
- let found = this.library.songs.listing.filter((item) => {
- if (item["attributes"]) {
- if (item["attributes"]["playParams"] && (item["attributes"]["playParams"]["catalogId"] == id)) {
- return item;
- }
- }
- })
- if (found.length != 0) {
- return true
- } else {
- return false
- }
- },
- mkReady() {
- if (this.mk["nowPlayingItem"]) {
- return true
- } else {
- return false
- }
- },
- getMediaItemArtwork(url, height = 64, width) {
- if (typeof url == "undefined" || url == "") {
- return "https://beta.music.apple.com/assets/product/MissingArtworkMusic.svg"
- }
- let newurl = `${url.replace('{w}', width ?? height).replace('{h}', height).replace('{f}', "webp").replace('{c}', ((width === 900) ? "sr" : "cc"))}`;
-
- if (newurl.includes("900x516")) {
- newurl = newurl.replace("900x516cc", "900x516sr").replace("900x516bb", "900x516sr");
- }
- return newurl
- },
- _rgbToRgb(rgb = [0, 0, 0]) {
- // if rgb
- return `rgb(${rgb[0]},${rgb[1]},${rgb[2]})`
- },
- getNowPlayingArtworkBG(size = 32, force = false) {
- let self = this
- if (typeof this.mk.nowPlayingItem === "undefined") return;
- let bginterval = setInterval(() => {
- if (!this.mkReady()) {
- return ""
- }
-
- try {
- if ((this.mk.nowPlayingItem && this.mk.nowPlayingItem["id"] != this.currentTrackID && document.querySelector('.bg-artwork')) || force) {
- if (document.querySelector('.bg-artwork')) {
- clearInterval(bginterval);
- }
- this.currentTrackID = this.mk.nowPlayingItem["id"];
- document.querySelector('.bg-artwork').src = "";
- if (this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"]) {
- getBase64FromUrl(this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"].replace('{w}', size).replace('{h}', size)).then(img => {
- document.querySelectorAll('.bg-artwork').forEach(artwork => {
- artwork.src = img;
- })
- self.$store.commit("setLCDArtwork", img)
- })
- try {
- clearInterval(bginterval);
- } catch (err) {
- }
- } else {
- this.setLibraryArtBG()
- }
- } else if (this.mk.nowPlayingItem["id"] == this.currentTrackID) {
- try {
- clearInterval(bginterval);
- } catch (err) {
- }
- }
- } catch (e) {
- if (this.mk.nowPlayingItem && this.mk.nowPlayingItem["id"] && document.querySelector('.bg-artwork')) {
- this.setLibraryArtBG()
- try {
- clearInterval(bginterval);
- } catch (err) {
- }
- }
- }
- }, 200)
- },
- async getCurrentArtURL() {
- try {
- let artworkSize = 50
- if (app.getThemeDirective("lcdArtworkSize") != "") {
- artworkSize = app.getThemeDirective("lcdArtworkSize")
- } else if (this.cfg.visual.directives.windowLayout == "twopanel") {
- artworkSize = 70
- }
- this.currentArtUrl = '';
- this.currentArtUrlRaw = '';
- if (app.mk.nowPlayingItem != null && app.mk.nowPlayingItem.attributes != null && app.mk.nowPlayingItem.attributes.artwork != null && app.mk.nowPlayingItem.attributes.artwork.url != null && app.mk.nowPlayingItem.attributes.artwork.url != '') {
- this.currentArtUrlRaw = (this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"] ?? '')
- this.currentArtUrl = (this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"] ?? '').replace('{w}', artworkSize).replace('{h}', artworkSize);
- try {
- document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);
- } catch (e) {
- }
- } else {
- let data = await this.mk.api.v3.music(`/v1/me/library/songs/${this.mk.nowPlayingItem.id}`);
- data = data.data.data[0];
- if (data != null && data !== "" && data.attributes != null && data.attributes.artwork != null) {
- this.currentArtUrlRaw = (this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"] ?? '')
- this.currentArtUrl = (data["attributes"]["artwork"]["url"] ?? '').replace('{w}', artworkSize).replace('{h}', artworkSize);
- ipcRenderer.send('updateRPCImage', this.currentArtUrl ?? '');
- try {
- document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);
- } catch (e) {
- }
- } else {
- this.currentArtUrlRaw = ''
- this.currentArtUrl = '';
- try {
- document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);
- } catch (e) {
- }
- }
- }
- } catch (e) {
-
- }
- },
- async setLibraryArt() {
- if (typeof this.mk.nowPlayingItem === "undefined") return;
- try {
- const data = await this.mk.api.v3.music(`/v1/me/library/songs/${this.mk.nowPlayingItem.id}`);
- data = data.data.data[0];
-
- if (data != null && data !== "") {
- document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', 'url("' + (data["attributes"]["artwork"]["url"]).toString() + '")');
- } else {
- document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("")`);
- }
- } catch (e) {
- }
- },
- async setLibraryArtBG() {
- if (typeof this.mk.nowPlayingItem === "undefined") return;
- try {
- const data = await this.mk.api.v3.music(`/v1/me/library/songs/${this.mk.nowPlayingItem.id}`);
- data = data.data.data[0];
-
- if (data != null && data !== "") {
- getBase64FromUrl((data["attributes"]["artwork"]["url"]).toString()).then(img => {
- document.querySelector('.bg-artwork').forEach(artwork => {
- artwork.src = img;
- })
- self.$store.commit("setLCDArtwork", img)
- })
- }
- } catch (e) {
- }
-
- },
- quickPlay(query) {
- let self = this
- MusicKit.getInstance().api.search(query, { limit: 2, types: 'songs' }).then(function (data) {
- MusicKit.getInstance().setQueue({
- song: data["songs"]['data'][0]["id"],
- parameters: { l: app.mklang }
- }).then(function (queue) {
- MusicKit.getInstance().play()
- setTimeout(() => {
- self.$forceUpdate()
- }, 1000)
- })
- })
- },
- async getRating(item) {
- let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
- let id = item.attributes?.playParams?.catalogId ? item.attributes.playParams.catalogId : (item.attributes?.playParams?.id ?? item.id)
- if (item.id != null && (item.id.toString()).startsWith("i.")) {
- if (!type.startsWith("library-")) {
- type = "library-" + type
- }
- id = item.id
- }
- let response = await this.mk.api.v3.music(`/v1/me/ratings/${type}?platform=web&ids=${type.includes('library') ? item.id : id}`)
- if (response.data.data.length != 0) {
- let value = response.data.data[0].attributes.value
- return value
- } else {
- return 0
- }
- },
- love(item) {
- let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
- let id = item.attributes?.playParams?.catalogId ? item.attributes.playParams.catalogId : (item.attributes?.playParams?.id ?? item.id)
- if (item.id != null && (item.id.toString()).startsWith("i.")) {
- if (!type.startsWith("library-")) {
- type = "library-" + type
- }
- id = item.id
- }
- this.mk.api.v3.music(`/v1/me/ratings/${type}/${id}`, {}, {
- fetchOptions: {
- method: "PUT",
- body: JSON.stringify({
- "type": "rating",
- "attributes": {
- "value": 1
- }
- })
- }
- })
- },
- dislike(item) {
- let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
- let id = item.attributes?.playParams?.catalogId ? item.attributes.playParams.catalogId : (item.attributes?.playParams?.id ?? item.id)
- if (item.id != null && (item.id.toString()).startsWith("i.")) {
- if (!type.startsWith("library-")) {
- type = "library-" + type
- }
- id = item.id
- }
- this.mk.api.v3.music(`/v1/me/ratings/${type}/${id}`, {}, {
- fetchOptions: {
- method: "PUT",
- body: JSON.stringify({
- "type": "rating",
- "attributes": {
- "value": -1
- }
- })
- }
- })
- },
- unlove(item) {
- let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
- let id = item.attributes.playParams.catalogId ? item.attributes.playParams.catalogId : item.id
- if (item.id.startsWith("i.")) {
- if (!type.startsWith("library-")) {
- type = "library-" + type
- }
- id = item.id
- }
- this.mk.api.v3.music(`/v1/me/ratings/${type}/${id}`, {}, {
- fetchOptions: {
- method: "DELETE",
- }
- })
- },
- checkScrollDirectionIsUp(event) {
- if (event.wheelDelta) {
- return event.wheelDelta > 0;
- }
- return event.deltaY < 0;
- },
- volumeUp() {
- if ((app.mk.volume + app.cfg.audio.volumeStep) > 1) {
- app.mk.volume = app.cfg.audio.maxVolume;
- console.log('setting max volume')
- } else {
- console.log('volume up')
- app.mk.volume = ((app.mk.volume * 100) + (app.cfg.audio.volumeStep * 100)) / 100
- }
- },
- volumeDown() {
- if ((app.mk.volume - app.cfg.audio.volumeStep) < 0) {
- app.mk.volume = 0;
- console.log('setting volume to 0')
- } else {
- console.log('volume down')
- app.mk.volume = ((app.mk.volume * 100) - (app.cfg.audio.volumeStep * 100)) / 100
- }
- },
- volumeWheel(event) {
- app.checkScrollDirectionIsUp(event) ? this.volumeUp() : this.volumeDown()
- },
- muteButtonPressed() {
- if (this.cfg.audio.muted) {
- this.mk.volume = this.cfg.audio.lastVolume;
- this.cfg.audio.muted = false;
- } else {
- this.cfg.audio.lastVolume = this.cfg.audio.volume;
- this.mk.volume = 0;
- this.cfg.audio.muted = true;
- }
- },
- checkMuteChange() {
- if (this.cfg.audio.muted) {
- this.cfg.audio.muted = false;
- }
- },
- async apiCall(url, callback) {
- const xmlHttp = new XMLHttpRequest();
-
- xmlHttp.onreadystatechange = (e) => {
- if (xmlHttp.readyState !== 4) {
- return;
- }
-
- if (xmlHttp.status === 200) {
- // console.log('SUCCESS', xmlHttp.responseText);
- callback(JSON.parse(xmlHttp.responseText));
- } else {
- console.warn('request_error');
- }
- };
-
- xmlHttp.open("GET", url);
- xmlHttp.setRequestHeader("Authorization", "Bearer " + MusicKit.getInstance().developerToken);
- xmlHttp.setRequestHeader("Music-User-Token", "" + MusicKit.getInstance().musicUserToken);
- xmlHttp.setRequestHeader("Accept", "application/json");
- xmlHttp.setRequestHeader("Content-Type", "application/json");
- xmlHttp.responseType = "text";
- xmlHttp.send();
- },
- fetchPlaylist(id, callback) {
- // id can be found in playlist.attributes.playParams.globalId
- // this.mk.api.
- this.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/playlists/${id}`).then(res => {
- callback(res.data.data[0])
- })
-
- // tracks are found in relationship.data
- },
- windowFocus(val) {
- if (val) {
- document.querySelectorAll(".animated-artwork-video").forEach(el => {
- el.play()
- })
- document.querySelector("body").classList.remove("stopanimation")
- this.animateBackground = true
- } else {
- document.querySelectorAll(".animated-artwork-video").forEach(el => {
- el.pause()
- })
- document.querySelector("body").classList.add("stopanimation")
- this.animateBackground = false
- }
- },
- async nowPlayingContextMenu(event) {
- let self = this
- let data_type = this.mk.nowPlayingItem.playParams.kind
- let item_id = this.mk.nowPlayingItem.attributes.playParams.id ?? this.mk.nowPlayingItem.id
- let isLibrary = this.mk.nowPlayingItem.attributes.playParams.isLibrary ?? false
- let params = { "fields[songs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library", "t": "1" }
- app.selectedMediaItems = []
- app.select_selectMediaItem(item_id, data_type, 0, '12344', isLibrary)
- let useMenu = "normal"
- let menus = {
- multiple: {
- items: []
- },
- normal: {
- headerItems: [{
- "icon": "./assets/feather/heart.svg",
- "id": "love",
- "name": app.getLz('action.love'),
- "hidden": false,
- "disabled": true,
- "action": function () {
- app.love(app.mk.nowPlayingItem)
- }
- },
- {
- "icon": "./assets/feather/heart.svg",
- "id": "unlove",
- "active": true,
- "name": app.getLz('action.unlove'),
- "hidden": true,
- "action": function () {
- app.unlove(app.mk.nowPlayingItem)
- }
- },
- {
- "icon": "./assets/feather/thumbs-down.svg",
- "id": "dislike",
- "name": app.getLz('action.dislike'),
- "hidden": false,
- "disabled": true,
- "action": function () {
- app.dislike(app.mk.nowPlayingItem)
- }
- },
- {
- "icon": "./assets/feather/thumbs-down.svg",
- "id": "undo_dislike",
- "name": app.getLz('action.undoDislike'),
- "active": true,
- "hidden": true,
- "action": function () {
- app.unlove(app.mk.nowPlayingItem)
- }
- },
- ],
- items: [
- {
- "icon": "./assets/feather/plus.svg",
- "id": "addToLibrary",
- "name": app.getLz('action.addToLibrary') + " ...",
- "disabled": true,
- "action": function () {
- app.addToLibrary(app.mk.nowPlayingItem.id);
- }
- },
- {
- "id": "removeFromLibrary",
- "icon": "./assets/feather/x-circle.svg",
- "name": app.getLz('action.removeFromLibrary'),
- "hidden": true,
- "action": function () {
- self.removeFromLibrary()
- }
- },
- {
- "icon": "./assets/feather/list.svg",
- "name": app.getLz('action.addToPlaylist') + " ...",
- "action": function () {
- app.promptAddToPlaylist()
- }
- },
- {
- "icon": "./assets/feather/radio.svg",
- "name": app.getLz('action.startRadio'),
- "action": function () {
- app.mk.setStationQueue({ song: app.mk.nowPlayingItem.id }).then(() => {
- app.mk.play()
- app.selectedMediaItems = []
- })
- }
- },
- {
- "icon": "./assets/feather/user.svg",
- "name": app.getLz('action.goToArtist'),
- "action": function () {
- app.appRoute(`artist/${app.mk.nowPlayingItem.relationships.artists.data[0].id}`)
- }
- },
- {
- "icon": "./assets/feather/disc.svg",
- "name": app.getLz('action.goToAlbum'),
- "action": function () {
- app.appRoute(`album/${app.mk.nowPlayingItem.relationships.albums.data[0].id}`)
- }
- },
- {
- "icon": "./assets/feather/share.svg",
- "name": app.getLz('action.share'),
- "action": function () {
- app.mkapi(app.mk.nowPlayingItem.attributes?.playParams?.kind ?? app.mk.nowPlayingItem.type ?? 'songs', false, app.mk.nowPlayingItem._songId ?? (app.mk.nowPlayingItem.songId ?? app.mk.nowPlayingItem.id) ?? '').then(u => {
- app.copyToClipboard((u.data.data.length && u.data.data.length > 0) ? u.data.data[0].attributes.url : u.data.data.attributes.url)
- })
- }
- },
- {
- "icon": "./assets/feather/share.svg",
- "name": `${app.getLz('action.share')} (song.link)`,
- "action": function () {
- app.mkapi(app.mk.nowPlayingItem.attributes?.playParams?.kind ?? app.mk.nowPlayingItem.type ?? 'songs', false, app.mk.nowPlayingItem._songId ?? (app.mk.nowPlayingItem.songId ?? app.mk.nowPlayingItem.id) ?? '').then(u => {
- app.songLinkShare((u.data.data.length && u.data.data.length > 0) ? u.data.data[0].attributes.url : u.data.data.attributes.url)
- })
- }
- }
- ]
- }
- }
- if (this.contextExt) {
- if (this.contextExt.normal) {
- menus.normal.items = menus.normal.items.concat(this.contextExt.normal)
- }
- }
- this.showMenuPanel(menus[useMenu], event)
-
- try {
- let result = await this.inLibrary([this.mk.nowPlayingItem])
- if (result[0].attributes.inLibrary) {
- menus.normal.items.find(x => x.id == 'addToLibrary').hidden = true
- menus.normal.items.find(x => x.id == 'removeFromLibrary').hidden = false
- } else {
- menus.normal.items.find(x => x.id == 'addToLibrary').disabled = false
- }
- } catch (e) {
- e = null
- }
-
- try {
- let rating = await app.getRating(app.mk.nowPlayingItem)
- if (rating == 0) {
- menus.normal.headerItems.find(x => x.id == 'love').disabled = false
- menus.normal.headerItems.find(x => x.id == 'dislike').disabled = false
- } else if (rating == 1) {
- menus.normal.headerItems.find(x => x.id == 'unlove').hidden = false
- menus.normal.headerItems.find(x => x.id == 'love').hidden = true
- } else if (rating == -1) {
- menus.normal.headerItems.find(x => x.id == 'undo_dislike').hidden = false
- menus.normal.headerItems.find(x => x.id == 'dislike').hidden = true
- }
- } catch (err) {
-
- }
- },
- LastFMDeauthorize() {
- ipcRenderer.invoke('setStoreValue', 'lastfm.enabled', false).catch((e) => console.error(e));
- ipcRenderer.invoke('setStoreValue', 'lastfm.auth_token', '').catch((e) => console.error(e));
- app.cfg.lastfm.auth_token = "";
- app.cfg.lastfm.enabled = false;
- const element = document.getElementById('lfmConnect');
- element.innerHTML = app.getLz('term.connect');
- element.onclick = app.LastFMAuthenticate;
- },
- LastFMAuthenticate() {
- console.log("[LastFM] Received LastFM authentication callback")
- const element = document.getElementById('lfmConnect');
- // new key : f9986d12aab5a0fe66193c559435ede3
- window.open('https://www.last.fm/api/auth?api_key=f9986d12aab5a0fe66193c559435ede3&cb=cider://auth/lastfm');
- element.innerText = app.getLz('term.connecting') + '...';
-
- /* Just a timeout for the button */
- setTimeout(() => {
- if (element.innerText === app.getLz('term.connecting') + '...') {
- element.innerText = app.getLz('term.connect');
- console.warn('[LastFM] Attempted connection timed out.');
- }
- }, 20000);
-
- ipcRenderer.on('LastfmAuthenticated', function (_event, lfmAuthKey) {
- app.cfg.lastfm.auth_token = lfmAuthKey;
- app.cfg.lastfm.enabled = true;
- element.innerHTML = `${app.getLz('term.disconnect')}\n
(${app.getLz('term.authed')}: ${lfmAuthKey})
`;
- element.onclick = app.LastFMDeauthorize;
- });
- },
- /**
- parseSCTagToRG: function (tag) {
- let soundcheck = tag.split(" ")
- let numbers = []
- for (item of soundcheck) {
- numbers.push(parseInt(item, 16))
-
- }
- numbers.shift()
- //let gain = Math.log10((Math.max(numbers[0], numbers[1]) ?? 1000) / 1000.0) * -10
- let peak = Math.max(numbers[6], numbers[7]) / 32768.0
- let gain = Math.pow(10, ((-7.63 - (Math.log10(peak) * 20)) / 20))// EBU R 128 Compliant
- return {
- gain: gain,
- peak: peak
- }
- },*/
- fullscreen(flag) {
- if (flag) {
- ipcRenderer.send('setFullScreen', true);
- if (app.mk.nowPlayingItem.type && app.mk.nowPlayingItem.type.toLowerCase().includes("video")) {
- document.querySelector('video#apple-music-video-player').requestFullscreen()
- } else {
- app.appMode = 'fullscreen';
- }
- document.addEventListener('keydown', event => {
- if (event.key === 'Escape' && app.appMode === 'fullscreen') {
- this.fullscreen(false);
- }
- });
- } else {
- ipcRenderer.send('setFullScreen', false);
- app.appMode = 'player';
- }
- },
- miniPlayer(flag) {
- if (flag) {
- this.tmpWidth = window.innerWidth;
- this.tmpHeight = window.innerHeight;
- ipcRenderer.send('unmaximize');
- ipcRenderer.send('windowmin', 250, 250)
- ipcRenderer.send('windowresize', 300, 300, false)
- app.appMode = 'mini';
- } else {
- ipcRenderer.send('windowmin', 844, 410)
- ipcRenderer.send('windowresize', this.tmpWidth, this.tmpHeight, false)
- ipcRenderer.send('windowontop', false)
- this.cfg.visual.miniplayer_top_toggle = true;
- app.appMode = 'player';
- }
- },
- pinMiniPlayer() {
- if (this.cfg.visual.miniplayer_top_toggle) {
- ipcRenderer.send('windowontop', true)
- this.cfg.visual.miniplayer_top_toggle = false
- } else {
- ipcRenderer.send('windowontop', false)
- this.cfg.visual.miniplayer_top_toggle = true;
- }
- },
- formatTimezoneOffset: (e = new Date) => {
- let leadingZeros = (e, s = 2) => {
- let n = "" + e;
- for (; n.length < s;)
- n = "0" + n;
- return n
- }
-
- const s = e.getTimezoneOffset(),
- n = Math.floor(Math.abs(s) / 60),
- d = Math.round(Math.abs(s) % 60);
- let h = "+";
- return 0 !== s && (h = s > 0 ? "-" : "+"),
- `${h}${leadingZeros(n, 2)}:${leadingZeros(d, 2)}`
- },
- toggleHideUserInfo() {
- if (this.chrome.hideUserInfo) {
- this.cfg.visual.showuserinfo = true
- this.chrome.hideUserInfo = false
- } else {
- this.cfg.visual.showuserinfo = false
- this.chrome.hideUserInfo = true
- }
- },
- isElementOverflowing(selector) {
- try {
- let element = document.querySelector(selector);
- var overflowX = element.offsetWidth < element.scrollWidth,
- overflowY = element.offsetHeight < element.scrollHeight;
- element.setAttribute('data-value', '\xa0\xa0\xa0\xa0' + element.textContent);
-
- return (overflowX || overflowY);
- } catch (e) {
- return false
- }
- },
- async showWebRemoteQR() {
- //this.webremoteqr = await ipcRenderer.invoke('setRemoteQR','')
- this.webremoteurl = await ipcRenderer.invoke('showQR', '')
- //this.modals.qrcode = true;
- },
- checkMarquee() {
- if (isElementOverflowing('#app-main > div.app-chrome > div.app-chrome--center > div > div > div.playback-info > div.song-artist') == true) {
- document.getElementsByClassName('song-artist')[0].classList.add('marquee');
- document.getElementsByClassName('song-artist')[1].classList.add('marquee-after');
- }
- if (isElementOverflowing('#app-main > div.app-chrome > div.app-chrome--center > div > div > div.playback-info > div.song-name') == true) {
- document.getElementsByClassName('song-name')[0].classList.add('marquee');
- document.getElementsByClassName('song-name')[1].classList.add('marquee-after');
- }
- },
- closeWindow() {
- ipcRenderer.send('close');
- },
- darwinShare(url) {
- ipcRenderer.send('share-menu', url)
- },
- arrayToChunk(arr, chunkSize) {
- let R = [];
- for (let i = 0, len = arr.length; i < len; i += chunkSize) {
- R.push(arr.slice(i, i + chunkSize));
- }
- return R;
- },
- SpacePause() {
- const elems = document.querySelectorAll('input');
- for (elem of elems) {
- if (elem === document.activeElement) {
- return;
- }
- }
- if (!this.isDev) // disable in dev mode to keep my sanity
- MusicKitInterop.playPause();
- },
- async MKJSLang() {
- let u = this.cfg.general.language;
- // use MusicKit.getInstance or crash
- try {
- let item = await MusicKit.getInstance().api.v3.music(`v1/storefronts/${app.mk.storefrontId}`)
- let langcodes = item.data.data[0].attributes.supportedLanguageTags;
- if (langcodes) langcodes = langcodes.map(function (u) {
- return u.replace(/-Han[s|t]/i, "").toLowerCase()
- })
- console.log(langcodes)
- let sellang = ""
- if (u && langcodes.includes(u.toLowerCase().replace('_', "-"))) {
- sellang = ((u.toLowerCase()).replace('_', "-"))
- } else if (u && u.includes('_') && langcodes.includes(((u.toLowerCase()).replace('_', "-")).split("-")[0])) {
- sellang = ((u.toLowerCase()).replace('_', "-")).split("-")[0]
- }
- if (sellang == "") sellang = (item.data.data[0].attributes.defaultLanguageTag).toLowerCase()
-
- // Fix weird locales:
- if (sellang == "iw") sellang = "iw-il"
- sellang = sellang.replace(/-Han[s|t]/i, "").toLowerCase()
-
- console.log(sellang)
- return await sellang
- } catch (err) {
- console.log('locale err', err)
- let langcodes = ['af', 'sq', 'ar', 'eu', 'bg', 'be', 'ca', 'zh', 'zh-tw', 'zh-cn', 'zh-hk', 'zh-sg', 'hr', 'cs', 'da', 'nl', 'nl-be', 'en', 'en-us', 'en-eg', 'en-au', 'en-gb', 'en-ca', 'en-nz', 'en-ie', 'en-za', 'en-jm', 'en-bz', 'en-tt', 'en-001', 'et', 'fo', 'fa', 'fi', 'fr', 'fr-ca', 'gd', 'de', 'de-ch', 'el', 'he', 'hi', 'hu', 'is', 'id', 'it', 'ja', 'ko', 'lv', 'lt', 'mk', 'mt', 'no', 'nb', 'nn', 'pl', 'pt-br', 'pt', 'rm', 'ro', 'ru', 'sr', 'sk', 'sl', 'es', 'es-mx', 'es-419', 'sv', 'th', 'ts', 'tn', 'tr', 'uk', 'ur', 've', 'vi', 'xh', 'yi', 'zu', 'ms', 'iw', 'lo', 'tl', 'kk', 'ta', 'te', 'bn', 'ga', 'ht', 'la', 'pa', 'sa'];
- let sellang = "en"
- if (u && langcodes.includes(u.toLowerCase().replace('_', "-"))) {
- sellang = ((u.toLowerCase()).replace('_', "-"))
- } else if (u && u.includes('_') && langcodes.includes(((u.toLowerCase()).replace('_', "-")).split("-")[0])) {
- sellang = ((u.toLowerCase()).replace('_', "-")).split("-")[0]
- }
- if (sellang.startsWith("en") && this.mk.storefrontId != "us") sellang = "en-gb"
- return await sellang
- }
- },
- skipToNextItem() {
- app.prevButtonBackIndicator = false;
- // app.mk.skipToNextItem() is buggy somehow so use this
- if (this.mk.queue.nextPlayableItemIndex != -1 && this.mk.queue.nextPlayableItemIndex != null)
- this.mk.changeToMediaAtIndex(this.mk.queue.nextPlayableItemIndex);
- },
- skipToPreviousItem() {
- // app.mk.skipToPreviousItem() is buggy somehow so use this
- if (this.mk.queue.previousPlayableItemIndex != -1 && this.mk.queue.previousPlayableItemIndex != null)
- this.mk.changeToMediaAtIndex(this.mk.queue.previousPlayableItemIndex);
- },
- mediaKeyFixes() {
- navigator.mediaSession.setActionHandler('previoustrack', function () {
- app.prevButton()
- });
- navigator.mediaSession.setActionHandler('nexttrack', function () {
- app.skipToNextItem()
- });
- },
- checkForUpdate() {
- ipcRenderer.send('check-for-update')
- ipcRenderer.on('update-response', (event, res) => {
- if (res === "update-not-available") {
- notyf.error(app.getLz(`settings.notyf.updateCider.${res}`))
- } else if (res === "update-downloaded") {
- notyf.success(app.getLz(`settings.notyf.updateCider.${res}`))
- } else if (res === "update-error") {
- notyf.error(app.getLz(`settings.notyf.updateCider.${res}`))
- } else if (res === "update-timeout") {
- notyf.error(app.getLz(`settings.notyf.updateCider.${res}`))
- }
-
- })
- },
- }
-})
-
Vue.component('animated-number', {
template: "{{ displayNumber }}
",
@@ -4546,27 +101,6 @@ Vue.component('sidebar-library-item', {
methods: {}
});
-// Key binds
-document.addEventListener('keydown', function (e) {
- if (e.keyCode === 70 && e.ctrlKey) {
- app.$refs.searchInput.focus()
- app.$refs.searchInput.select()
- }
-});
-
-// Hang Timer
-app.hangtimer = setTimeout(() => {
- if (confirm("Cider is not responding. Reload the app?")) {
- window.location.reload()
- }
-}, 10000)
-
-// add event listener for when window.location.hash changes
-window.addEventListener("hashchange", function () {
- app.appRoute(window.location.hash)
-});
-
-
function fallbackinitMusicKit() {
const request = new XMLHttpRequest();
@@ -4593,6 +127,7 @@ function fallbackinitMusicKit() {
}
document.addEventListener('musickitloaded', function () {
+ console.log('MusicKit loaded')
// MusicKit global is now defined
function initMusicKit() {
let parsedJson = JSON.parse(this.responseText)
@@ -4605,10 +140,9 @@ document.addEventListener('musickitloaded', function () {
},
sourceType: 24,
suppressErrorDialog: true
- });
- setTimeout(() => {
+ }).then(() => {
app.init()
- }, 1000)
+ })
}
@@ -4630,6 +164,7 @@ document.addEventListener('musickitloaded', function () {
});
});
+
if ('serviceWorker' in navigator) {
// Use the window load event to keep the page load performant
window.addEventListener('load', () => {
@@ -4660,19 +195,6 @@ function uuidv4() {
);
}
-function refreshFocus() {
- if (document.hasFocus() == false) {
- app.windowFocus(false)
- } else {
- app.windowFocus(true)
- }
- setTimeout(refreshFocus, 200);
-}
-
-app.getHTMLStyle()
-
-refreshFocus();
-
function xmlToJson(xml) {
// Create the return object
@@ -4767,37 +289,3 @@ window.onerror = function (error) {
console.log(error)
bootbox.alert("Error occured: " + error)
};
-
-
-// Key bind to unjam MusicKit in case it fails: CTRL+F10
-document.addEventListener('keydown', function (event) {
- if (event.ctrlKey && event.keyCode == 121) {
- try {
- app.mk._services.mediaItemPlayback._currentPlayer.stop()
- } catch (e) {
- }
- try {
- app.mk._services.mediaItemPlayback._currentPlayer.destroy()
- } catch (e) {
- }
- }
-});
-
-window.addEventListener("mouseup", (e) => {
- if (e.button === 3) {
- e.preventDefault()
- app.navigateBack()
- } else if (e.button === 4) {
- e.preventDefault()
- app.navigateForward()
- }
-});
-
-document.addEventListener('keydown', function (event) {
- if (event.ctrlKey && event.keyCode == 122) {
- try {
- ipcRenderer.send('detachDT', '')
- } catch (e) {
- }
- }
-});
diff --git a/src/renderer/js/bootbox.min.js b/src/renderer/lib/bootbox.min.js
similarity index 100%
rename from src/renderer/js/bootbox.min.js
rename to src/renderer/lib/bootbox.min.js
diff --git a/src/renderer/js/bootstrap.min.js b/src/renderer/lib/bootstrap.min.js
similarity index 100%
rename from src/renderer/js/bootstrap.min.js
rename to src/renderer/lib/bootstrap.min.js
diff --git a/src/renderer/js/fast-plural-rules.js b/src/renderer/lib/fast-plural-rules.js
similarity index 100%
rename from src/renderer/js/fast-plural-rules.js
rename to src/renderer/lib/fast-plural-rules.js
diff --git a/src/renderer/js/jquery-3.2.1.slim.min.js b/src/renderer/lib/jquery-3.2.1.slim.min.js
similarity index 100%
rename from src/renderer/js/jquery-3.2.1.slim.min.js
rename to src/renderer/lib/jquery-3.2.1.slim.min.js
diff --git a/src/renderer/js/less.js b/src/renderer/lib/less.js
similarity index 100%
rename from src/renderer/js/less.js
rename to src/renderer/lib/less.js
diff --git a/src/renderer/js/notyf.min.js b/src/renderer/lib/notyf.min.js
similarity index 100%
rename from src/renderer/js/notyf.min.js
rename to src/renderer/lib/notyf.min.js
diff --git a/src/renderer/js/popper.min.js b/src/renderer/lib/popper.min.js
similarity index 100%
rename from src/renderer/js/popper.min.js
rename to src/renderer/lib/popper.min.js
diff --git a/src/renderer/lib/resonance-audio.min.js b/src/renderer/lib/resonance-audio.min.js
new file mode 100644
index 00000000..7d4349e1
--- /dev/null
+++ b/src/renderer/lib/resonance-audio.min.js
@@ -0,0 +1,181 @@
+!function(A,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e=t();for(var i in e)("object"==typeof exports?exports:A)[i]=e[i]}}(this,function(){return function(A){function t(i){if(e[i])return e[i].exports;var n=e[i]={i:i,l:!1,exports:{}};return A[i].call(n.exports,n,n.exports,t),n.l=!0,n.exports}var e={};return t.m=A,t.c=e,t.d=function(A,e,i){t.o(A,e)||Object.defineProperty(A,e,{configurable:!1,enumerable:!0,get:i})},t.n=function(A){var e=A&&A.__esModule?function(){return A.default}:function(){return A};return t.d(e,"a",e),e},t.o=function(A,t){return Object.prototype.hasOwnProperty.call(A,t)},t.p="",t(t.s=10)}([function(A,t,e){"use strict";/**
+ * @license
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+function i(){}i.DEFAULT_SOURCE_GAIN=1,i.LISTENER_MAX_OUTSIDE_ROOM_DISTANCE=1,i.SOURCE_MAX_OUTSIDE_ROOM_DISTANCE=1,i.DEFAULT_SOURCE_DISTANCE=1,i.DEFAULT_POSITION=[0,0,0],i.DEFAULT_FORWARD=[0,0,-1],i.DEFAULT_UP=[0,1,0],i.DEFAULT_RIGHT=[1,0,0],i.DEFAULT_SPEED_OF_SOUND=343,i.ATTENUATION_ROLLOFFS=["logarithmic","linear","none"],i.DEFAULT_ATTENUATION_ROLLOFF="logarithmic",i.DEFAULT_MIN_DISTANCE=1,i.DEFAULT_MAX_DISTANCE=1e3,i.DEFAULT_DIRECTIVITY_ALPHA=0,i.DEFAULT_DIRECTIVITY_SHARPNESS=1,i.DEFAULT_AZIMUTH=0,i.DEFAULT_ELEVATION=0,i.DEFAULT_AMBISONIC_ORDER=1,i.DEFAULT_SOURCE_WIDTH=0,i.DEFAULT_REFLECTION_MAX_DURATION=.5,i.DEFAULT_REFLECTION_CUTOFF_FREQUENCY=6400,i.DEFAULT_REFLECTION_COEFFICIENTS={left:0,right:0,front:0,back:0,down:0,up:0},i.DEFAULT_REFLECTION_MIN_DISTANCE=1,i.DEFAULT_ROOM_DIMENSIONS={width:0,height:0,depth:0},i.DEFAULT_REFLECTION_MULTIPLIER=1,i.DEFAULT_REVERB_BANDWIDTH=1,i.DEFAULT_REVERB_DURATION_MULTIPLIER=1,i.DEFAULT_REVERB_PREDELAY=1.5,i.DEFAULT_REVERB_TAIL_ONSET=3.8,i.DEFAULT_REVERB_GAIN=.01,i.DEFAULT_REVERB_MAX_DURATION=3,i.NUMBER_REVERB_FREQUENCY_BANDS=(i.DEFAULT_REVERB_FREQUENCY_BANDS=[31.25,62.5,125,250,500,1e3,2e3,4e3,8e3]).length,i.DEFAULT_REVERB_DURATIONS=new Float32Array(i.NUMBER_REVERB_FREQUENCY_BANDS),i.ROOM_MATERIAL_COEFFICIENTS={transparent:[1,1,1,1,1,1,1,1,1],"acoustic-ceiling-tiles":[.672,.675,.7,.66,.72,.92,.88,.75,1],"brick-bare":[.03,.03,.03,.03,.03,.04,.05,.07,.14],"brick-painted":[.006,.007,.01,.01,.02,.02,.02,.03,.06],"concrete-block-coarse":[.36,.36,.36,.44,.31,.29,.39,.25,.5],"concrete-block-painted":[.092,.09,.1,.05,.06,.07,.09,.08,.16],"curtain-heavy":[.073,.106,.14,.35,.55,.72,.7,.65,1],"fiber-glass-insulation":[.193,.22,.22,.82,.99,.99,.99,.99,1],"glass-thin":[.18,.169,.18,.06,.04,.03,.02,.02,.04],"glass-thick":[.35,.35,.35,.25,.18,.12,.07,.04,.08],grass:[.05,.05,.15,.25,.4,.55,.6,.6,.6],"linoleum-on-concrete":[.02,.02,.02,.03,.03,.03,.03,.02,.04],marble:[.01,.01,.01,.01,.01,.01,.02,.02,.04],metal:[.03,.035,.04,.04,.05,.05,.05,.07,.09],"parquet-on-concrete":[.028,.03,.04,.04,.07,.06,.06,.07,.14],"plaster-rough":[.017,.018,.02,.03,.04,.05,.04,.03,.06],"plaster-smooth":[.011,.012,.013,.015,.02,.03,.04,.05,.1],"plywood-panel":[.4,.34,.28,.22,.17,.09,.1,.11,.22],"polished-concrete-or-tile":[.008,.008,.01,.01,.015,.02,.02,.02,.04],"sheet-rock":[.29,.279,.29,.1,.05,.04,.07,.09,.18],"water-or-ice-surface":[.006,.006,.008,.008,.013,.015,.02,.025,.05],"wood-ceiling":[.15,.147,.15,.11,.1,.07,.06,.07,.14],"wood-panel":[.28,.28,.28,.22,.17,.09,.1,.11,.22],uniform:[.5,.5,.5,.5,.5,.5,.5,.5,.5]},i.DEFAULT_ROOM_MATERIALS={left:"transparent",right:"transparent",front:"transparent",back:"transparent",down:"transparent",up:"transparent"},i.NUMBER_REFLECTION_AVERAGING_BANDS=3,i.ROOM_STARTING_AVERAGING_BAND=4,i.ROOM_MIN_VOLUME=1e-4,i.ROOM_AIR_ABSORPTION_COEFFICIENTS=[6e-4,6e-4,7e-4,8e-4,.001,.0015,.0026,.006,.0207],i.ROOM_EYRING_CORRECTION_COEFFICIENT=1.38,i.TWO_PI=6.28318530717959,i.TWENTY_FOUR_LOG10=55.2620422318571,i.LOG1000=6.90775527898214,i.LOG2_DIV2=.346573590279973,i.DEGREES_TO_RADIANS=.017453292519943,i.RADIANS_TO_DEGREES=57.29577951308232,i.EPSILON_FLOAT=1e-8,i.log=function(){window.console.log.apply(window.console,["%c[ResonanceAudio]%c "+Array.prototype.slice.call(arguments).join(" ")+" %c(@"+performance.now().toFixed(2)+"ms)","background: #BBDEFB; color: #FF5722; font-weight: 700","font-weight: 400","color: #AAA"])},i.normalizeVector=function(A){let e=Math.sqrt(A[0]*A[0]+A[1]*A[1]+A[2]*A[2]);return e>t.EPSILON_FLOAT&&(e=1/e,A[0]*=e,A[1]*=e,A[2]*=e),A},i.crossProduct=function(A,t){return[A[1]*t[2]-A[2]*t[1],A[2]*t[0]-A[0]*t[2],A[0]*t[1]-A[1]*t[0]]},A.exports=i},function(A,t,e){"use strict";function i(A,t){void 0==t&&(t={}),void 0==t.ambisonicOrder&&(t.ambisonicOrder=s.DEFAULT_AMBISONIC_ORDER),void 0==t.azimuth&&(t.azimuth=s.DEFAULT_AZIMUTH),void 0==t.elevation&&(t.elevation=s.DEFAULT_ELEVATION),void 0==t.sourceWidth&&(t.sourceWidth=s.DEFAULT_SOURCE_WIDTH),this._context=A,this.input=A.createGain(),this._channelGain=[],this._merger=void 0,this.output=A.createGain(),this.setAmbisonicOrder(t.ambisonicOrder),this._azimuth=t.azimuth,this._elevation=t.elevation,this.setSourceWidth(t.sourceWidth)}/**
+ * @license
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const n=e(3),s=e(0);i.prototype.setAmbisonicOrder=function(A){this._ambisonicOrder=i.validateAmbisonicOrder(A),this.input.disconnect();for(let e=0;en.SPHERICAL_HARMONICS_MAX_ORDER&&(s.log("Error: Unable to render ambisonic order",options.ambisonicOrder,"(Max order is",n.SPHERICAL_HARMONICS_MAX_ORDER,")\nUsing max order instead."),options.ambisonicOrder=n.SPHERICAL_HARMONICS_MAX_ORDER),A},A.exports=i},function(A,t,e){"use strict";function i(A,t){void 0==t&&(t={}),void 0==t.ambisonicOrder&&(t.ambisonicOrder=o.DEFAULT_AMBISONIC_ORDER),void 0==t.position&&(t.position=o.DEFAULT_POSITION.slice()),void 0==t.forward&&(t.forward=o.DEFAULT_FORWARD.slice()),void 0==t.up&&(t.up=o.DEFAULT_UP.slice()),this.position=new Float32Array(3),this._tempMatrix3=new Float32Array(9),this._ambisonicOrder=s.validateAmbisonicOrder(t.ambisonicOrder),this._context=A,1==this._ambisonicOrder?this._renderer=n.Omnitone.createFOARenderer(A,{}):this._ambisonicOrder>1&&(this._renderer=n.Omnitone.createHOARenderer(A,{ambisonicOrder:this._ambisonicOrder})),this.input=A.createGain(),this.output=A.createGain(),this.ambisonicOutput=A.createGain();let e=this;this._renderer.initialize().then(function(){e.input.connect(e._renderer.input),e._ambisonicOrder>1?e._renderer._hoaRotator.output.connect(e.ambisonicOutput):e._renderer._foaRotator.output.connect(e.ambisonicOutput),e._renderer.output.connect(e.output)}),this.setOrientation(t.forward[0],t.forward[1],t.forward[2],t.up[0],t.up[1],t.up[2])}/**
+ * @license
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const n=e(12),s=e(1),o=e(0);i.prototype.setOrientation=function(A,t,e,i,n,s){let r=o.crossProduct([A,t,e],[i,n,s]);this._tempMatrix3[0]=r[0],this._tempMatrix3[1]=r[1],this._tempMatrix3[2]=r[2],this._tempMatrix3[3]=i,this._tempMatrix3[4]=n,this._tempMatrix3[5]=s,this._tempMatrix3[6]=A,this._tempMatrix3[7]=t,this._tempMatrix3[8]=e,this._renderer.setRotationMatrix3(this._tempMatrix3)},i.prototype.setFromMatrix=function(A){this._renderer.setRotationMatrix4(A.elements),this.position[0]=A.elements[12],this.position[1]=A.elements[13],this.position[2]=A.elements[14]},A.exports=i},function(A,t,e){"use strict";t.SPHERICAL_HARMONICS=[[[0,0,0,1,1,1],[.052336,.034899,.017452,.999848,.999391,.99863],[.104528,.069756,.034899,.999391,.997564,.994522],[.156434,.104528,.052336,.99863,.994522,.987688],[.207912,.139173,.069756,.997564,.990268,.978148],[.258819,.173648,.087156,.996195,.984808,.965926],[.309017,.207912,.104528,.994522,.978148,.951057],[.358368,.241922,.121869,.992546,.970296,.93358],[.406737,.275637,.139173,.990268,.961262,.913545],[.45399,.309017,.156434,.987688,.951057,.891007],[.5,.34202,.173648,.984808,.939693,.866025],[.544639,.374607,.190809,.981627,.927184,.838671],[.587785,.406737,.207912,.978148,.913545,.809017],[.62932,.438371,.224951,.97437,.898794,.777146],[.669131,.469472,.241922,.970296,.882948,.743145],[.707107,.5,.258819,.965926,.866025,.707107],[.743145,.529919,.275637,.961262,.848048,.669131],[.777146,.559193,.292372,.956305,.829038,.62932],[.809017,.587785,.309017,.951057,.809017,.587785],[.838671,.615661,.325568,.945519,.788011,.544639],[.866025,.642788,.34202,.939693,.766044,.5],[.891007,.669131,.358368,.93358,.743145,.45399],[.913545,.694658,.374607,.927184,.71934,.406737],[.93358,.71934,.390731,.920505,.694658,.358368],[.951057,.743145,.406737,.913545,.669131,.309017],[.965926,.766044,.422618,.906308,.642788,.258819],[.978148,.788011,.438371,.898794,.615661,.207912],[.987688,.809017,.45399,.891007,.587785,.156434],[.994522,.829038,.469472,.882948,.559193,.104528],[.99863,.848048,.48481,.87462,.529919,.052336],[1,.866025,.5,.866025,.5,0],[.99863,.882948,.515038,.857167,.469472,-.052336],[.994522,.898794,.529919,.848048,.438371,-.104528],[.987688,.913545,.544639,.838671,.406737,-.156434],[.978148,.927184,.559193,.829038,.374607,-.207912],[.965926,.939693,.573576,.819152,.34202,-.258819],[.951057,.951057,.587785,.809017,.309017,-.309017],[.93358,.961262,.601815,.798636,.275637,-.358368],[.913545,.970296,.615661,.788011,.241922,-.406737],[.891007,.978148,.62932,.777146,.207912,-.45399],[.866025,.984808,.642788,.766044,.173648,-.5],[.838671,.990268,.656059,.75471,.139173,-.544639],[.809017,.994522,.669131,.743145,.104528,-.587785],[.777146,.997564,.681998,.731354,.069756,-.62932],[.743145,.999391,.694658,.71934,.034899,-.669131],[.707107,1,.707107,.707107,0,-.707107],[.669131,.999391,.71934,.694658,-.034899,-.743145],[.62932,.997564,.731354,.681998,-.069756,-.777146],[.587785,.994522,.743145,.669131,-.104528,-.809017],[.544639,.990268,.75471,.656059,-.139173,-.838671],[.5,.984808,.766044,.642788,-.173648,-.866025],[.45399,.978148,.777146,.62932,-.207912,-.891007],[.406737,.970296,.788011,.615661,-.241922,-.913545],[.358368,.961262,.798636,.601815,-.275637,-.93358],[.309017,.951057,.809017,.587785,-.309017,-.951057],[.258819,.939693,.819152,.573576,-.34202,-.965926],[.207912,.927184,.829038,.559193,-.374607,-.978148],[.156434,.913545,.838671,.544639,-.406737,-.987688],[.104528,.898794,.848048,.529919,-.438371,-.994522],[.052336,.882948,.857167,.515038,-.469472,-.99863],[0,.866025,.866025,.5,-.5,-1],[-.052336,.848048,.87462,.48481,-.529919,-.99863],[-.104528,.829038,.882948,.469472,-.559193,-.994522],[-.156434,.809017,.891007,.45399,-.587785,-.987688],[-.207912,.788011,.898794,.438371,-.615661,-.978148],[-.258819,.766044,.906308,.422618,-.642788,-.965926],[-.309017,.743145,.913545,.406737,-.669131,-.951057],[-.358368,.71934,.920505,.390731,-.694658,-.93358],[-.406737,.694658,.927184,.374607,-.71934,-.913545],[-.45399,.669131,.93358,.358368,-.743145,-.891007],[-.5,.642788,.939693,.34202,-.766044,-.866025],[-.544639,.615661,.945519,.325568,-.788011,-.838671],[-.587785,.587785,.951057,.309017,-.809017,-.809017],[-.62932,.559193,.956305,.292372,-.829038,-.777146],[-.669131,.529919,.961262,.275637,-.848048,-.743145],[-.707107,.5,.965926,.258819,-.866025,-.707107],[-.743145,.469472,.970296,.241922,-.882948,-.669131],[-.777146,.438371,.97437,.224951,-.898794,-.62932],[-.809017,.406737,.978148,.207912,-.913545,-.587785],[-.838671,.374607,.981627,.190809,-.927184,-.544639],[-.866025,.34202,.984808,.173648,-.939693,-.5],[-.891007,.309017,.987688,.156434,-.951057,-.45399],[-.913545,.275637,.990268,.139173,-.961262,-.406737],[-.93358,.241922,.992546,.121869,-.970296,-.358368],[-.951057,.207912,.994522,.104528,-.978148,-.309017],[-.965926,.173648,.996195,.087156,-.984808,-.258819],[-.978148,.139173,.997564,.069756,-.990268,-.207912],[-.987688,.104528,.99863,.052336,-.994522,-.156434],[-.994522,.069756,.999391,.034899,-.997564,-.104528],[-.99863,.034899,.999848,.017452,-.999391,-.052336],[-1,0,1,0,-1,-0],[-.99863,-.034899,.999848,-.017452,-.999391,.052336],[-.994522,-.069756,.999391,-.034899,-.997564,.104528],[-.987688,-.104528,.99863,-.052336,-.994522,.156434],[-.978148,-.139173,.997564,-.069756,-.990268,.207912],[-.965926,-.173648,.996195,-.087156,-.984808,.258819],[-.951057,-.207912,.994522,-.104528,-.978148,.309017],[-.93358,-.241922,.992546,-.121869,-.970296,.358368],[-.913545,-.275637,.990268,-.139173,-.961262,.406737],[-.891007,-.309017,.987688,-.156434,-.951057,.45399],[-.866025,-.34202,.984808,-.173648,-.939693,.5],[-.838671,-.374607,.981627,-.190809,-.927184,.544639],[-.809017,-.406737,.978148,-.207912,-.913545,.587785],[-.777146,-.438371,.97437,-.224951,-.898794,.62932],[-.743145,-.469472,.970296,-.241922,-.882948,.669131],[-.707107,-.5,.965926,-.258819,-.866025,.707107],[-.669131,-.529919,.961262,-.275637,-.848048,.743145],[-.62932,-.559193,.956305,-.292372,-.829038,.777146],[-.587785,-.587785,.951057,-.309017,-.809017,.809017],[-.544639,-.615661,.945519,-.325568,-.788011,.838671],[-.5,-.642788,.939693,-.34202,-.766044,.866025],[-.45399,-.669131,.93358,-.358368,-.743145,.891007],[-.406737,-.694658,.927184,-.374607,-.71934,.913545],[-.358368,-.71934,.920505,-.390731,-.694658,.93358],[-.309017,-.743145,.913545,-.406737,-.669131,.951057],[-.258819,-.766044,.906308,-.422618,-.642788,.965926],[-.207912,-.788011,.898794,-.438371,-.615661,.978148],[-.156434,-.809017,.891007,-.45399,-.587785,.987688],[-.104528,-.829038,.882948,-.469472,-.559193,.994522],[-.052336,-.848048,.87462,-.48481,-.529919,.99863],[-0,-.866025,.866025,-.5,-.5,1],[.052336,-.882948,.857167,-.515038,-.469472,.99863],[.104528,-.898794,.848048,-.529919,-.438371,.994522],[.156434,-.913545,.838671,-.544639,-.406737,.987688],[.207912,-.927184,.829038,-.559193,-.374607,.978148],[.258819,-.939693,.819152,-.573576,-.34202,.965926],[.309017,-.951057,.809017,-.587785,-.309017,.951057],[.358368,-.961262,.798636,-.601815,-.275637,.93358],[.406737,-.970296,.788011,-.615661,-.241922,.913545],[.45399,-.978148,.777146,-.62932,-.207912,.891007],[.5,-.984808,.766044,-.642788,-.173648,.866025],[.544639,-.990268,.75471,-.656059,-.139173,.838671],[.587785,-.994522,.743145,-.669131,-.104528,.809017],[.62932,-.997564,.731354,-.681998,-.069756,.777146],[.669131,-.999391,.71934,-.694658,-.034899,.743145],[.707107,-1,.707107,-.707107,-0,.707107],[.743145,-.999391,.694658,-.71934,.034899,.669131],[.777146,-.997564,.681998,-.731354,.069756,.62932],[.809017,-.994522,.669131,-.743145,.104528,.587785],[.838671,-.990268,.656059,-.75471,.139173,.544639],[.866025,-.984808,.642788,-.766044,.173648,.5],[.891007,-.978148,.62932,-.777146,.207912,.45399],[.913545,-.970296,.615661,-.788011,.241922,.406737],[.93358,-.961262,.601815,-.798636,.275637,.358368],[.951057,-.951057,.587785,-.809017,.309017,.309017],[.965926,-.939693,.573576,-.819152,.34202,.258819],[.978148,-.927184,.559193,-.829038,.374607,.207912],[.987688,-.913545,.544639,-.838671,.406737,.156434],[.994522,-.898794,.529919,-.848048,.438371,.104528],[.99863,-.882948,.515038,-.857167,.469472,.052336],[1,-.866025,.5,-.866025,.5,0],[.99863,-.848048,.48481,-.87462,.529919,-.052336],[.994522,-.829038,.469472,-.882948,.559193,-.104528],[.987688,-.809017,.45399,-.891007,.587785,-.156434],[.978148,-.788011,.438371,-.898794,.615661,-.207912],[.965926,-.766044,.422618,-.906308,.642788,-.258819],[.951057,-.743145,.406737,-.913545,.669131,-.309017],[.93358,-.71934,.390731,-.920505,.694658,-.358368],[.913545,-.694658,.374607,-.927184,.71934,-.406737],[.891007,-.669131,.358368,-.93358,.743145,-.45399],[.866025,-.642788,.34202,-.939693,.766044,-.5],[.838671,-.615661,.325568,-.945519,.788011,-.544639],[.809017,-.587785,.309017,-.951057,.809017,-.587785],[.777146,-.559193,.292372,-.956305,.829038,-.62932],[.743145,-.529919,.275637,-.961262,.848048,-.669131],[.707107,-.5,.258819,-.965926,.866025,-.707107],[.669131,-.469472,.241922,-.970296,.882948,-.743145],[.62932,-.438371,.224951,-.97437,.898794,-.777146],[.587785,-.406737,.207912,-.978148,.913545,-.809017],[.544639,-.374607,.190809,-.981627,.927184,-.838671],[.5,-.34202,.173648,-.984808,.939693,-.866025],[.45399,-.309017,.156434,-.987688,.951057,-.891007],[.406737,-.275637,.139173,-.990268,.961262,-.913545],[.358368,-.241922,.121869,-.992546,.970296,-.93358],[.309017,-.207912,.104528,-.994522,.978148,-.951057],[.258819,-.173648,.087156,-.996195,.984808,-.965926],[.207912,-.139173,.069756,-.997564,.990268,-.978148],[.156434,-.104528,.052336,-.99863,.994522,-.987688],[.104528,-.069756,.034899,-.999391,.997564,-.994522],[.052336,-.034899,.017452,-.999848,.999391,-.99863],[0,-0,0,-1,1,-1],[-.052336,.034899,-.017452,-.999848,.999391,-.99863],[-.104528,.069756,-.034899,-.999391,.997564,-.994522],[-.156434,.104528,-.052336,-.99863,.994522,-.987688],[-.207912,.139173,-.069756,-.997564,.990268,-.978148],[-.258819,.173648,-.087156,-.996195,.984808,-.965926],[-.309017,.207912,-.104528,-.994522,.978148,-.951057],[-.358368,.241922,-.121869,-.992546,.970296,-.93358],[-.406737,.275637,-.139173,-.990268,.961262,-.913545],[-.45399,.309017,-.156434,-.987688,.951057,-.891007],[-.5,.34202,-.173648,-.984808,.939693,-.866025],[-.544639,.374607,-.190809,-.981627,.927184,-.838671],[-.587785,.406737,-.207912,-.978148,.913545,-.809017],[-.62932,.438371,-.224951,-.97437,.898794,-.777146],[-.669131,.469472,-.241922,-.970296,.882948,-.743145],[-.707107,.5,-.258819,-.965926,.866025,-.707107],[-.743145,.529919,-.275637,-.961262,.848048,-.669131],[-.777146,.559193,-.292372,-.956305,.829038,-.62932],[-.809017,.587785,-.309017,-.951057,.809017,-.587785],[-.838671,.615661,-.325568,-.945519,.788011,-.544639],[-.866025,.642788,-.34202,-.939693,.766044,-.5],[-.891007,.669131,-.358368,-.93358,.743145,-.45399],[-.913545,.694658,-.374607,-.927184,.71934,-.406737],[-.93358,.71934,-.390731,-.920505,.694658,-.358368],[-.951057,.743145,-.406737,-.913545,.669131,-.309017],[-.965926,.766044,-.422618,-.906308,.642788,-.258819],[-.978148,.788011,-.438371,-.898794,.615661,-.207912],[-.987688,.809017,-.45399,-.891007,.587785,-.156434],[-.994522,.829038,-.469472,-.882948,.559193,-.104528],[-.99863,.848048,-.48481,-.87462,.529919,-.052336],[-1,.866025,-.5,-.866025,.5,0],[-.99863,.882948,-.515038,-.857167,.469472,.052336],[-.994522,.898794,-.529919,-.848048,.438371,.104528],[-.987688,.913545,-.544639,-.838671,.406737,.156434],[-.978148,.927184,-.559193,-.829038,.374607,.207912],[-.965926,.939693,-.573576,-.819152,.34202,.258819],[-.951057,.951057,-.587785,-.809017,.309017,.309017],[-.93358,.961262,-.601815,-.798636,.275637,.358368],[-.913545,.970296,-.615661,-.788011,.241922,.406737],[-.891007,.978148,-.62932,-.777146,.207912,.45399],[-.866025,.984808,-.642788,-.766044,.173648,.5],[-.838671,.990268,-.656059,-.75471,.139173,.544639],[-.809017,.994522,-.669131,-.743145,.104528,.587785],[-.777146,.997564,-.681998,-.731354,.069756,.62932],[-.743145,.999391,-.694658,-.71934,.034899,.669131],[-.707107,1,-.707107,-.707107,0,.707107],[-.669131,.999391,-.71934,-.694658,-.034899,.743145],[-.62932,.997564,-.731354,-.681998,-.069756,.777146],[-.587785,.994522,-.743145,-.669131,-.104528,.809017],[-.544639,.990268,-.75471,-.656059,-.139173,.838671],[-.5,.984808,-.766044,-.642788,-.173648,.866025],[-.45399,.978148,-.777146,-.62932,-.207912,.891007],[-.406737,.970296,-.788011,-.615661,-.241922,.913545],[-.358368,.961262,-.798636,-.601815,-.275637,.93358],[-.309017,.951057,-.809017,-.587785,-.309017,.951057],[-.258819,.939693,-.819152,-.573576,-.34202,.965926],[-.207912,.927184,-.829038,-.559193,-.374607,.978148],[-.156434,.913545,-.838671,-.544639,-.406737,.987688],[-.104528,.898794,-.848048,-.529919,-.438371,.994522],[-.052336,.882948,-.857167,-.515038,-.469472,.99863],[-0,.866025,-.866025,-.5,-.5,1],[.052336,.848048,-.87462,-.48481,-.529919,.99863],[.104528,.829038,-.882948,-.469472,-.559193,.994522],[.156434,.809017,-.891007,-.45399,-.587785,.987688],[.207912,.788011,-.898794,-.438371,-.615661,.978148],[.258819,.766044,-.906308,-.422618,-.642788,.965926],[.309017,.743145,-.913545,-.406737,-.669131,.951057],[.358368,.71934,-.920505,-.390731,-.694658,.93358],[.406737,.694658,-.927184,-.374607,-.71934,.913545],[.45399,.669131,-.93358,-.358368,-.743145,.891007],[.5,.642788,-.939693,-.34202,-.766044,.866025],[.544639,.615661,-.945519,-.325568,-.788011,.838671],[.587785,.587785,-.951057,-.309017,-.809017,.809017],[.62932,.559193,-.956305,-.292372,-.829038,.777146],[.669131,.529919,-.961262,-.275637,-.848048,.743145],[.707107,.5,-.965926,-.258819,-.866025,.707107],[.743145,.469472,-.970296,-.241922,-.882948,.669131],[.777146,.438371,-.97437,-.224951,-.898794,.62932],[.809017,.406737,-.978148,-.207912,-.913545,.587785],[.838671,.374607,-.981627,-.190809,-.927184,.544639],[.866025,.34202,-.984808,-.173648,-.939693,.5],[.891007,.309017,-.987688,-.156434,-.951057,.45399],[.913545,.275637,-.990268,-.139173,-.961262,.406737],[.93358,.241922,-.992546,-.121869,-.970296,.358368],[.951057,.207912,-.994522,-.104528,-.978148,.309017],[.965926,.173648,-.996195,-.087156,-.984808,.258819],[.978148,.139173,-.997564,-.069756,-.990268,.207912],[.987688,.104528,-.99863,-.052336,-.994522,.156434],[.994522,.069756,-.999391,-.034899,-.997564,.104528],[.99863,.034899,-.999848,-.017452,-.999391,.052336],[1,0,-1,-0,-1,0],[.99863,-.034899,-.999848,.017452,-.999391,-.052336],[.994522,-.069756,-.999391,.034899,-.997564,-.104528],[.987688,-.104528,-.99863,.052336,-.994522,-.156434],[.978148,-.139173,-.997564,.069756,-.990268,-.207912],[.965926,-.173648,-.996195,.087156,-.984808,-.258819],[.951057,-.207912,-.994522,.104528,-.978148,-.309017],[.93358,-.241922,-.992546,.121869,-.970296,-.358368],[.913545,-.275637,-.990268,.139173,-.961262,-.406737],[.891007,-.309017,-.987688,.156434,-.951057,-.45399],[.866025,-.34202,-.984808,.173648,-.939693,-.5],[.838671,-.374607,-.981627,.190809,-.927184,-.544639],[.809017,-.406737,-.978148,.207912,-.913545,-.587785],[.777146,-.438371,-.97437,.224951,-.898794,-.62932],[.743145,-.469472,-.970296,.241922,-.882948,-.669131],[.707107,-.5,-.965926,.258819,-.866025,-.707107],[.669131,-.529919,-.961262,.275637,-.848048,-.743145],[.62932,-.559193,-.956305,.292372,-.829038,-.777146],[.587785,-.587785,-.951057,.309017,-.809017,-.809017],[.544639,-.615661,-.945519,.325568,-.788011,-.838671],[.5,-.642788,-.939693,.34202,-.766044,-.866025],[.45399,-.669131,-.93358,.358368,-.743145,-.891007],[.406737,-.694658,-.927184,.374607,-.71934,-.913545],[.358368,-.71934,-.920505,.390731,-.694658,-.93358],[.309017,-.743145,-.913545,.406737,-.669131,-.951057],[.258819,-.766044,-.906308,.422618,-.642788,-.965926],[.207912,-.788011,-.898794,.438371,-.615661,-.978148],[.156434,-.809017,-.891007,.45399,-.587785,-.987688],[.104528,-.829038,-.882948,.469472,-.559193,-.994522],[.052336,-.848048,-.87462,.48481,-.529919,-.99863],[0,-.866025,-.866025,.5,-.5,-1],[-.052336,-.882948,-.857167,.515038,-.469472,-.99863],[-.104528,-.898794,-.848048,.529919,-.438371,-.994522],[-.156434,-.913545,-.838671,.544639,-.406737,-.987688],[-.207912,-.927184,-.829038,.559193,-.374607,-.978148],[-.258819,-.939693,-.819152,.573576,-.34202,-.965926],[-.309017,-.951057,-.809017,.587785,-.309017,-.951057],[-.358368,-.961262,-.798636,.601815,-.275637,-.93358],[-.406737,-.970296,-.788011,.615661,-.241922,-.913545],[-.45399,-.978148,-.777146,.62932,-.207912,-.891007],[-.5,-.984808,-.766044,.642788,-.173648,-.866025],[-.544639,-.990268,-.75471,.656059,-.139173,-.838671],[-.587785,-.994522,-.743145,.669131,-.104528,-.809017],[-.62932,-.997564,-.731354,.681998,-.069756,-.777146],[-.669131,-.999391,-.71934,.694658,-.034899,-.743145],[-.707107,-1,-.707107,.707107,-0,-.707107],[-.743145,-.999391,-.694658,.71934,.034899,-.669131],[-.777146,-.997564,-.681998,.731354,.069756,-.62932],[-.809017,-.994522,-.669131,.743145,.104528,-.587785],[-.838671,-.990268,-.656059,.75471,.139173,-.544639],[-.866025,-.984808,-.642788,.766044,.173648,-.5],[-.891007,-.978148,-.62932,.777146,.207912,-.45399],[-.913545,-.970296,-.615661,.788011,.241922,-.406737],[-.93358,-.961262,-.601815,.798636,.275637,-.358368],[-.951057,-.951057,-.587785,.809017,.309017,-.309017],[-.965926,-.939693,-.573576,.819152,.34202,-.258819],[-.978148,-.927184,-.559193,.829038,.374607,-.207912],[-.987688,-.913545,-.544639,.838671,.406737,-.156434],[-.994522,-.898794,-.529919,.848048,.438371,-.104528],[-.99863,-.882948,-.515038,.857167,.469472,-.052336],[-1,-.866025,-.5,.866025,.5,-0],[-.99863,-.848048,-.48481,.87462,.529919,.052336],[-.994522,-.829038,-.469472,.882948,.559193,.104528],[-.987688,-.809017,-.45399,.891007,.587785,.156434],[-.978148,-.788011,-.438371,.898794,.615661,.207912],[-.965926,-.766044,-.422618,.906308,.642788,.258819],[-.951057,-.743145,-.406737,.913545,.669131,.309017],[-.93358,-.71934,-.390731,.920505,.694658,.358368],[-.913545,-.694658,-.374607,.927184,.71934,.406737],[-.891007,-.669131,-.358368,.93358,.743145,.45399],[-.866025,-.642788,-.34202,.939693,.766044,.5],[-.838671,-.615661,-.325568,.945519,.788011,.544639],[-.809017,-.587785,-.309017,.951057,.809017,.587785],[-.777146,-.559193,-.292372,.956305,.829038,.62932],[-.743145,-.529919,-.275637,.961262,.848048,.669131],[-.707107,-.5,-.258819,.965926,.866025,.707107],[-.669131,-.469472,-.241922,.970296,.882948,.743145],[-.62932,-.438371,-.224951,.97437,.898794,.777146],[-.587785,-.406737,-.207912,.978148,.913545,.809017],[-.544639,-.374607,-.190809,.981627,.927184,.838671],[-.5,-.34202,-.173648,.984808,.939693,.866025],[-.45399,-.309017,-.156434,.987688,.951057,.891007],[-.406737,-.275637,-.139173,.990268,.961262,.913545],[-.358368,-.241922,-.121869,.992546,.970296,.93358],[-.309017,-.207912,-.104528,.994522,.978148,.951057],[-.258819,-.173648,-.087156,.996195,.984808,.965926],[-.207912,-.139173,-.069756,.997564,.990268,.978148],[-.156434,-.104528,-.052336,.99863,.994522,.987688],[-.104528,-.069756,-.034899,.999391,.997564,.994522],[-.052336,-.034899,-.017452,.999848,.999391,.99863]],[[-1,-0,1,-0,0,-1,-0,0,-0],[-.999848,.017452,.999543,-.030224,264e-6,-.999086,.042733,-59e-5,4e-6],[-.999391,.034899,.998173,-.060411,.001055,-.996348,.085356,-.002357,34e-6],[-.99863,.052336,.995891,-.090524,.002372,-.991791,.127757,-.005297,113e-6],[-.997564,.069756,.992701,-.120527,.004214,-.985429,.169828,-.0094,268e-6],[-.996195,.087156,.988606,-.150384,.006578,-.977277,.21146,-.014654,523e-6],[-.994522,.104528,.983611,-.180057,.009462,-.967356,.252544,-.021043,903e-6],[-.992546,.121869,.977722,-.209511,.012862,-.955693,.292976,-.028547,.001431],[-.990268,.139173,.970946,-.238709,.016774,-.942316,.332649,-.037143,.002131],[-.987688,.156434,.963292,-.267617,.021193,-.927262,.371463,-.046806,.003026],[-.984808,.173648,.954769,-.296198,.026114,-.910569,.409317,-.057505,.00414],[-.981627,.190809,.945388,-.324419,.03153,-.892279,.446114,-.069209,.005492],[-.978148,.207912,.935159,-.352244,.037436,-.872441,.481759,-.08188,.007105],[-.97437,.224951,.924096,-.379641,.043823,-.851105,.516162,-.095481,.008999],[-.970296,.241922,.912211,-.406574,.050685,-.828326,.549233,-.109969,.011193],[-.965926,.258819,.899519,-.433013,.058013,-.804164,.580889,-.1253,.013707],[-.961262,.275637,.886036,-.458924,.065797,-.77868,.61105,-.141427,.016556],[-.956305,.292372,.871778,-.484275,.074029,-.75194,.639639,-.158301,.019758],[-.951057,.309017,.856763,-.509037,.082698,-.724012,.666583,-.175868,.023329],[-.945519,.325568,.841008,-.533178,.091794,-.694969,.691816,-.194075,.027281],[-.939693,.34202,.824533,-.55667,.101306,-.664885,.715274,-.212865,.03163],[-.93358,.358368,.807359,-.579484,.111222,-.633837,.736898,-.23218,.036385],[-.927184,.374607,.789505,-.601592,.121529,-.601904,.756637,-.25196,.041559],[-.920505,.390731,.770994,-.622967,.132217,-.569169,.774442,-.272143,.04716],[-.913545,.406737,.751848,-.643582,.143271,-.535715,.79027,-.292666,.053196],[-.906308,.422618,.732091,-.663414,.154678,-.501627,.804083,-.313464,.059674],[-.898794,.438371,.711746,-.682437,.166423,-.466993,.81585,-.334472,.066599],[-.891007,.45399,.690839,-.700629,.178494,-.431899,.825544,-.355623,.073974],[-.882948,.469472,.669395,-.717968,.190875,-.396436,.833145,-.376851,.081803],[-.87462,.48481,.647439,-.734431,.203551,-.360692,.838638,-.398086,.090085],[-.866025,.5,.625,-.75,.216506,-.32476,.842012,-.419263,.098821],[-.857167,.515038,.602104,-.764655,.229726,-.288728,.843265,-.440311,.108009],[-.848048,.529919,.578778,-.778378,.243192,-.252688,.842399,-.461164,.117644],[-.838671,.544639,.555052,-.791154,.256891,-.21673,.839422,-.481753,.127722],[-.829038,.559193,.530955,-.802965,.270803,-.180944,.834347,-.502011,.138237],[-.819152,.573576,.506515,-.813798,.284914,-.14542,.827194,-.521871,.149181],[-.809017,.587785,.481763,-.823639,.299204,-.110246,.817987,-.541266,.160545],[-.798636,.601815,.456728,-.832477,.313658,-.075508,.806757,-.560132,.172317],[-.788011,.615661,.431441,-.840301,.328257,-.041294,.793541,-.578405,.184487],[-.777146,.62932,.405934,-.847101,.342984,-.007686,.778379,-.596021,.19704],[-.766044,.642788,.380236,-.852869,.357821,.025233,.761319,-.612921,.209963],[-.75471,.656059,.35438,-.857597,.372749,.057383,.742412,-.629044,.223238],[-.743145,.669131,.328396,-.861281,.387751,.088686,.721714,-.644334,.23685],[-.731354,.681998,.302317,-.863916,.402807,.119068,.699288,-.658734,.250778],[-.71934,.694658,.276175,-.865498,.417901,.148454,.675199,-.67219,.265005],[-.707107,.707107,.25,-.866025,.433013,.176777,.649519,-.684653,.279508],[-.694658,.71934,.223825,-.865498,.448125,.203969,.622322,-.696073,.294267],[-.681998,.731354,.197683,-.863916,.463218,.229967,.593688,-.706405,.309259],[-.669131,.743145,.171604,-.861281,.478275,.254712,.5637,-.715605,.324459],[-.656059,.75471,.14562,-.857597,.493276,.278147,.532443,-.723633,.339844],[-.642788,.766044,.119764,-.852869,.508205,.300221,.500009,-.730451,.355387],[-.62932,.777146,.094066,-.847101,.523041,.320884,.46649,-.736025,.371063],[-.615661,.788011,.068559,-.840301,.537768,.340093,.431982,-.740324,.386845],[-.601815,.798636,.043272,-.832477,.552367,.357807,.396584,-.74332,.402704],[-.587785,.809017,.018237,-.823639,.566821,.373991,.360397,-.744989,.418613],[-.573576,.819152,-.006515,-.813798,.581112,.388612,.323524,-.745308,.434544],[-.559193,.829038,-.030955,-.802965,.595222,.401645,.286069,-.744262,.450467],[-.544639,.838671,-.055052,-.791154,.609135,.413066,.24814,-.741835,.466352],[-.529919,.848048,-.078778,-.778378,.622833,.422856,.209843,-.738017,.482171],[-.515038,.857167,-.102104,-.764655,.6363,.431004,.171288,-.732801,.497894],[-.5,.866025,-.125,-.75,.649519,.4375,.132583,-.726184,.51349],[-.48481,.87462,-.147439,-.734431,.662474,.44234,.093837,-.718167,.528929],[-.469472,.882948,-.169395,-.717968,.67515,.445524,.05516,-.708753,.544183],[-.45399,.891007,-.190839,-.700629,.687531,.447059,.016662,-.69795,.55922],[-.438371,.898794,-.211746,-.682437,.699602,.446953,-.02155,-.685769,.574011],[-.422618,.906308,-.232091,-.663414,.711348,.445222,-.059368,-.672226,.588528],[-.406737,.913545,-.251848,-.643582,.722755,.441884,-.096684,-.657339,.602741],[-.390731,.920505,-.270994,-.622967,.733809,.436964,-.133395,-.64113,.616621],[-.374607,.927184,-.289505,-.601592,.744496,.430488,-.169397,-.623624,.630141],[-.358368,.93358,-.307359,-.579484,.754804,.422491,-.204589,-.604851,.643273],[-.34202,.939693,-.324533,-.55667,.76472,.413008,-.238872,-.584843,.65599],[-.325568,.945519,-.341008,-.533178,.774231,.402081,-.27215,-.563635,.668267],[-.309017,.951057,-.356763,-.509037,.783327,.389754,-.304329,-.541266,.680078],[-.292372,.956305,-.371778,-.484275,.791997,.376077,-.335319,-.517778,.691399],[-.275637,.961262,-.386036,-.458924,.800228,.361102,-.365034,-.493216,.702207],[-.258819,.965926,-.399519,-.433013,.808013,.344885,-.393389,-.467627,.712478],[-.241922,.970296,-.412211,-.406574,.81534,.327486,-.420306,-.441061,.722191],[-.224951,.97437,-.424096,-.379641,.822202,.308969,-.445709,-.413572,.731327],[-.207912,.978148,-.435159,-.352244,.828589,.289399,-.469527,-.385215,.739866],[-.190809,.981627,-.445388,-.324419,.834495,.268846,-.491693,-.356047,.74779],[-.173648,.984808,-.454769,-.296198,.839912,.247382,-.512145,-.326129,.755082],[-.156434,.987688,-.463292,-.267617,.844832,.225081,-.530827,-.295521,.761728],[-.139173,.990268,-.470946,-.238709,.849251,.20202,-.547684,-.264287,.767712],[-.121869,.992546,-.477722,-.209511,.853163,.178279,-.562672,-.232494,.773023],[-.104528,.994522,-.483611,-.180057,.856563,.153937,-.575747,-.200207,.777648],[-.087156,.996195,-.488606,-.150384,.859447,.129078,-.586872,-.167494,.781579],[-.069756,.997564,-.492701,-.120527,.861811,.103786,-.596018,-.134426,.784806],[-.052336,.99863,-.495891,-.090524,.863653,.078146,-.603158,-.101071,.787324],[-.034899,.999391,-.498173,-.060411,.864971,.052243,-.608272,-.0675,.789126],[-.017452,.999848,-.499543,-.030224,.865762,.026165,-.611347,-.033786,.790208],[0,1,-.5,0,.866025,-0,-.612372,0,.790569],[.017452,.999848,-.499543,.030224,.865762,-.026165,-.611347,.033786,.790208],[.034899,.999391,-.498173,.060411,.864971,-.052243,-.608272,.0675,.789126],[.052336,.99863,-.495891,.090524,.863653,-.078146,-.603158,.101071,.787324],[.069756,.997564,-.492701,.120527,.861811,-.103786,-.596018,.134426,.784806],[.087156,.996195,-.488606,.150384,.859447,-.129078,-.586872,.167494,.781579],[.104528,.994522,-.483611,.180057,.856563,-.153937,-.575747,.200207,.777648],[.121869,.992546,-.477722,.209511,.853163,-.178279,-.562672,.232494,.773023],[.139173,.990268,-.470946,.238709,.849251,-.20202,-.547684,.264287,.767712],[.156434,.987688,-.463292,.267617,.844832,-.225081,-.530827,.295521,.761728],[.173648,.984808,-.454769,.296198,.839912,-.247382,-.512145,.326129,.755082],[.190809,.981627,-.445388,.324419,.834495,-.268846,-.491693,.356047,.74779],[.207912,.978148,-.435159,.352244,.828589,-.289399,-.469527,.385215,.739866],[.224951,.97437,-.424096,.379641,.822202,-.308969,-.445709,.413572,.731327],[.241922,.970296,-.412211,.406574,.81534,-.327486,-.420306,.441061,.722191],[.258819,.965926,-.399519,.433013,.808013,-.344885,-.393389,.467627,.712478],[.275637,.961262,-.386036,.458924,.800228,-.361102,-.365034,.493216,.702207],[.292372,.956305,-.371778,.484275,.791997,-.376077,-.335319,.517778,.691399],[.309017,.951057,-.356763,.509037,.783327,-.389754,-.304329,.541266,.680078],[.325568,.945519,-.341008,.533178,.774231,-.402081,-.27215,.563635,.668267],[.34202,.939693,-.324533,.55667,.76472,-.413008,-.238872,.584843,.65599],[.358368,.93358,-.307359,.579484,.754804,-.422491,-.204589,.604851,.643273],[.374607,.927184,-.289505,.601592,.744496,-.430488,-.169397,.623624,.630141],[.390731,.920505,-.270994,.622967,.733809,-.436964,-.133395,.64113,.616621],[.406737,.913545,-.251848,.643582,.722755,-.441884,-.096684,.657339,.602741],[.422618,.906308,-.232091,.663414,.711348,-.445222,-.059368,.672226,.588528],[.438371,.898794,-.211746,.682437,.699602,-.446953,-.02155,.685769,.574011],[.45399,.891007,-.190839,.700629,.687531,-.447059,.016662,.69795,.55922],[.469472,.882948,-.169395,.717968,.67515,-.445524,.05516,.708753,.544183],[.48481,.87462,-.147439,.734431,.662474,-.44234,.093837,.718167,.528929],[.5,.866025,-.125,.75,.649519,-.4375,.132583,.726184,.51349],[.515038,.857167,-.102104,.764655,.6363,-.431004,.171288,.732801,.497894],[.529919,.848048,-.078778,.778378,.622833,-.422856,.209843,.738017,.482171],[.544639,.838671,-.055052,.791154,.609135,-.413066,.24814,.741835,.466352],[.559193,.829038,-.030955,.802965,.595222,-.401645,.286069,.744262,.450467],[.573576,.819152,-.006515,.813798,.581112,-.388612,.323524,.745308,.434544],[.587785,.809017,.018237,.823639,.566821,-.373991,.360397,.744989,.418613],[.601815,.798636,.043272,.832477,.552367,-.357807,.396584,.74332,.402704],[.615661,.788011,.068559,.840301,.537768,-.340093,.431982,.740324,.386845],[.62932,.777146,.094066,.847101,.523041,-.320884,.46649,.736025,.371063],[.642788,.766044,.119764,.852869,.508205,-.300221,.500009,.730451,.355387],[.656059,.75471,.14562,.857597,.493276,-.278147,.532443,.723633,.339844],[.669131,.743145,.171604,.861281,.478275,-.254712,.5637,.715605,.324459],[.681998,.731354,.197683,.863916,.463218,-.229967,.593688,.706405,.309259],[.694658,.71934,.223825,.865498,.448125,-.203969,.622322,.696073,.294267],[.707107,.707107,.25,.866025,.433013,-.176777,.649519,.684653,.279508],[.71934,.694658,.276175,.865498,.417901,-.148454,.675199,.67219,.265005],[.731354,.681998,.302317,.863916,.402807,-.119068,.699288,.658734,.250778],[.743145,.669131,.328396,.861281,.387751,-.088686,.721714,.644334,.23685],[.75471,.656059,.35438,.857597,.372749,-.057383,.742412,.629044,.223238],[.766044,.642788,.380236,.852869,.357821,-.025233,.761319,.612921,.209963],[.777146,.62932,.405934,.847101,.342984,.007686,.778379,.596021,.19704],[.788011,.615661,.431441,.840301,.328257,.041294,.793541,.578405,.184487],[.798636,.601815,.456728,.832477,.313658,.075508,.806757,.560132,.172317],[.809017,.587785,.481763,.823639,.299204,.110246,.817987,.541266,.160545],[.819152,.573576,.506515,.813798,.284914,.14542,.827194,.521871,.149181],[.829038,.559193,.530955,.802965,.270803,.180944,.834347,.502011,.138237],[.838671,.544639,.555052,.791154,.256891,.21673,.839422,.481753,.127722],[.848048,.529919,.578778,.778378,.243192,.252688,.842399,.461164,.117644],[.857167,.515038,.602104,.764655,.229726,.288728,.843265,.440311,.108009],[.866025,.5,.625,.75,.216506,.32476,.842012,.419263,.098821],[.87462,.48481,.647439,.734431,.203551,.360692,.838638,.398086,.090085],[.882948,.469472,.669395,.717968,.190875,.396436,.833145,.376851,.081803],[.891007,.45399,.690839,.700629,.178494,.431899,.825544,.355623,.073974],[.898794,.438371,.711746,.682437,.166423,.466993,.81585,.334472,.066599],[.906308,.422618,.732091,.663414,.154678,.501627,.804083,.313464,.059674],[.913545,.406737,.751848,.643582,.143271,.535715,.79027,.292666,.053196],[.920505,.390731,.770994,.622967,.132217,.569169,.774442,.272143,.04716],[.927184,.374607,.789505,.601592,.121529,.601904,.756637,.25196,.041559],[.93358,.358368,.807359,.579484,.111222,.633837,.736898,.23218,.036385],[.939693,.34202,.824533,.55667,.101306,.664885,.715274,.212865,.03163],[.945519,.325568,.841008,.533178,.091794,.694969,.691816,.194075,.027281],[.951057,.309017,.856763,.509037,.082698,.724012,.666583,.175868,.023329],[.956305,.292372,.871778,.484275,.074029,.75194,.639639,.158301,.019758],[.961262,.275637,.886036,.458924,.065797,.77868,.61105,.141427,.016556],[.965926,.258819,.899519,.433013,.058013,.804164,.580889,.1253,.013707],[.970296,.241922,.912211,.406574,.050685,.828326,.549233,.109969,.011193],[.97437,.224951,.924096,.379641,.043823,.851105,.516162,.095481,.008999],[.978148,.207912,.935159,.352244,.037436,.872441,.481759,.08188,.007105],[.981627,.190809,.945388,.324419,.03153,.892279,.446114,.069209,.005492],[.984808,.173648,.954769,.296198,.026114,.910569,.409317,.057505,.00414],[.987688,.156434,.963292,.267617,.021193,.927262,.371463,.046806,.003026],[.990268,.139173,.970946,.238709,.016774,.942316,.332649,.037143,.002131],[.992546,.121869,.977722,.209511,.012862,.955693,.292976,.028547,.001431],[.994522,.104528,.983611,.180057,.009462,.967356,.252544,.021043,903e-6],[.996195,.087156,.988606,.150384,.006578,.977277,.21146,.014654,523e-6],[.997564,.069756,.992701,.120527,.004214,.985429,.169828,.0094,268e-6],[.99863,.052336,.995891,.090524,.002372,.991791,.127757,.005297,113e-6],[.999391,.034899,.998173,.060411,.001055,.996348,.085356,.002357,34e-6],[.999848,.017452,.999543,.030224,264e-6,.999086,.042733,59e-5,4e-6],[1,-0,1,-0,0,1,-0,0,-0]]],t.SPHERICAL_HARMONICS_AZIMUTH_RESOLUTION=t.SPHERICAL_HARMONICS[0].length,t.SPHERICAL_HARMONICS_ELEVATION_RESOLUTION=t.SPHERICAL_HARMONICS[1].length,t.SPHERICAL_HARMONICS_MAX_ORDER=t.SPHERICAL_HARMONICS[0][0].length/2,t.MAX_RE_WEIGHTS=[[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1],[1.003236,1.002156,.999152,.990038],[1.03237,1.021194,.990433,.898572],[1.062694,1.040231,.979161,.799806],[1.093999,1.058954,.964976,.693603],[1.126003,1.077006,.947526,.57989],[1.158345,1.093982,.926474,.45869],[1.19059,1.109437,.901512,.330158],[1.222228,1.12289,.87237,.194621],[1.252684,1.133837,.838839,.052614],[1.281987,1.142358,.801199,0],[1.312073,1.150207,.760839,0],[1.343011,1.157424,.717799,0],[1.374649,1.163859,.671999,0],[1.406809,1.169354,.623371,0],[1.439286,1.173739,.571868,0],[1.471846,1.176837,.517465,0],[1.504226,1.178465,.460174,0],[1.536133,1.178438,.400043,0],[1.567253,1.176573,.337165,0],[1.597247,1.172695,.271688,0],[1.625766,1.166645,.203815,0],[1.652455,1.158285,.133806,0],[1.676966,1.147506,.061983,0],[1.699006,1.134261,0,0],[1.720224,1.119789,0,0],[1.741631,1.10481,0,0],[1.763183,1.08933,0,0],[1.784837,1.073356,0,0],[1.806548,1.056898,0,0],[1.828269,1.039968,0,0],[1.849952,1.02258,0,0],[1.871552,1.004752,0,0],[1.893018,.986504,0,0],[1.914305,.967857,0,0],[1.935366,.948837,0,0],[1.956154,.929471,0,0],[1.976625,.90979,0,0],[1.996736,.889823,0,0],[2.016448,.869607,0,0],[2.035721,.849175,0,0],[2.054522,.828565,0,0],[2.072818,.807816,0,0],[2.090581,.786964,0,0],[2.107785,.766051,0,0],[2.124411,.745115,0,0],[2.140439,.724196,0,0],[2.155856,.703332,0,0],[2.170653,.682561,0,0],[2.184823,.661921,0,0],[2.198364,.641445,0,0],[2.211275,.621169,0,0],[2.223562,.601125,0,0],[2.23523,.581341,0,0],[2.246289,.561847,0,0],[2.256751,.542667,0,0],[2.266631,.523826,0,0],[2.275943,.505344,0,0],[2.284707,.487239,0,0],[2.292939,.469528,0,0],[2.300661,.452225,0,0],[2.307892,.435342,0,0],[2.314654,.418888,0,0],[2.320969,.40287,0,0],[2.326858,.387294,0,0],[2.332343,.372164,0,0],[2.337445,.357481,0,0],[2.342186,.343246,0,0],[2.346585,.329458,0,0],[2.350664,.316113,0,0],[2.354442,.303208,0,0],[2.357937,.290738,0,0],[2.361168,.278698,0,0],[2.364152,.26708,0,0],[2.366906,.255878,0,0],[2.369446,.245082,0,0],[2.371786,.234685,0,0],[2.37394,.224677,0,0],[2.375923,.215048,0,0],[2.377745,.20579,0,0],[2.379421,.196891,0,0],[2.380959,.188342,0,0],[2.382372,.180132,0,0],[2.383667,.172251,0,0],[2.384856,.164689,0,0],[2.385945,.157435,0,0],[2.386943,.150479,0,0],[2.387857,.143811,0,0],[2.388694,.137421,0,0],[2.38946,.131299,0,0],[2.39016,.125435,0,0],[2.390801,.11982,0,0],[2.391386,.114445,0,0],[2.391921,.1093,0,0],[2.39241,.104376,0,0],[2.392857,.099666,0,0],[2.393265,.09516,0,0],[2.393637,.090851,0,0],[2.393977,.086731,0,0],[2.394288,.082791,0,0],[2.394571,.079025,0,0],[2.394829,.075426,0,0],[2.395064,.071986,0,0],[2.395279,.068699,0,0],[2.395475,.065558,0,0],[2.395653,.062558,0,0],[2.395816,.059693,0,0],[2.395964,.056955,0,0],[2.396099,.054341,0,0],[2.396222,.051845,0,0],[2.396334,.049462,0,0],[2.396436,.047186,0,0],[2.396529,.045013,0,0],[2.396613,.042939,0,0],[2.396691,.040959,0,0],[2.396761,.039069,0,0],[2.396825,.037266,0,0],[2.396883,.035544,0,0],[2.396936,.033901,0,0],[2.396984,.032334,0,0],[2.397028,.030838,0,0],[2.397068,.02941,0,0],[2.397104,.028048,0,0],[2.397137,.026749,0,0],[2.397167,.025509,0,0],[2.397194,.024326,0,0],[2.397219,.023198,0,0],[2.397242,.022122,0,0],[2.397262,.021095,0,0],[2.397281,.020116,0,0],[2.397298,.019181,0,0],[2.397314,.01829,0,0],[2.397328,.017441,0,0],[2.397341,.01663,0,0],[2.397352,.015857,0,0],[2.397363,.015119,0,0],[2.397372,.014416,0,0],[2.397381,.013745,0,0],[2.397389,.013106,0,0],[2.397396,.012496,0,0],[2.397403,.011914,0,0],[2.397409,.01136,0,0],[2.397414,.010831,0,0],[2.397419,.010326,0,0],[2.397424,.009845,0,0],[2.397428,.009387,0,0],[2.397432,.008949,0,0],[2.397435,.008532,0,0],[2.397438,.008135,0,0],[2.397441,.007755,0,0],[2.397443,.007394,0,0],[2.397446,.007049,0,0],[2.397448,.006721,0,0],[2.39745,.006407,0,0],[2.397451,.006108,0,0],[2.397453,.005824,0,0],[2.397454,.005552,0,0],[2.397456,.005293,0,0],[2.397457,.005046,0,0],[2.397458,.004811,0,0],[2.397459,.004586,0,0],[2.39746,.004372,0,0],[2.397461,.004168,0,0],[2.397461,.003974,0,0],[2.397462,.003788,0,0],[2.397463,.003611,0,0],[2.397463,.003443,0,0],[2.397464,.003282,0,0],[2.397464,.003129,0,0],[2.397465,.002983,0,0],[2.397465,.002844,0,0],[2.397465,.002711,0,0],[2.397466,.002584,0,0],[2.397466,.002464,0,0],[2.397466,.002349,0,0],[2.397466,.002239,0,0],[2.397467,.002135,0,0],[2.397467,.002035,0,0],[2.397467,.00194,0,0],[2.397467,.001849,0,0],[2.397467,.001763,0,0],[2.397467,.001681,0,0],[2.397468,.001602,0,0],[2.397468,.001527,0,0],[2.397468,.001456,0,0],[2.397468,.001388,0,0],[2.397468,.001323,0,0],[2.397468,.001261,0,0],[2.397468,.001202,0,0],[2.397468,.001146,0,0],[2.397468,.001093,0,0],[2.397468,.001042,0,0],[2.397468,993e-6,0,0],[2.397468,947e-6,0,0],[2.397468,902e-6,0,0],[2.397468,86e-5,0,0],[2.397468,82e-5,0,0],[2.397469,782e-6,0,0],[2.397469,745e-6,0,0],[2.397469,71e-5,0,0],[2.397469,677e-6,0,0],[2.397469,646e-6,0,0],[2.397469,616e-6,0,0],[2.397469,587e-6,0,0],[2.397469,559e-6,0,0],[2.397469,533e-6,0,0],[2.397469,508e-6,0,0],[2.397469,485e-6,0,0],[2.397469,462e-6,0,0],[2.397469,44e-5,0,0],[2.397469,42e-5,0,0],[2.397469,4e-4,0,0],[2.397469,381e-6,0,0],[2.397469,364e-6,0,0],[2.397469,347e-6,0,0],[2.397469,33e-5,0,0],[2.397469,315e-6,0,0],[2.397469,3e-4,0,0],[2.397469,286e-6,0,0],[2.397469,273e-6,0,0],[2.397469,26e-5,0,0],[2.397469,248e-6,0,0],[2.397469,236e-6,0,0],[2.397469,225e-6,0,0],[2.397469,215e-6,0,0],[2.397469,205e-6,0,0],[2.397469,195e-6,0,0],[2.397469,186e-6,0,0],[2.397469,177e-6,0,0],[2.397469,169e-6,0,0],[2.397469,161e-6,0,0],[2.397469,154e-6,0,0],[2.397469,147e-6,0,0],[2.397469,14e-5,0,0],[2.397469,133e-6,0,0],[2.397469,127e-6,0,0],[2.397469,121e-6,0,0],[2.397469,115e-6,0,0],[2.397469,11e-5,0,0],[2.397469,105e-6,0,0],[2.397469,1e-4,0,0],[2.397469,95e-6,0,0],[2.397469,91e-6,0,0],[2.397469,87e-6,0,0],[2.397469,83e-6,0,0],[2.397469,79e-6,0,0],[2.397469,75e-6,0,0],[2.397469,71e-6,0,0],[2.397469,68e-6,0,0],[2.397469,65e-6,0,0],[2.397469,62e-6,0,0],[2.397469,59e-6,0,0],[2.397469,56e-6,0,0],[2.397469,54e-6,0,0],[2.397469,51e-6,0,0],[2.397469,49e-6,0,0],[2.397469,46e-6,0,0],[2.397469,44e-6,0,0],[2.397469,42e-6,0,0],[2.397469,4e-5,0,0],[2.397469,38e-6,0,0],[2.397469,37e-6,0,0],[2.397469,35e-6,0,0],[2.397469,33e-6,0,0],[2.397469,32e-6,0,0],[2.397469,3e-5,0,0],[2.397469,29e-6,0,0],[2.397469,27e-6,0,0],[2.397469,26e-6,0,0],[2.397469,25e-6,0,0],[2.397469,24e-6,0,0],[2.397469,23e-6,0,0],[2.397469,22e-6,0,0],[2.397469,21e-6,0,0],[2.397469,2e-5,0,0],[2.397469,19e-6,0,0],[2.397469,18e-6,0,0],[2.397469,17e-6,0,0],[2.397469,16e-6,0,0],[2.397469,15e-6,0,0],[2.397469,15e-6,0,0],[2.397469,14e-6,0,0],[2.397469,13e-6,0,0],[2.397469,13e-6,0,0],[2.397469,12e-6,0,0],[2.397469,12e-6,0,0],[2.397469,11e-6,0,0],[2.397469,11e-6,0,0],[2.397469,1e-5,0,0],[2.397469,1e-5,0,0],[2.397469,9e-6,0,0],[2.397469,9e-6,0,0],[2.397469,8e-6,0,0],[2.397469,8e-6,0,0],[2.397469,8e-6,0,0],[2.397469,7e-6,0,0],[2.397469,7e-6,0,0],[2.397469,7e-6,0,0],[2.397469,6e-6,0,0],[2.397469,6e-6,0,0],[2.397469,6e-6,0,0],[2.397469,5e-6,0,0],[2.397469,5e-6,0,0],[2.397469,5e-6,0,0],[2.397469,5e-6,0,0],[2.397469,4e-6,0,0],[2.397469,4e-6,0,0],[2.397469,4e-6,0,0],[2.397469,4e-6,0,0],[2.397469,4e-6,0,0],[2.397469,4e-6,0,0],[2.397469,3e-6,0,0],[2.397469,3e-6,0,0],[2.397469,3e-6,0,0],[2.397469,3e-6,0,0],[2.397469,3e-6,0,0],[2.397469,3e-6,0,0],[2.397469,3e-6,0,0],[2.397469,2e-6,0,0],[2.397469,2e-6,0,0],[2.397469,2e-6,0,0],[2.397469,2e-6,0,0],[2.397469,2e-6,0,0],[2.397469,2e-6,0,0],[2.397469,2e-6,0,0],[2.397469,2e-6,0,0],[2.397469,2e-6,0,0],[2.397469,2e-6,0,0],[2.397469,1e-6,0,0],[2.397469,1e-6,0,0],[2.397469,1e-6,0,0]],t.MAX_RE_WEIGHTS_RESOLUTION=t.MAX_RE_WEIGHTS.length},function(A,t,e){"use strict";function i(A,t){void 0==t&&(t={}),void 0==t.position&&(t.position=a.DEFAULT_POSITION.slice()),void 0==t.forward&&(t.forward=a.DEFAULT_FORWARD.slice()),void 0==t.up&&(t.up=a.DEFAULT_UP.slice()),void 0==t.minDistance&&(t.minDistance=a.DEFAULT_MIN_DISTANCE),void 0==t.maxDistance&&(t.maxDistance=a.DEFAULT_MAX_DISTANCE),void 0==t.rolloff&&(t.rolloff=a.DEFAULT_ROLLOFF),void 0==t.gain&&(t.gain=a.DEFAULT_SOURCE_GAIN),void 0==t.alpha&&(t.alpha=a.DEFAULT_DIRECTIVITY_ALPHA),void 0==t.sharpness&&(t.sharpness=a.DEFAULT_DIRECTIVITY_SHARPNESS),void 0==t.sourceWidth&&(t.sourceWidth=a.DEFAULT_SOURCE_WIDTH),this._scene=A,this._position=t.position,this._forward=t.forward,this._up=t.up,this._dx=new Float32Array(3),this._right=a.crossProduct(this._forward,this._up);let e=A._context;this.input=e.createGain(),this._directivity=new s(e,{alpha:t.alpha,sharpness:t.sharpness}),this._toEarly=e.createGain(),this._toLate=e.createGain(),this._attenuation=new o(e,{minDistance:t.minDistance,maxDistance:t.maxDistance,rolloff:t.rolloff}),this._encoder=new r(e,{ambisonicOrder:A._ambisonicOrder,sourceWidth:t.sourceWidth}),this.input.connect(this._toLate),this._toLate.connect(A._room.late.input),this.input.connect(this._attenuation.input),this._attenuation.output.connect(this._toEarly),this._toEarly.connect(A._room.early.input),this._attenuation.output.connect(this._directivity.input),this._directivity.output.connect(this._encoder.input),this._encoder.output.connect(A._listener.input),this.setPosition(t.position[0],t.position[1],t.position[2]),this.input.gain.value=t.gain}function n(A){let t=1;return A>a.EPSILON_FLOAT&&(t=1-A/a.SOURCE_MAX_OUTSIDE_ROOM_DISTANCE,t=Math.max(0,Math.min(1,t))),t}/**
+ * @license
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const s=e(5),o=e(6),r=e(1),a=e(0);i.prototype.setPosition=function(A,t,e){this._position[0]=A,this._position[1]=t,this._position[2]=e;let i=n(this._scene._room.getDistanceOutsideRoom(this._position[0],this._position[1],this._position[2]));this._toLate.gain.value=i,this._toEarly.gain.value=i,this._update()},i.prototype._update=function(){for(let i=0;i<3;i++)this._dx[i]=this._position[i]-this._scene._listener.position[i];let A=Math.sqrt(this._dx[0]*this._dx[0]+this._dx[1]*this._dx[1]+this._dx[2]*this._dx[2]);A>0&&(this._dx[0]/=A,this._dx[1]/=A,this._dx[2]/=A);let t=Math.atan2(-this._dx[0],this._dx[2])*a.RADIANS_TO_DEGREES,e=Math.atan2(this._dx[1],Math.sqrt(this._dx[0]*this._dx[0]+this._dx[2]*this._dx[2]))*a.RADIANS_TO_DEGREES;this._attenuation.setDistance(A),this._directivity.computeAngle(this._forward,this._dx),this._encoder.setDirection(t,e)},i.prototype.setRolloff=function(A){this._attenuation.setRolloff(A)},i.prototype.setMinDistance=function(A){this._attenuation.minDistance=A},i.prototype.setMaxDistance=function(A){this._attenuation.maxDistance=A},i.prototype.setGain=function(A){this.input.gain.value=A},i.prototype.setOrientation=function(A,t,e,i,n,s){this._forward[0]=A,this._forward[1]=t,this._forward[2]=e,this._up[0]=i,this._up[1]=n,this._up[2]=s,this._right=a.crossProduct(this._forward,this._up)},i.prototype.setFromMatrix=function(A){this._right[0]=A.elements[0],this._right[1]=A.elements[1],this._right[2]=A.elements[2],this._up[0]=A.elements[4],this._up[1]=A.elements[5],this._up[2]=A.elements[6],this._forward[0]=A.elements[8],this._forward[1]=A.elements[9],this._forward[2]=A.elements[10],this._right=a.normalizeVector(this._right),this._up=a.normalizeVector(this._up),this._forward=a.normalizeVector(this._forward),this.setPosition(A.elements[12],A.elements[13],A.elements[14])},i.prototype.setSourceWidth=function(A){this._encoder.setSourceWidth(A),this.setPosition(this._position[0],this._position[1],this._position[2])},i.prototype.setDirectivityPattern=function(A,t){this._directivity.setPattern(A,t),this.setPosition(this._position[0],this._position[1],this._position[2])},A.exports=i},function(A,t,e){"use strict";function i(A,t){void 0==t&&(t={}),void 0==t.alpha&&(t.alpha=n.DEFAULT_DIRECTIVITY_ALPHA),void 0==t.sharpness&&(t.sharpness=n.DEFAULT_DIRECTIVITY_SHARPNESS),this._context=A,this._lowpass=A.createBiquadFilter(),this._lowpass.type="lowpass",this._lowpass.Q.value=0,this._lowpass.frequency.value=.5*A.sampleRate,this._cosTheta=0,this.setPattern(t.alpha,t.sharpness),this.input=this._lowpass,this.output=this._lowpass}/**
+ * @license
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const n=e(0);i.prototype.computeAngle=function(A,t){let e=n.normalizeVector(A),i=n.normalizeVector(t),s=1;if(this._alpha>n.EPSILON_FLOAT){let A=e[0]*i[0]+e[1]*i[1]+e[2]*i[2];s=1-this._alpha+this._alpha*A,s=Math.pow(Math.abs(s),this._sharpness)}this._lowpass.frequency.value=.5*this._context.sampleRate*s},i.prototype.setPattern=function(A,t){this._alpha=Math.min(1,Math.max(0,A)),this._sharpness=Math.max(1,t),this.computeAngle([this._cosTheta*this._cosTheta,0,0],[1,0,0])},A.exports=i},function(A,t,e){"use strict";function i(A,t){void 0==t&&(t={}),void 0==t.minDistance&&(t.minDistance=n.DEFAULT_MIN_DISTANCE),void 0==t.maxDistance&&(t.maxDistance=n.DEFAULT_MAX_DISTANCE),void 0==t.rolloff&&(t.rolloff=n.DEFAULT_ATTENUATION_ROLLOFF),this.minDistance=t.minDistance,this.maxDistance=t.maxDistance,this.setRolloff(t.rolloff),this._gainNode=A.createGain(),this.setDistance(t.maxDistance),this.input=this._gainNode,this.output=this._gainNode}/**
+ * @license
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const n=e(0);i.prototype.setDistance=function(A){let t=1;if("logarithmic"==this._rolloff){if(A>this.maxDistance)t=0;else if(A>this.minDistance){let e=this.maxDistance-this.minDistance;if(e>n.EPSILON_FLOAT){let i=1/(e+1);t=(1/(A-this.minDistance+1)-i)/(1-i)}}}else if("linear"==this._rolloff)if(A>this.maxDistance)t=0;else if(A>this.minDistance){let e=this.maxDistance-this.minDistance;e>n.EPSILON_FLOAT&&(t=(this.maxDistance-A)/e)}this._gainNode.gain.value=t},i.prototype.setRolloff=function(A){let t=~n.ATTENUATION_ROLLOFFS.indexOf(A);void 0!=A&&t?A=A.toString().toLowerCase():(t||n.log('Invalid rolloff model ("'+A+'"). Using default: "'+n.DEFAULT_ATTENUATION_ROLLOFF+'".'),A=n.DEFAULT_ATTENUATION_ROLLOFF),this._rolloff=A},A.exports=i},function(A,t,e){"use strict";function i(A){let t={};for(let e in _.DEFAULT_ROOM_MATERIALS)_.DEFAULT_ROOM_MATERIALS.hasOwnProperty(e)&&(t[e]=_.ROOM_MATERIAL_COEFFICIENTS[_.DEFAULT_ROOM_MATERIALS[e]]);void 0==A&&(A={},Object.assign(A,_.DEFAULT_ROOM_MATERIALS));for(let e in _.DEFAULT_ROOM_MATERIALS)_.DEFAULT_ROOM_MATERIALS.hasOwnProperty(e)&&A.hasOwnProperty(e)?A[e]in _.ROOM_MATERIAL_COEFFICIENTS?t[e]=_.ROOM_MATERIAL_COEFFICIENTS[A[e]]:_.log('Material "'+A[e]+'" on wall "'+e+'" not found. Using "'+_.DEFAULT_ROOM_MATERIALS[e]+'".'):_.log('Wall "'+e+'" is not defined. Default used.');return t}function n(A){void 0==A&&(A={});for(let t in _.DEFAULT_ROOM_MATERIALS)A.hasOwnProperty(t)||(A[t]=_.ROOM_MATERIAL_COEFFICIENTS[_.DEFAULT_ROOM_MATERIALS[t]]);return A}function s(A){void 0==A&&(A={});for(let t in _.DEFAULT_ROOM_DIMENSIONS)A.hasOwnProperty(t)||(A[t]=_.DEFAULT_ROOM_DIMENSIONS[t]);return A}function o(A,t,e){let i=new Float32Array(_.NUMBER_REVERB_FREQUENCY_BANDS);A=s(A),t=n(t),void 0==e&&(e=_.DEFAULT_SPEED_OF_SOUND);let o=_.TWENTY_FOUR_LOG10/e,r=A.width*A.height*A.depth;if(r<_.ROOM_MIN_VOLUME)return i;let a=A.width*A.height,h=A.width*A.depth,c=A.depth*A.height,f=2*(a+h+c);for(let n=0;n<_.NUMBER_REVERB_FREQUENCY_BANDS;n++){let A=((t.left[n]+t.right[n])*a+(t.down[n]+t.up[n])*h+(t.front[n]+t.back[n])*c)/f;i[n]=_.ROOM_EYRING_CORRECTION_COEFFICIENT*o*r/(-f*Math.log(1-A)+4*_.ROOM_AIR_ABSORPTION_COEFFICIENTS[n]*r)}return i}function r(A){let t=[];for(let e in _.DEFAULT_REFLECTION_COEFFICIENTS)if(_.DEFAULT_REFLECTION_COEFFICIENTS.hasOwnProperty(e)){t[e]=0;for(let i=0;i<_.NUMBER_REFLECTION_AVERAGING_BANDS;i++){let n=i+_.ROOM_STARTING_AVERAGING_BAND;t[e]+=A[e][n]}t[e]/=_.NUMBER_REFLECTION_AVERAGING_BANDS,t[e]=Math.sqrt(1-t[e])}return t}function a(A,t){void 0==t&&(t={}),void 0==t.listenerPosition&&(t.listenerPosition=_.DEFAULT_POSITION.slice()),void 0==t.dimensions&&(t.dimensions={},Object.assign(t.dimensions,_.DEFAULT_ROOM_DIMENSIONS)),void 0==t.materials&&(t.materials={},Object.assign(t.materials,_.DEFAULT_ROOM_MATERIALS)),void 0==t.speedOfSound&&(t.speedOfSound=_.DEFAULT_SPEED_OF_SOUND),t.dimensions=s(t.dimensions);let e=i(t.materials),n=r(e),a=o(t.dimensions,e,t.speedOfSound);this.early=new c(A,{dimensions:t.dimensions,coefficients:n,speedOfSound:t.speedOfSound,listenerPosition:t.listenerPosition}),this.late=new h(A,{durations:a}),this.speedOfSound=t.speedOfSound,this.output=A.createGain(),this.early.output.connect(this.output),this._merger=A.createChannelMerger(4),this.late.output.connect(this._merger,0,0),this._merger.connect(this.output)}/**
+ * @license
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const h=e(8),c=e(9),_=e(0);a.prototype.setProperties=function(A,t){let e=i(t),n=o(A,e,this.speedOfSound);this.late.setDurations(n),this.early.speedOfSound=this.speedOfSound;let s=r(e);this.early.setRoomProperties(A,s)},a.prototype.setListenerPosition=function(A,t,e){this.early.speedOfSound=this.speedOfSound,this.early.setListenerPosition(A,t,e);let i=this.getDistanceOutsideRoom(A,t,e),n=1;i>_.EPSILON_FLOAT&&(n=1-i/_.LISTENER_MAX_OUTSIDE_ROOM_DISTANCE,n=Math.max(0,Math.min(1,n))),this.output.gain.value=n},a.prototype.getDistanceOutsideRoom=function(A,t,e){let i=Math.max(0,-this.early._halfDimensions.width-A,A-this.early._halfDimensions.width),n=Math.max(0,-this.early._halfDimensions.height-t,t-this.early._halfDimensions.height),s=Math.max(0,-this.early._halfDimensions.depth-e,e-this.early._halfDimensions.depth);return Math.sqrt(i*i+n*n+s*s)},A.exports=a},function(A,t,e){"use strict";function i(A,t){void 0==t&&(t={}),void 0==t.durations&&(t.durations=n.DEFAULT_REVERB_DURATIONS.slice()),void 0==t.predelay&&(t.predelay=n.DEFAULT_REVERB_PREDELAY),void 0==t.gain&&(t.gain=n.DEFAULT_REVERB_GAIN),void 0==t.bandwidth&&(t.bandwidth=n.DEFAULT_REVERB_BANDWIDTH),void 0==t.tailonset&&(t.tailonset=n.DEFAULT_REVERB_TAIL_ONSET);let e=t.predelay/1e3;this._bandwidthCoeff=t.bandwidth*n.LOG2_DIV2,this._tailonsetSamples=t.tailonset/1e3,this._context=A,this.input=A.createGain(),this._predelay=A.createDelay(e),this._convolver=A.createConvolver(),this.output=A.createGain(),this.output.gain.value=t.gain,this._convolver.normalize=!1,this.input.connect(this._predelay),this._predelay.connect(this._convolver),this._convolver.connect(this.output),this.setDurations(t.durations)}/**
+ * @license
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const n=e(0);i.prototype.setDurations=function(A){if(A.length!==n.NUMBER_REVERB_FREQUENCY_BANDS)return void n.log("Warning: invalid number of RT60 values provided to reverb.");let t=new Float32Array(n.NUMBER_REVERB_FREQUENCY_BANDS),e=this._context.sampleRate;for(let h=0;hi&&(i=t[n]);i<1&&(i=1);let s=this._context.createBuffer(1,i,e),o=s.getChannelData(0),r=new Float32Array(i);for(let n=0;n32&&t.throw("Utils.mergeBuffer: Number of channels cannot exceed 32.(got "+s+")"),i!==e[a].length&&t.throw("Utils.mergeBuffer: AudioBuffer lengths are inconsistent. (expected "+i+" but got "+e[a].length+")"),n!==e[a].sampleRate&&t.throw("Utils.mergeBuffer: AudioBuffer sample rates are inconsistent. (expected "+n+" but got "+e[a].sampleRate+")"),s+=e[a].numberOfChannels;const o=A.createBuffer(s,i,n);let r=0;for(let t=0;te[i]=t.charCodeAt(i)),e.buffer}},function(A,t,e){"use strict";function i(A,t,e){this._context=n.isAudioContext(A)?A:n.throw("BufferList: Invalid BaseAudioContext."),this._options={dataType:s.BASE64,verbose:!1},e&&(e.dataType&&n.isDefinedENUMEntry(s,e.dataType)&&(this._options.dataType=e.dataType),e.verbose&&(this._options.verbose=Boolean(e.verbose))),this._bufferList=[],this._bufferData=this._options.dataType===s.BASE64?t:t.slice(0),this._numberOfTasks=this._bufferData.length,this._resolveHandler=null,this._rejectHandler=new Function}const n=e(0),s={BASE64:"base64",URL:"url"};i.prototype.load=function(){return new Promise(this._promiseGenerator.bind(this))},i.prototype._promiseGenerator=function(A,t){"function"!=typeof A?n.throw("BufferList: Invalid Promise resolver."):this._resolveHandler=A,"function"==typeof t&&(this._rejectHandler=t);for(let e=0;e=0?this._stereoSplitters[i].connect(this._positiveIndexSphericalHarmonics,e%2):this._stereoSplitters[i].connect(this._negativeIndexSphericalHarmonics,e%2)}this._positiveIndexSphericalHarmonics.connect(this._binauralMerger,0,0),this._positiveIndexSphericalHarmonics.connect(this._binauralMerger,0,1),this._negativeIndexSphericalHarmonics.connect(this._binauralMerger,0,0),this._negativeIndexSphericalHarmonics.connect(this._inverter),this._inverter.connect(this._binauralMerger,0,1),this._inverter.gain.value=-1,this.input=this._inputSplitter,this.output=this._outputGain},i.prototype.setHRIRBufferList=function(A){if(!this._isBufferLoaded){for(let t=0;t0){const s=i(t,1);return o(A,1,t-1,e,n)*Math.sqrt(1+s)-o(A,-1,1-t,e,n)*(1-s)}{const s=i(t,-1);return o(A,1,t+1,e,n)*(1-s)+o(A,-1,-t-1,e,n)*Math.sqrt(1+s)}}function h(A,t,e,i){return 0===t?0:t>0?o(A,1,t+1,e,i)+o(A,-1,-t-1,e,i):o(A,1,t-1,e,i)-o(A,-1,1-t,e,i)}function c(A,t,e){const n=i(A,0),s=Math.abs(t)===e?1/(2*e*(2*e-1)):1/((e+t)*(e-t));return[Math.sqrt((e+A)*(e-A)*s),.5*(1-2*n)*Math.sqrt((1+n)*(e+Math.abs(A)-1)*(e+Math.abs(A))*s),-.5*(1-n)*Math.sqrt((e-Math.abs(A)-1)*(e-Math.abs(A)))*s]}function _(A,t){for(let e=-t;e<=t;e++)for(let i=-t;i<=t;i++){const s=c(e,i,t);Math.abs(s[0])>0&&(s[0]*=r(A,e,i,t)),Math.abs(s[1])>0&&(s[1]*=a(A,e,i,t)),Math.abs(s[2])>0&&(s[2]*=h(A,e,i,t)),n(A,t,e,i,s[0]+s[1]+s[2])}}function f(A){for(let t=2;t<=A.length;t++)_(A,t)}function l(A,t){this._context=A,this._ambisonicOrder=t;const e=(t+1)*(t+1);this._splitter=this._context.createChannelSplitter(e),this._merger=this._context.createChannelMerger(e),this._gainNodeMatrix=[];let i,n,s,o,r;for(let a=1;a<=t;a++){i=a*a,n=2*a+1,this._gainNodeMatrix[a-1]=[];for(let A=0;A ["+A+"])"),this._audioElementSource=this._context.createMediaElementSource(this._videoElement),this._foaRouter=new s(this._context,this._channelMap),this._foaRotator=new o(this._context),this._foaPhaseMatchedFilter=new r(this._context),this._audioElementSource.connect(this._foaRouter.input),this._foaRouter.output.connect(this._foaRotator.input),this._foaRotator.output.connect(this._foaPhaseMatchedFilter.input),this._foaVirtualSpeakers=[],this._bypass=this._context.createGain(),this._audioElementSource.connect(this._bypass);const e=Math.pow(10,this._postGainDB/20);c.log("Gain compensation: "+e+" ("+this._postGainDB+"dB)");const i=this;return new Promise(function(A,t){new n(i._context,i._speakerData,function(t){for(let A=0;A ["+A.toString()+"])."),this._config.channelMap=A.slice(),this._foaRouter.setChannelMap(this._config.channelMap))},i.prototype.setRotationMatrix3=function(A){this._isRendererReady&&this._foaRotator.setRotationMatrix3(A)},i.prototype.setRotationMatrix4=function(A){this._isRendererReady&&this._foaRotator.setRotationMatrix4(A)},i.prototype.setRotationMatrixFromCamera=function(A){this._isRendererReady&&(h.invertMatrix4(this._tempMatrix4,A.elements),this._foaRotator.setRotationMatrix4(this._tempMatrix4))},i.prototype.setRenderingMode=function(A){if(A!==this._config.renderingMode){switch(A){case c.AMBISONIC:this._foaConvolver.enable(),this._bypass.disconnect();break;case c.BYPASS:this._foaConvolver.disable(),this._bypass.connect(this.output);break;case c.OFF:this._foaConvolver.disable(),this._bypass.disconnect();break;default:return void h.log('FOARenderer: Rendering mode "'+A+'" is not supported.')}this._config.renderingMode=A,h.log("FOARenderer: Rendering mode changed. ("+A+")")}},A.exports=i},function(A,t){A.exports=["UklGRiQEAABXQVZFZm10IBAAAAABAAIAgLsAAADuAgAEABAAZGF0YQAEAAD+/wIA9v8QAPv/CwD+/wcA/v8MAP//AQD7/wEACAAEAPj/+v8YABAA7v/n//v/9P/M/8D//f34/R38EvzxAfEBtA2lDTcBJQFJ9T71FP0D/cD1tfVo/Wv9uPTO9PPmOufc/U/+agL3Aisc/RxuGKEZBv3j/iYMzQ2gAzsEQQUABiQFrASzA5cB2QmyCy0AtgR4AeYGtfgAA2j5OQHP+scArPsMBJgEggIEBtz6+QVq/pj/aPg8BPP3gQEi+jEAof0fA1v9+/7S+8IBjvwd/xD4IADL/Pf9zvs+/l3+wgB7/+L+7fzFADH9kf6A+n3+DP6+/TP9xP68/pn+w/26/i39YgA0/u790Pt9/kD+7v1s/Wb+8f4C/1P+pf/x/cT+6/3p/Xz9ff5F/0f9G/4r/6v/4P5L/sL+ff7c/pj+Ov7X/UT+9P5G/oz+6v6A/2D+9/6P/8r/bP7m/ij+C//e/tj/Gf4e/9v+FwDP/lz/sP7F/2H+rv/G/s7/Hf7y/4P+NAD9/k0AK/6w/zP/hACh/sX/gf44AOP+dgCm/iUAk/5qAOD+PwC+/jEAWP4CAAr/bQBw/vv/zf5iACD/OgCS/uD/Cv9oAAb/CgDK/kwA//5tACH/TgCg/h4AHP9aABP/JADP/hEAYv9gAAj/3f8m/ysAYv8gACX/8/8k/ysAXv8bABH//v8j/ygAa/8qAAD/9f9g/1YAWf8JACH/AgB2/z4AXP/w/z3/FgB2/ykAX//9/z//EwCV/zUAS//n/1T/GACK/x4ATv/0/4P/QQB4//v/WP/2/3X/HAB8//P/V//3/2f/AQBh/9v/Tf/x/5P/IwCI/wMAf/8hAKP/JACZ/xUAiv8nAK//HgCr/yMAm/8uAMz/OACi/yQAqf87AMT/MwCY/yUAtP9FAMH/KgCu/ycAyP85AMv/IwCz/xoA1f8qAMn/FgC8/xQA4/8nAMX/CwDJ/xQA4f8ZAMH/BgDO/xQA4f8WAMP/BwDU/xQA4P8QAMH/AQDb/xQA3P8JAMP/AgDh/xIA2v8EAMj/AgDk/w0A1f/+/8v/AwDm/wwA0v/+/9H/BgDl/wkAzv/8/9T/BwDk/wcAzv/8/9r/CQDi/wQAzf/8/9//CADf////0P/9/+L/BwDd//7/0////+T/BgDb//z/1f8AAOf/BQDZ//v/2v8CAOb/AwDY//v/3v8EAOb/AgDY//3/4f8FAOX/AQDZ//7/5P8GAOP/AADb/wAA5/8GAOH////d/wIA5/8FAOD////f/wMA6P8FAOD////h/wQA6P8EAN7////h/wUA4v8DANv/AQDd/wQA3P8CANn/AgDb/wMA2/8CANv/AgDd/wIA3v8CAOH/AQDj/wEA","UklGRiQEAABXQVZFZm10IBAAAAABAAIAgLsAAADuAgAEABAAZGF0YQAEAAAAAAAA/f8CAP//AQD//wEA//8BAP3/AAACAP7/+f8AAAIA/P8FAAQA8/8AABoA+f/V/wQAHQDO/xoAQQBO/ocA0Px1/ucHW/4UCm8HLO6kAjv8/fCRDdAAYfPiBIgFXveUCM0GBvh6/nz7rf0J/QcQSRVdBgoBSgFR62r9NP8m+LoEAvriBVAAiAPmABEGMf2l+SwBjva6/G4A//8P/CYDMgXm/R0CKAE6/fcBBwAtAND+kQA0A5UDhwFs/8IB8fydAEP/A/8v/e7/mP8j/2YBIwE3Av0AYv+uAOD8lgAg/wwAIf/L/n0Ae//OAJMB3P/XAF//XwCM/08AB/8NAEf/rf4jAT3/lgAJAP4AHgDpAO8AUf9L/07/Qf8KAOD/x/+D/3sATQCDAMoA0f79/+L/EQDt/7EAqv+S/7IAuv/o/wgAc//X//H/SwCm/+3/Yf/B/yoAAADI/7X/AwBg/5EATgCX/xYA/P+q/00AVACY/6v/BADD/zwALQCN/8z/KQDu/ygAEgCZ/6f/VQDC//T/KQCs/7P/UgAfAO7/NgC8/57/awAZAPP/+P/V/8z/bQBBAL//DgD0/+T/TABBAMz/CwAxAPz/SQBqALn/BgALAPz/EAA7AIz/3/8iAAUA//8kALf/y/9VABQA+v81AOj/0P9cAB4A+f8WAOr/vv83ABgAw/8JAOj/4f8nACIAsf/y/w4A3v8gACQAxP/n/ycA7P8WAC0Ayf/U/ycA9v/7/yUA0P/P/zUABADc/xUA5P/J/zcACwDS/xUA9P/m/zAACQDX/+3/9v/2/yQACgDZ/+P/AwAKABYA///b/9j/EQALABkADgD6/+7/GwD4/w4A8P/w//j/EgAEAAUA9f/1/wQAGgD4/wAA5////wAAGQD1////7f8FAAUAFQDv/wAA6v8LAAcAFQDs/wEA9P8SAAYACwDr//7/AQASAAYABQDv/wIAAwAWAAIAAgDv/wAABgATAAEA/f/u/wQABgAQAPr/+P/z/wUACQALAPj/9//4/wgABwAKAPT/+f/5/w4ABwAIAPT/+//9/w4AAwADAPH//f///w8A//8BAPP///8BAA0A/f/+//X/AgACAA0A+//8//b/BAADAAoA+f/7//n/BgADAAcA+P/7//v/BwABAAQA+P/8//3/CQABAAIA9//9////CQD/////+P///wAACAD9//7/+f8AAAAABwD8//3/+v8CAAAABgD7//z//P8EAAAABAD6//3//P8FAP//AgD6//7//v8FAP7/AQD7//////8GAP7/AAD7/wEA//8EAP3/AAD9/wEA/v8DAP3/AAD9/wIA/v8CAP3/AQD9/wIA/v8CAP7/AQD+/wEA"]},function(A,t,e){"use strict";function i(A,t){this._context=h.isAudioContext(A)?A:h.throw("HOARenderer: Invalid BaseAudioContext."),this._config={ambisonicOrder:3,renderingMode:c.AMBISONIC},t&&t.ambisonicOrder&&(_.includes(t.ambisonicOrder)?this._config.ambisonicOrder=t.ambisonicOrder:h.log("HOARenderer: Invalid ambisonic order. (got "+t.ambisonicOrder+") Fallbacks to 3rd-order ambisonic.")),this._config.numberOfChannels=(this._config.ambisonicOrder+1)*(this._config.ambisonicOrder+1),this._config.numberOfStereoChannels=Math.ceil(this._config.numberOfChannels/2),t&&t.hrirPathList&&(Array.isArray(t.hrirPathList)&&t.hrirPathList.length===this._config.numberOfStereoChannels?this._config.pathList=t.hrirPathList:h.throw("HOARenderer: Invalid HRIR URLs. It must be an array with "+this._config.numberOfStereoChannels+" URLs to HRIR files. (got "+t.hrirPathList+")")),t&&t.renderingMode&&(Object.values(c).includes(t.renderingMode)?this._config.renderingMode=t.renderingMode:h.log("HOARenderer: Invalid rendering mode. (got "+t.renderingMode+') Fallbacks to "ambisonic".')),this._buildAudioGraph(),this._isRendererReady=!1}const n=e(1),s=e(8),o=e(9),r=e(17),a=e(18),h=e(0),c={AMBISONIC:"ambisonic",BYPASS:"bypass",OFF:"off"},_=[2,3];i.prototype._buildAudioGraph=function(){this.input=this._context.createGain(),this.output=this._context.createGain(),this._bypass=this._context.createGain(),this._hoaRotator=new o(this._context,this._config.ambisonicOrder),this._hoaConvolver=new s(this._context,this._config.ambisonicOrder),this.input.connect(this._hoaRotator.input),this.input.connect(this._bypass),this._hoaRotator.output.connect(this._hoaConvolver.input),this._hoaConvolver.output.connect(this.output),this.input.channelCount=this._config.numberOfChannels,this.input.channelCountMode="explicit",this.input.channelInterpretation="discrete"},i.prototype._initializeCallback=function(A,t){let e;(e=this._config.pathList?new n(this._context,this._config.pathList,{dataType:"url"}):2===this._config.ambisonicOrder?new n(this._context,a):new n(this._context,r)).load().then(function(t){this._hoaConvolver.setHRIRBufferList(t),this.setRenderingMode(this._config.renderingMode),this._isRendererReady=!0,h.log("HOARenderer: HRIRs loaded successfully. Ready."),A()}.bind(this),function(){const A="HOARenderer: HRIR loading/decoding failed.";h.throw(A),t(A)})},i.prototype.initialize=function(){return h.log("HOARenderer: Initializing... (mode: "+this._config.renderingMode+", ambisonic order: "+this._config.ambisonicOrder+")"),new Promise(this._initializeCallback.bind(this),function(A){h.throw("HOARenderer: Initialization failed. ("+A+")")})},i.prototype.setRotationMatrix3=function(A){this._isRendererReady&&this._hoaRotator.setRotationMatrix3(A)},i.prototype.setRotationMatrix4=function(A){this._isRendererReady&&this._hoaRotator.setRotationMatrix4(A)},i.prototype.setRenderingMode=function(A){if(A!==this._config.renderingMode){switch(A){case c.AMBISONIC:this._hoaConvolver.enable(),this._bypass.disconnect();break;case c.BYPASS:this._hoaConvolver.disable(),this._bypass.connect(this.output);break;case c.OFF:this._hoaConvolver.disable(),this._bypass.disconnect();break;default:return void h.log('HOARenderer: Rendering mode "'+A+'" is not supported.')}this._config.renderingMode=A,h.log("HOARenderer: Rendering mode changed. ("+A+")")}},A.exports=i},function(A,t){A.exports=["UklGRiQEAABXQVZFZm10IBAAAAABAAIAgLsAAADuAgAEABAAZGF0YQAEAAD+/wQA8/8YAP3/CgACAAAA//8CAAYA8/8AAPH/CgDv/97/e/+y/9P+UQDwAHUBEwV7/pP8P/y09bsDwAfNBGYIFf/Y+736+fP890Hv8AGcC3T/vwYy+S70AAICA3AD4AagBw0R4w3ZEAcN8RVYAV8Q8P2z+kECHwdK/jIG0QNKAYUElf8IClj7BgjX+/f8j/l3/5f/6fkK+xz8FP0v/nj/Mf/n/FcBPfvH/1H3+gBP/Hf8cfiCAR/54QBh+UQAcvkzAWL8TP13+iD/V/73+wv9Kv+Y/hv+xPz7/UL83//a/z/9AP6R/5L+jf26/P3+rP26/tD8nP7B/Pv+WP1V/sP9gv91/3P9xP3J/nv/GP5S/sb+IP8v/9j/dv7U/pr+6v+u/Z3/sv5cAOr9Q/83/+n/zP5x/57+2//k/nwA/v01//L+SACB/sD/Ff81AJT+TgDp/ocAm/5dAFT+MgD+/pMAW/7o/yH/xQDA/kkA9P6LAL3+pAC0/iQAz/5UALD+UwAt/3UAhf4UAA//pwC+/joAz/5aAAv/fwDY/iMAIf+uAPP+ZAAc/0QAy/4xAB7/TgDs/goADP8wAEL/NwDo/ub/Uf9BAC3/+v9F/y4ARP9HAFP/EQA3/xMATP81AG3/HQAu/wgAaP9FACb/9f9B/y0AUP8rAED/CwBV/z4AW/8TAGH/BQBK/xsAfv8eAFn/AgB3/zwAff8RAGj//v+E/yAAb//0/3n/FwBz/xcAiv8PAHn/FQCJ/xgAg//x/3j/EQCa/ycAff/w/47/HwCI//X/iv/7/43/JQCM/+n/kP8AAJb/JACj//7/oP8ZAML/SwCo/w4Atv8tAMb/PACr/xcAwP9HAMP/OADF/y4A0f9IANL/NwC//zEA0f9LAMb/MAC8/y4A3f9GAMH/FQDQ/yYA2/8sAMT/AwDX/xkA3v8SAM3/9v/c/w8A4f8LAMj/8f/h/xQA2P8CAMn/8//j/xQA0v/7/9H//P/i/xEA0v/1/9L//f/j/w0A0f/x/9f//v/k/wgAz//u/9z/AwDg/wMA0P/v/9//BQDf////0v/y/+D/CADc//3/0v/2/+L/CgDa//r/1v/5/+T/CgDY//j/2f/9/+T/CADY//f/3P8AAOT/BwDY//f/4P8EAOP/BADZ//j/4v8GAOL/AwDa//r/5f8IAOH/AQDc//3/5v8JAOD//v/f////5v8IAOD//v/h/wIA5/8HAOD//f/j/wMA5/8GAOD//f/l/wYA5v8EAOD//v/m/wYA5f8CAOL////n/wYA5P8BAOH/AADl/wUA4f///+H/AQDk/wMA4f///+T/AQDm/wEA5////+r/AADt/wAA7/////P/AAD1////","UklGRiQEAABXQVZFZm10IBAAAAABAAIAgLsAAADuAgAEABAAZGF0YQAEAAD//////v///wAAAAAAAAAAAQAAAAAA///9/wAABAD+//n/AgAJAAAA+v/+//f/DAAdAPv/+v+l/8L+jf/4/vgAdwVPAQACLQBo+Qj/Ev7o/N3/VgCbA08Bxf+L+yn9J/2HCU8FmgBvDe30Rv5h/LT09gi5CxkA5gOi8/30kwEM+4YJMf2nBmkJJAQQBLoFtvvv+m4A7PF6/R0Bif3qAuf8WARAAf4GyABG/BIAwvr4Acv8U//c/yIC8AEn/B8Daf2CAgMBAf3MAN38vgLK/UT/QwCyAPYClPyvAW/+pQAoASD+zP+R/IYC1f7C/nEBQP96AZb+1QAIAM//yQE7/tkAZ/7TAXL/w/8+AIsAtwB7/24A4v9a/z4A7v4iADb/dwCj/23/kgBOANUAIv8lAKEAxP9gAK7/BwCP/5kA7/9v/0wAzv9DAGT/3/9vAHv/6P+q/xUA7P8XAO//uv/g/2UAEgCV/wEATADM/+7/+//j/+D/9v/i//j/IgD+/xoAxf/6/z4A5/+8/9D/QwDq/+3/OQDT/zUAIgA/APP/PgAjAPD/BwAGACAADAC3//b/HAA3AN//RgDN/w8AIAACAN//GQBDACEAIwA+ACoAJQAeAPz/KgAYAPr/DgAEABYAIgAcAMT/7f8OAOL/5P/2//L/9P8GAPT/7v/8/+7/6v/t//z/AgAUAOL//P8VAAMA4/8IAPb/+P8MAAoA5v8NAAsA9v///wEAAAD9//n/9/8JAAYA7v/6/wMA+f8GAAEA7f/7/xgACAD4/w8A///3/w0A+f8BAAIA/P/5/xIA///9//r/7v/+/xYACQD///H/CwDz/wEADgAHAPP/FADn/+3/AQD5//f/AgD7/wEABwAMAAEADQD8//n/8f8OAPX/BAD+//X/+v8WAAQA+f8CAAEA7/8QAAEA/P8DAAUA9f8KAAwA9v8DAAUA+f8OAAoA9f/7/w0A+v8EAAgA8P/6/woA+//8/wkA+P/3/woA+//8/wcA9//1/woAAwD5/wcA/P/3/w0AAwD3/wEABAD2/wkABgD3/wEABQD3/wUABQD3//v/BwD3/wMABQD3//r/CQD7////BQD6//n/CQD9//3/BAD9//j/BwAAAPv/AwD///j/BwABAPn/AQABAPn/BQACAPn///8DAPr/AwADAPr//v8EAPv/AQADAPv//P8FAP3///8DAPz/+/8FAP7//f8CAP7/+/8EAP///P8BAP//+/8DAAEA+/8AAAEA+/8CAAIA+////wIA/f8AAAIA/P/+/wIA/f8AAAIA/f/9/wMA/////wEA///+/wIA/////wAAAAD+/wAAAAD/////AAD//wAA//8AAP//AAD//wAA","UklGRiQEAABXQVZFZm10IBAAAAABAAIAgLsAAADuAgAEABAAZGF0YQAEAAD////////+//////8AAP////8AAP//AAAAAPz//f8IAAMA9////w4AAQD6/wwA8//+/y8Afv/0/2H/UP5gAbH+2QG1B2cAVAIh/l32FPyM/nACPQDV/+UEo/Q6AQwCu/oLD9kF8QJA/Uz+Wf2KCOcC+wUKBsL5aQBQ97rwOPiPAvn5CAl8AHEDkQPcAA8Bn/lIAdz7HQF1+xz9cAM4/94E4gDKAun+cgPYAYr9JgJr/bf+ivxz/MoBgv5UA8EBSgAQAJ7/UgEk/cQB7f63/sD/vf4XAhT/BQFCADYAnQGI/9EBtv3hALD/vP+c/3H/TgIN/1sBpf8yAP3/4f8qABr+1f8OAJ3/dwAGADEBnv9JAPz/IQBwAIH/jgAS/4wAsACTAOn/DQDCALn/ZQCSAAIAAwD1/9//jv9aADQA/v9EAB0AfgA8AAQACgB9APr/IAARAPT/5v9xACAABAAHAGUAt/89AC4ACgAjAMP/+v/9/xYA7f/1/+D/7P87AC0Auv8RAAcA9/8FAC8A2//y/xIAEwAaADQAJADp/zoAAgAfABIA2f/e/zUA+P/6/w4A9//A/zcA4//P//T/5f/R////EwDb/w4A8/8BABkANADh/xEA+f/0/wIAHADc//j/GwD1//f/GADs/+v/EAAAAPz/EgD3/+r/FgAMAAkAGAD9/+z/IQAQAPH/GQD3//z/CgAfAOX/AgD8//H/BAATAOv/+v///wIABAAdAOj/BQAPAAcAAQATAOz/8/8JAAkA6f8VAOv/+f8QABUA/v8OAO3/+P8KABUA9f8FAPv/5/8TAA0A7f8XAAkAAQAJABYA4/8WAAcACgANABEA7v8EAP7/AAD+/wMA9//7/xAAAQD8/wQA+f/7/wMABgDq/wAA+v/3/wYACQD1//3/BAD9/wgADgDw//r/AgD6/wEACADv//j/BQD///X/BwDu//j/AgACAPP/BAD2//n/BAAGAPb/BAD8//3/BQAJAPL/AwD+//3/BAAIAPP//f8DAPz/AAAGAPP/+/8CAP7//f8FAPX/+f8DAAAA/P8EAPf/+v8GAAMA+/8EAPv/+/8GAAQA+v8CAP///P8EAAUA+f8AAP///f8CAAUA+P///wEA/v8BAAUA+f/+/wIAAAD//wUA+v/9/wMAAQD9/wQA+//9/wMAAgD8/wMA/P/9/wMAAwD7/wEA/v/+/wIAAwD6/wEA///+/wAABAD6/wAAAQD//wAAAwD7////AQAAAP//AwD8//7/AgABAP3/AgD9//7/AQABAP3/AQD+//7/AAACAPz/AAD+//////8BAP3/AAD//wAA//8BAP7/AAD//wAA/v8AAP7/AAD//wAA//8AAP//","UklGRiQEAABXQVZFZm10IBAAAAABAAIAgLsAAADuAgAEABAAZGF0YQAEAAD//////P/9//3//////wAAAAAAAAIAAgACAP//CAAEAEEA//+cAAUAb/8HAAH9+P9eARkAogQUAJn8BwCd/gX/+QQNAKoC9gFdAtb/b/vd/936TP/6AsD/nfqn/un1W/0dA8IEsQLvAJv2bP72+WMAkP8dAcX+nQO2AIr6bP/EABX+NgK/Bdj2IQv2AE4EUAiD/xQAnwIm/B0B/wGNAoH7sQaP/b8CiQakAqD+R/9xA477KQL//6r75v/O/pcCgQCtAiMCBQAkANAARwHf//39hgBl/kUAJgEtAUEATgA/AgoASADK/zUAJv29/vL+l/9c/0cAUwBBAE8A6QE5/87/Wv9NAOf+5v7P/5P/4/9BAKYAQwDD/zYB5v+r/zYATwAp/1v/WQAEAB0AhwA0AA0AIAA3AAEAzv/u/+//5v9m/zwAIADQ/8T/SABiANb/SwAbAFf/MQDX/7L/hP8TAPr/AgAMAAsAHwAZAI3/VgDC/9v/5//x/6P/AwBlAMv/yf82AB4A+P9WAPj/NwDi/1EA0v9JANj/JwAcAAEADABYANj/4f8MAEwAmP82AN//3P8UADYA7//6/wIACADU/ygAyv82AN7/9v/2/ygAxv/9/+3/5//n/zUA6//g/y4ADgD5/wsABwDv/xIADwAGACoAJQD3/zIA+/8FABsAFgDO/zAAHAAIABQALADp/xcACAAAAPH/GADs/wkACQAFAAgAFQDp/wIAHAD1//P/EQDw/+3/GAD9/+f/HAD8//T/DAAQAPH/HwD4//r/DwAPAOj/EQACAOn/DAAXAOX/BAAOANH/9/8MAO//9f8LANT/9f8EAO//6f8NANb/+P8KAOz/5v8MAOD/7f8UAO//7//+//7/9v8YAPj/9f/z/wsA+v8SAPD/+v/x/xYA+f8SAPb/9//3/xEABQACAPn/9//y/xQACQD///b//v/7/xIACQD9//H/AAD7/xEAAgD5//P/AwD9/w8AAgD3//D/BAD//wUA/v/0//D/BgADAAMA/P/2//f/BwAGAP7/+//2//j/CAAFAPv/+f/5//v/BwAHAPn/9//7//7/BQAFAPf/9//+/wEABAACAPf/+P8BAAIAAgAAAPj/9/8CAAMAAAD+//n/+f8EAAQA/v/8//r/+/8EAAMA/P/7//z//P8EAAIA/P/5//7//v8DAAEA+//5//////8CAAAA+//5/wEAAAABAP//+//6/wIAAQD///3//P/7/wMAAQD///3//f/9/wIAAQD9//3//v/9/wMAAQD9//z/AAD//wEAAAD9//z/AAAAAAAA///9//3/AAD//wAA/v////7/AAD//wAA////////AAD//wAA//8AAP//","UklGRiQEAABXQVZFZm10IBAAAAABAAIAgLsAAADuAgAEABAAZGF0YQAEAAD+////+f////v//v///wAA/////wUAAQAIAAIABwACAHkATAAOAaMAAf9C/9X6QvwhArAAtghABW37nv/y+0wAWQNcAE8JRwSOC6AEJe8P8S/zrPWaBI/+LQA/+0L+P/4K8AgAb/8uCh78BQtC614GaQWfAin5UfzN8Tf+GQizAZ4MCQMbGJ4BoRS7AvcHyQARA6n9ZwHZ/z4DvwAZAlAB6gbNAS4GFADFATL7E/2K+j37C/xp/SD9Uv0VAOsDs//WAd3+bv7F/f79mP2X/KH+FwC0/1n+VgFcATABHQGaAET+nf8Y/hoAovpqAXj9CQKW/lsCl/4RApj+bAHk/RcAlv4BAG/+DgDi//3/GwAOAEIAq/+y/3z/8v8+/7T/Tv8//27/mgDZ/1sA+P+cAAAA/P/i/yMAi/85AMP/KgDM/9MA9P+QABoA4QAiACwACwBdAP7/TQDb/y0Ayf+SAA0AZwDg/4wA+/8/AAMAgQDp/w0ADAAQAAoANgAgAA4AKABIAB4A4v/3/+f/+v/c/+n/EADn/wgAFAAqAOz/IwDc/9//3f8XAND/2v/a/w0A5v8BANb/9P/m/wAA8P8ZAN3/RwAGAEsABgB/AP7/NAASAEgABAA3AP3/KgD9/1sA8P8lAOr/FgD1/xAA4/8kAOv/AwD4/xEA5f8NAPT/+v/3/x8A7f8PAPj/IwD5/yAA9/8ZAAEAGgD4/xoA9f8HAAMACAD0/xgA+P8AAPr/IQDp/w4A8v8HAPX/IgD1/wYA+P8GAPX/GgD3/woABQASAAcAGQDw/+v/9P8bAP3/HADs/+f/7/8LAPr//v/0//T/AgD2/wsA6P///+P/CADY//7/5v/3/wQA/v8LAPD/GgD1/yMA/P8QAOv/LADw/yQA+P8XAO7/MQD9/yEAAQAcAPD/IgD9/xMA+/8OAO//FQABAAoA+/8PAPP/FQABAAQA9/8PAPX/CAADAAEA+P8NAPv/CAAGAAUA9/8JAP//AAAFAPz/+f8HAAQA/f8FAP3//P8FAAYA+P8DAP7/+/8AAAcA9/8BAP///f///wgA9//+/wAA/v/8/wUA9//8/wIA///7/wUA+v/7/wIAAAD6/wMA/P/6/wEAAQD6/wEA/v/7/wIAAgD6////AAD7/wEAAgD7//7/AQD8/wAAAwD8//3/AwD9/wAAAgD9//z/AwD/////AgD+//z/AwAAAP7/AQD///3/AgABAP3/AAAAAP3/AgACAPz///8BAP3/AQACAP3//v8BAP7/AAABAP3//v8CAP7///8BAP7//f8CAP////8AAAAA/v8CAAAAAAAAAAAA/v8BAAAAAAD//wAA//8AAP//AAD//wAA//8AAP//","UklGRiQEAABXQVZFZm10IBAAAAABAAIAgLsAAADuAgAEABAAZGF0YQAEAAAAAP//AAD//wAA//8AAAAA/////wAAAQD+////AAAGAP3/OAABAIIAAwBv//f/E/0QAK0ADQCzA/7/8P4u/0cBDQCJA6ABbQDg/w7/z/9o+Vn/SPnL/1//Ef+2+jr9RfZgA5QFZwILDFj+PAb2/nEFKgKk/R0Dlv6b/FUDsP6YAoj9SgAT/iL/tAPwAv8A0P6zAr7/dwAnAf39uP22/skA2v///2YCoP4UAUsAZgF2AJH+4P70/rz9+f+U/Xv/8v7CAcb+TACS/kwAv/+x/tX9oP71/oL/1f8nAEUAZwGtAAgAIgC/AD4BaP8GAGH/dQDF/64Arf8nAakAhAH9/+kAQQD3AFb/q/8p/yIAR/8FAPD/ZAA/AIYA3v8tADQADQBp/3f/CwABAP3/Wf8OANj/WwDH/xoAe/8DAKz/zv96/z8A3f/J/5X/IAD5//j/q//c/+//RADq//D/vv8pADUAFQDI/y8ACAAbANb/OwD3/+3/9f/e/wcAIAAeAMH/8/8xAC0AEADW/+3/HAADAPv/8P8DAOL/OwD3/xcACQAHAM//5f8XAAcAz//T/9D/HgD9////yf/e//v/AgD//9H/6/////H/+/8hAAIA9//7/w0AFgAQAPL/2v/8/xsAGQABANz/9P8YAAQA/v/y/wMA5v8YAAkAAAAAAAMA7/8KABgADwDs//j/BwATABsA8P/1//z/BAAMAAAA9P/s/xAA/v8GAAkA/v/p/wMACwALAP7/9P/p/wcADQAFAPb/7//4/w0ACAD8//b//v/1/wMACwD1//T/8P/8/wAACQDz/+f/5P8GAAkABQD5//D/+v8FAA0AAwD///T/AgACABAA/v8CAPD/+/8FAAoA9f/3//f//v8GAP7/9v/t//z/+f8AAPj/+v/3/wEA+v8HAPr//P/5/wQA//8DAPr/+P/3/wYA///+//X/+//5/wQA/f/7//X/+//4/wMA/f/8//j//v/9/wYA///8//f/AgAAAAUA/f/6//n/AwACAAIA/f/7//z/AwACAAAA/f/6//3/AgADAP7//f/7/wAAAwAFAPz////8/wMAAgAEAPv//v/+/wMAAgADAPv//v///wMAAQABAPv//f8AAAIAAAD///v//f8BAAIA///+//z//v8CAAIA/v/9//3///8CAAEA/v/9//7/AAACAAAA/v/9////AAABAAAA/f/9/wAAAQABAP///f/+/wEAAQAAAP///v/+/wEAAQD///7//v///wEAAQD///7//v///wEAAAD+//7///8AAAAAAAD+//7///8AAAAA///+//7///8AAAAA////////AAAAAP////////////8AAP//////////","UklGRiQEAABXQVZFZm10IBAAAAABAAIAgLsAAADuAgAEABAAZGF0YQAEAAAAAAAAAAABAAAAAAD//////////////v////3/////////+//8////AQD9//z/9f8BAAIA+f8dACgAWQBxAJX/qv+Y/uz9aP9k/7UDUQQBAiQA4Pgi/AkB0gKaBsD/+fxp/vz9CQSp/I/+ywDO+vMD0fzK/PABcgBeBfoBv/+uAuH9Sf5gAy39awMmBWUBuP9fA9/9fgDj/2/+EACaACcCSv9Z/2j/rv7hAA0AWf55/7L84P7E/SIAT/67AMv/tf+FAA7/1v+7/gv/IP+E/sQA+P5aAXz/tP9XAFX/tP8o/4r/j//e/yQAMv9mAJT/rgCr/9X/EwCb//H/9f7F/6D/EAAoAK3//v+e/zsAh/+B/7r/if/C/2r/4P/z/6//HwCy/0IA7/9ZALT/y/80ACgA9v/J/9//DgA5ADUALQARADIACwAfAOf/NgArACMACQBBAEcAGAAjAC4AWQBUAHcAAAAfACEAIAAcAPj/CADk/yQA7v89AEEAFwD5/xYA6f8aAOX/AADF/zQADwAUAOT/BQDr/yUA6P8XAOf/HADR/0AA8P8nAAgACQDt/ycAKAAHAPH/IQDz/xsACADn//n/DgADAA4A8P///8z/GgDN/yMA/f8QANj/MwACAC0ACwAOAO3/JgAZAAUACgAAAA4AIgAaAAkADwACAAAAHQATAAUABQACAAgACwAjAO////8AAA8ABQAPAPL//f8GAAsABgAGAPD/8v8GAPz/CAD6//H/6v8PAAgABgD4//3/9v8aAAgABwD1//7//v8QAAoACAD//wUA9v8QAAoABAAFAAgAAgAJAAoAAwD//w0AAgD//wcA/v8DAAoABQAFABUABAAKAAYABwAHAA8ACgAGAAwADwAMAAkAEAAJAAgADwAMAAgADgAJAAUACQAPAAUACwAHAAEABgAIAAEABAAGAP//AgAJAAAAAgAEAP7///8IAAIA//8GAAEAAQAJAAIA/v8EAAMA//8JAAEA/v8DAAMA/v8HAAMA/f8BAAUA/v8FAAMA/v8BAAcA//8DAAMA/v8BAAYA//8CAAMA/////wcAAAAAAAMAAAD//wYAAQD+/wMAAQD//wUAAQD+/wIAAgD//wQAAgD+/wEAAwD//wMAAwD+/wEAAwD//wIAAwD//wEABAAAAAEABAD//wAABAABAAAAAwAAAAAABAABAP//AwABAAAAAwACAP//AgACAAAAAwACAP//AgACAAAAAgACAAAAAQADAAAAAQACAAAAAQADAAAAAQACAAAAAAACAAEAAAACAAEAAAACAAEAAAABAAEAAAABAAEAAAABAAEAAAABAAEAAAABAAEAAAABAAEAAAAAAAAAAAAAAAAA","UklGRiQEAABXQVZFZm10IBAAAAABAAIAgLsAAADuAgAEABAAZGF0YQAEAAAAAP//AAD//wAA//8AAAAA//8AAP//AAACAAAA+f8BAAYA///4/wIA//8AAA8A/v/V/wEAEwA9AAEBRwA2AF7/kfog/3gBwv99CDYBU/qtAUX/AP7OAfkAX/o9B38FSfwaAuT14/60BAr8CQAI/tfyIQTzAXP+egdUBBwBof7TBMT8bAWi/5EEWwBRAAAKyfxE/8b88vp6ACP+PAF4/qD8MQNM/ygCJ/2XAPD9kP5gAVT/iP9I/lEB4P8qAD0BFAGa/+7/DgB2AOP98gFm/u/+Vv5/AG8ASP9gAM//qv9w//oAcv+2/jIBHgA7/6D/oAAGAKH/lADT/wAAggC8AAYAkP9yAEcAkf8BAOD/RAAr/zUANwDt/xQAJQAkAMT/zwA/AOH/xv9zAGsANQBTAIcALAAvACIATACy/xMADADg/xcAWABvAJL/7f9VAPb/EgDt/wcA4f8kAPP/5P+h/wgACQDy//r/LgAQAMn/8/9CAOX/5v/S/9//3P8pABYAuP/s/w8AFgDt/+3/7v/w/9j/5/8GAOf/2P/2//P//v8kABMAuf/m/xoADADZ/+r/3P8KAAUAKwDe/wsA3P8VAAAADgAfAB0ACAAMAF4AGgAhAPL/MwDz/0kABAAKAPX/LwAbAAkA9v/s/+3/8/8CABAAAADm//n/BQALAAUAAQDj//n/JQAVAPX/9v/+/wIAEQABAPP/8P/1/wAABgD6/+3/7//o//j/DAD8/+b/8P8IAAkABgD4//D/8P8UAAoAAwD4/wAA+f8OAAcAAAAFAPX/9v8TAAkA8v8EAPb/9/8dAA0A7/8CAPn/+f8SAAQA8/8CAOf/+v8DAAgA9P////H//P8IAAUA8//0/wIAAQAGAAgA9//7/wAA+/8EAP//+P/+////AgACAAsA8v/+/wIABQD7/wgA9v/7/wMABAD5/wAA/P/3/wEAAQD7//7//P/1/wQA///3//r////3/wMAAwD1//r/AwD6////AgD4//n/AwD8//7/AgD4//n/AwD+//3/AQD4//n/BQD///n/AAD6//j/BAABAPj/AAD9//v/AwADAPj//v/+//z/AwAEAPj//v8BAP7/AQADAPj//f8CAP////8EAPr//P8DAAAA/v8CAPv//P8DAAEA/f8BAP3//f8DAAIA/P8AAP7//f8DAAIA/P///wAA/f8BAAIA+//+/wEA//8AAAEA+//+/wEA/////wEA/P/+/wEA///+/wAA/f/9/wEAAAD9/wAA/f/+/wEAAQD8/////v/+/wAAAQD8////////////AQD9////AAD/////AAD+////AAAAAP//AAD///////8AAP//AAD//wAA//8AAP//"]},function(A,t){A.exports=["UklGRiQEAABXQVZFZm10IBAAAAABAAIAgLsAAADuAgAEABAAZGF0YQAEAAD+/wQA8/8ZAPr/DAD+/wMA/v8KAAQA/f8DAAMABADs//z/8v/z/8f/R/90/ob+//zAAWsDAwY3DKn9//tu93DvkwI6An4CuwJ0/BH7VPux92X0Gu7N/EX9mgfqCkkIiRMgBd4NQQGL/c0G/xBxAKELZATUA/sIHRSx+fkCyAUmBNEJIARlAdHz2AjNACcIsAW4AlECsvtJ/P/7K/tf++n8aP4W+g0FXAElAMn8nQHn/sT+Zv7N+9X2xvzM/O3+EvpqBBD7SQLd+vb/sPlw/JD72/3n+Rr+L/wS/vz6UQGg/Nf+Av5L/5X9Gv2//SP+mf3j/lf+v/2B/ZH/5P05/iL9MP9F/uf9UP4v/qv9mv7o/Xn+wP2k/8L+uP5J/tD+Dv/Y/bL+mP72/n3+pP+7/hAA+/5zAGH+Z/+u/g8Azv2y/6L+//9o/iIADP8VACz/CwCN/pb/1v4yAFP+wf+4/jsAcf5VAP3+bADa/nMA6f4sAOT+IQBd/v7/7v6aAIL+QADe/nEA0P4yAKz+CQCo/moAuf5xAN7+mAC8/jcANf9eAPX+IAA1/1kAAP9hAMz+PQD5/m0A2/4gAPr+UQDh/jQAEv9BAPH+FABN/zkASv9DADP/BABe/1IAGf8oAE3/RQAw/zIAQf8mADn/GgBE/xIAR/8hAD7/BABy/zEAKP/0/07/GwBX/z4ARf8mAFr/QQBV/zUAVP8eAFz/JABt/0EAUP8MAHz/KgBr/ycAYv8EAH3/MABl/x8Agv8bAIj/GgBv//z/ff8AAJX/IABu/+T/jv/r/4z/9/9n/77/pP8JAJD/EQCJ//r/q/8WAJ//GQCU/xYAtv8qAKr/PQCW/ysAwf8+ALb/OgC3/ygAz/8uAM7/OgDH/ygAz/8kAMz/OgC//xsA1f8qAMn/LwDN/xcA1f8oAMv/JQDR/xMAzf8bAM//HgDU/wUA2v8ZANL/EwDW/wEA1f8ZAMz/BwDX/wIA0v8SANT/BQDW/wMA0/8PANT/AADY/wIA1f8MANX/+f/a/wUA0v8IANf/+//Y/wUA0/8DANr/+f/Y/wQA1v8BANr/+f/Z/wUA1//8/9z/+v/Y/wYA2f/8/93//v/Y/wUA2v/9/93////Z/wUA3P/8/97/AgDa/wMA3v/8/97/AwDb/wIA3//9/97/BADd/wEA4f///9//BQDf/wAA4v8AAN//BQDf/wAA4/8CAN//BADh/wAA4/8DAOD/BADi////4/8DAOH/AwDk/wAA5P8FAOL/AgDl/wEA5P8FAOL/AQDl/wEA4/8EAOL/AQDj/wIA4P8DAN//AADg/wIA3v8CAOD/AADh/wEA4v8AAOP/AADm/wAA6P8AAOz/AADu/wAA","UklGRiQEAABXQVZFZm10IBAAAAABAAIAgLsAAADuAgAEABAAZGF0YQAEAAD//////f/+//7///8AAP////8BAAEA/f8AAAEAAQAFAAUA9//6/x0A2f/9/xMA3P+jAE//of9HAKP//gCj/77/Z/vi/28D9/ywDJAJIvr6AsX0Xec4BhcGzf23DZP7yfZ6C1//nwBDBIHyYgob/Tf3sQ41ANoKRA/A+E7yffAa9gD5EQUBDMwMygiqAHMAqPqhAGUB2/gE+a78H/+4APT6DwIUAA0HNwMhBfL8E/90A5n7dP9cALIC+v5C/q0AOv9kAogBHv01/+3/qAQD/ub8T/4vAOUA5P6KATv+ywEYAeT+KP6i/3gCFP6h/hr/+P83ACL/VADn/8UARQJI/4MAu/8qAlj+wf4iAPb/LgFJ/8QAUABAAI4ABf+k/3X/YgFK/ij/j/9HADoAi/+WAA0BVwC/ACL/LACe//cARv9i/xgAUgA0ACj/FgBgAIj/5P9M/7z/zv8/AKz/gv8sAEQA6/+I/yYAawDL/7T/xf8qAOv/FQCu/5n/EgAyAO3/i/9LAE4A+//R//P/FgDe/8z/u/8DADIALAAZALL/TAA8ABwAo//1/xwA/P/L/z0A6P8jAN7/7v+a/zAAwf/7/3//KQAuACwA9v8RAGYAIwBNADgAKgASAF0ADgANACEAMQDH//H/LQACAB0Ay////x0APAABAAQA2v8iAAcAEgDE/+v/FQD+/+P/DAD1/97/6v/4//X/EwD4/+7/5P8cAA0ACQDH//7/CQAXAAEA/P/5//j/CwAWAAEABQD9//n/AQAWAB0A7v/k/wAACQAmAP//9/8AAPn/8/8aAO//6/8fAOv/5v8hAP//5/8PAOf/AAAGAPn/6v8JAAYABgABAOv/1//1//L/+P8DABcA6f/8/wMACgD7/xAA3v/2//z/DADu//z/5v/5/wEA/P/6//7/7v/x/wQABgD5/wAA8v/w/wkAEQD2//j/+v8EAAcAEAD3//v/+v8CAAAACQD3//v//v/9/wUADAD2//X/AgAHAAAABwD2//T/BgAKAP7/AQD4//r/BAAIAPn/AAD3//f/BQAHAPv//v/7//n/BQAJAPj/+v/9//7/AgAGAPj/+f8BAAEAAgAFAPn/+v8BAAIAAAAEAPn/+f8CAAQA/v8BAPr/+v8CAAQA/P////v//P8CAAQA+//+//3//f8CAAUA+v/9//////8AAAQA+v/8////AAD//wIA+//8/wAAAQD+/wEA+//8/wAAAgD9/////P/9/wEAAgD8//7//f/9/wAAAgD8//3//v/+////AQD8//z/////////AAD8//3///8AAP7/AAD9//7///8AAP7////+//////8AAP7////+////////////////////","UklGRiQEAABXQVZFZm10IBAAAAABAAIAgLsAAADuAgAEABAAZGF0YQAEAAD//////v8AAP///////wAAAAAAAP7/AQABAAAABwD///X/BQAjAPL/CQDb/9D/GAAb/7sAYwCW/z0BcP/X/7T/2QDW+wH8yANCCCUJ5QT++UXmhPwhA78FuAxH+p78ifudBlAG9vmu/lAK2fdlB///cfjoCa0E7Akn9Yb/zvba+AkAHPywBGEBFwUNAL8AXAAGA20DFvmR/kz+F/06Ag/+GwHl/5EEKgJd/q0AP/ym/9n6EfxY/2H+/QFtAC4C6QBDAaMCo/20/+3/3f/p/fL9rv9V/6cBhQHuAX4AcwJYAaH/IP/P/gsApP0LAe7/sQBuAI0AAgGDAE4BzACe/5X//v+v/+f+Zf+gAOv/5QBhAOIApAANASYAuP+h/8b/HQBr/9//bACWAGEAFAB5AD0AWQDU/+D/Yf/p//D/s/+R/4QAMQBvABEAkQBfABQAJgDW/wwA8/8XALz/vf8zAFAAKwD1/zEAPwDJ/x0A7/8LAOX/FwDR//H/EQAdAO//6P8QAFEA2f8WABEAMgDy/xIA+f/s/xAALgDv////HQAvAPT/+f8iAAYAEgAFABoAGgD//w0A+f/0/xsAHgDx/9f/GAACAPH/8f8JAPf/GwALABEA7/8cAPT/CgD2//j/BQD8/+3/OgAgAAYA9f8PAN7/DgD9/9r/1//3/+3/9//1//b/8//5//f/AgAJAOf/+v8OAAMACwD9/+7/5f8eAAEA9//q//7/8P8WAP7/+//4/wIA+f8TAAIA9f/5/wcA+P8iAAgA9v/n/xoA//8gAAUABwDj/wAA9v8BAAUAFQDn/wMA7v8QABAAEQDm/wwA8f8aAAAABwDu/wcACgASAAEA7//w//f/BgARAAkA6P/3/wcADgAKAAYA4f/4/wYADgAAAPr/8P/9/xQACgAHAPn/7//9/xEAAgD+//L/8v/8/xUAAwDw//H/9f8CAAsA/v/q//L/+f8FAAYA/P/r//j///8GAAkA+//o//j/AQAIAP//+v/o//v/CAAIAPv/+P/w/wEACQAHAPj/+f/0/wIACwAFAPb/+f/4/wQACwACAPP/+f/+/wYACAD///L/+/8BAAYABQD9//P//P8FAAUAAgD7//T//f8HAAQA///7//f///8IAAMA/P/6//r/AQAIAAEA+v/6//3/AgAHAAAA+f/7/wAAAwAFAP7/+P/8/wIAAgACAP3/+f/9/wMAAwAAAPz/+v/+/wQAAgD+//z/+/8AAAQAAQD8//z//f8BAAQAAAD7//3///8BAAMA///7//3/AAACAAEA/v/7//7/AQABAAAA/v/9////AQAAAP///v/+////AAD/////////////////////////////","UklGRiQEAABXQVZFZm10IBAAAAABAAIAgLsAAADuAgAEABAAZGF0YQAEAAD////////+//////8AAAAA/v/+/wAAAQD8//3/CQAJAP3/+v8PAAcApABlABkBkwCO/i//lfqa/HQAcf/3BdkCzwJcBCMC0wMN/9/9wgI7AaECYfxV/Tf83vhn/xrt8Owx/8n7cgHABYb43QcZDh4WugNrA7P74gHu/9z/zv0t/acCiQHY/iv4qQOl/ysCE/0//XT9Sf4O//j9xfupAn394gHO+rsCXAFIAxQC9wIXBgcD2AQuAnb/9gJh/6wAVfxEAI4Bvf7oAFv/bALsAMQBe/88/joAT/4dAH39/v9LAXn/gwDI//QBdABcAA0A7f4lAMn///+9/tv/iABp/13/pP/dALv/w/8MAHv//f+y/6////7U/5AAZP+Z/8r/nQDR/5r/DwDr/xAA4v+s/3z/+P9uAOv/t/82AGcAHgCb/yQAFQBGAM7/CgD3/xoAegAaAOz/CgBHAA8Adv8/AAAABQC2/xIAAAA7ABQAKgCj/z4AAQAXAJz/JAADAAcA8f/1/2AAAQAlAPD/NgDx/1wA7v/4/wMAZADv//3/HQAkAFoA8P9FAPv/FgBIAPf/WQAHAEUACQD0/xIAQwDu/wMAwP9VALn/XwCw/yEA5f8sAPj/FgDD/1YAyv8rAOX/HQDo//j/IQAQACAAHwD9/yQAHQBAABgABQAiAAUAKAD3/wkACwAKAAMABwAJAPb/+f8GAOr/JQAHABMA6P8TAA4AGgD//woA8/8ZAP//GADu/w0A9v8SAAMABwD4/wQA5P8XAAQACgDq/wUA+/8VAAcACADs/xIAAAATAPH/+v/1//T/7f///+z/+v/y/+//9/8KAAcACgAJAPT/BAAKAAAABgAIAPL/9v8KAAMABAACAPr/9v8OAAIA+P/x//v/+f8MAPb/+P/w/wQA9f8MAPn////7/woA/v8PAAEAAgD1/xAAAQAPAP//AwD//xQABwALAAAABgADABAAAgAHAAAACAABAA8ABQAFAAMABwAEAA4ABwADAAEACQAFAAoAAwD//wAACQADAAUAAQD/////CAABAAMAAAD/////BwACAAEAAAD/////BwACAP7///8BAAAABgABAP7///8CAAAABAAAAP7///8DAAAAAwAAAP3///8DAAAAAQAAAP3//v8EAAAAAAD+//////8EAP/////+/wAA/v8EAP/////+/wEA/v8EAP///v/+/wIA//8DAP///v/+/wIA//8BAP///v/+/wMA//8BAP/////+/wMA//8AAP//AAD+/wQA//8AAP7/AQD//wIA////////AQD//wIA////////AQAAAAEAAAAAAP//AQD//wEAAAAAAP//AQAAAAEAAAAAAAAA","UklGRiQEAABXQVZFZm10IBAAAAABAAIAgLsAAADuAgAEABAAZGF0YQAEAAD+/wAA+v8AAPz/AAD//wAA/f8AAAEAAAD+/wAACQAAAAQAAAAZAAAAtgAAAFsBAABW/gAAH/oAAGcBAABoBwAAlAAAAO3/AAARAQAA+wIAAEoEAACe/gAAiv4AALD0AADJ8wAAkQQAAF34AABi8QAAPQAAAAH2AAD19AAADAMAAJwGAACTEAAA0AwAAJkHAACOBwAAuQEAANcDAAC6AgAAHwUAAHEFAAB0AwAAbgEAADz+AADYAQAAGAAAAJwCAADgAAAA//0AAMn+AAAT/AAAwP8AAOn9AAAJAAAAewEAAOn+AACN/wAAOv0AAO3+AADN/gAAcP8AACj/AACq/gAA+f4AAML9AACa/wAA/f4AAN7/AABo/wAA6/4AAE//AAAC/wAAEQAAAHX/AAB0AAAA5f8AAEwAAAB3AAAA5/8AAMIAAABCAAAAzgAAAE8AAAB3AAAAKAAAADMAAACqAAAALwAAAK4AAAASAAAAVgAAACgAAAAtAAAATAAAAP3/AAA7AAAA2/8AACQAAADw/wAALQAAADEAAAAlAAAAbAAAADMAAABUAAAAEAAAACgAAAD1/wAA9v8AAPr/AADu/wAALgAAABIAAABUAAAARAAAAGUAAABGAAAAOAAAAGAAAAAuAAAARQAAACEAAAAfAAAAAAAAAAkAAAAQAAAAAwAAABIAAADs/wAAEAAAAAYAAAASAAAAIgAAABEAAAADAAAABAAAAA8AAAD4/wAAHQAAAAsAAAAIAAAADgAAAP//AAAcAAAADwAAAAYAAAASAAAAFwAAAAMAAAAYAAAAEgAAAPr/AAAQAAAADQAAAAoAAAD3/wAABgAAAPb/AADf/wAA/v8AAPL/AAD6/wAAFAAAAAQAAAAEAAAAGwAAAAEAAAAMAAAAIAAAAAIAAAAdAAAAGAAAAAIAAAAcAAAAEgAAAAcAAAAeAAAADwAAAAQAAAAeAAAABAAAAAYAAAAZAAAAAQAAAA4AAAATAAAA/v8AAAoAAAAOAAAA+/8AAAsAAAAJAAAA+f8AAAsAAAABAAAA+f8AAAoAAAD9/wAA+v8AAAcAAAD5/wAA+v8AAAUAAAD3/wAA/f8AAAQAAAD2/wAAAAAAAAEAAAD3/wAAAgAAAAAAAAD4/wAAAwAAAP7/AAD6/wAABAAAAP3/AAD8/wAABAAAAPv/AAD+/wAAAwAAAPv/AAD//wAAAQAAAPv/AAAAAAAAAAAAAPv/AAACAAAA//8AAPz/AAACAAAA/v8AAP3/AAACAAAA/f8AAP7/AAABAAAA/f8AAP//AAABAAAA/f8AAAAAAAAAAAAA/v8AAAEAAAAAAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA"]},function(A,t,e){"use strict";t.getBrowserInfo=function(){const A=navigator.userAgent;let t,e=A.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*([\d\.]+)/i)||[];if(/trident/i.test(e[1]))return t=/\brv[ :]+(\d+)/g.exec(A)||[],{name:"IE",version:t[1]||""};if("Chrome"===e[1]&&null!=(t=A.match(/\bOPR|Edge\/(\d+)/)))return{name:"Opera",version:t[1]};e=e[2]?[e[1],e[2]]:[navigator.appName,navigator.appVersion,"-?"],null!=(t=A.match(/version\/([\d.]+)/i))&&e.splice(1,1,t[1]);let i=A.match(/android|ipad|iphone/i);return i||(i=A.match(/cros|linux|mac os x|windows/i)),{name:e[0],version:e[1],platform:i?i[0]:"unknown"}},t.patchSafari=function(){window.webkitAudioContext&&window.webkitOfflineAudioContext&&(window.AudioContext=window.webkitAudioContext,window.OfflineAudioContext=window.webkitOfflineAudioContext)}},function(A,t,e){"use strict";A.exports="1.0.6"}])})},function(A,t,e){"use strict";A.exports="0.0.4"}])});
\ No newline at end of file
diff --git a/src/renderer/js/showdown.min.js b/src/renderer/lib/showdown.min.js
similarity index 100%
rename from src/renderer/js/showdown.min.js
rename to src/renderer/lib/showdown.min.js
diff --git a/src/renderer/js/smoothscroll.js b/src/renderer/lib/smoothscroll.js
similarity index 100%
rename from src/renderer/js/smoothscroll.js
rename to src/renderer/lib/smoothscroll.js
diff --git a/src/renderer/js/sortable.min.js b/src/renderer/lib/sortable.min.js
similarity index 100%
rename from src/renderer/js/sortable.min.js
rename to src/renderer/lib/sortable.min.js
diff --git a/src/renderer/js/velocity.min.js b/src/renderer/lib/velocity.min.js
similarity index 100%
rename from src/renderer/js/velocity.min.js
rename to src/renderer/lib/velocity.min.js
diff --git a/src/renderer/js/vue-horizontal.js b/src/renderer/lib/vue-horizontal.js
similarity index 100%
rename from src/renderer/js/vue-horizontal.js
rename to src/renderer/lib/vue-horizontal.js
diff --git a/src/renderer/js/vue-observe-visibility.min.js b/src/renderer/lib/vue-observe-visibility.min.js
similarity index 100%
rename from src/renderer/js/vue-observe-visibility.min.js
rename to src/renderer/lib/vue-observe-visibility.min.js
diff --git a/src/renderer/js/vue.dev.js b/src/renderer/lib/vue.dev.js
similarity index 100%
rename from src/renderer/js/vue.dev.js
rename to src/renderer/lib/vue.dev.js
diff --git a/src/renderer/js/vue.js b/src/renderer/lib/vue.js
similarity index 100%
rename from src/renderer/js/vue.js
rename to src/renderer/lib/vue.js
diff --git a/src/renderer/js/vuedraggable.umd.min.js b/src/renderer/lib/vuedraggable.umd.min.js
similarity index 100%
rename from src/renderer/js/vuedraggable.umd.min.js
rename to src/renderer/lib/vuedraggable.umd.min.js
diff --git a/src/renderer/js/vuex.min.js b/src/renderer/lib/vuex.min.js
similarity index 100%
rename from src/renderer/js/vuex.min.js
rename to src/renderer/lib/vuex.min.js
diff --git a/src/renderer/main/app.js b/src/renderer/main/app.js
new file mode 100644
index 00000000..a5bfe938
--- /dev/null
+++ b/src/renderer/main/app.js
@@ -0,0 +1,28 @@
+import { app } from "./vueapp.js"
+import {CiderCache} from './cidercache.js'
+import {CiderFrontAPI} from './ciderfrontapi.js'
+import {simulateGamepad} from './gamepad.js'
+import {CiderAudio} from '../audio/audio.js'
+import {Events} from './events.js'
+import { wsapi } from "./wsapi_interop.js"
+
+
+// Define window objects
+window.app = app
+window.CiderAudio = CiderAudio
+window.CiderCache = CiderCache
+window.CiderFrontAPI = CiderFrontAPI
+window.wsapi = wsapi
+
+// Mount Vue to #app
+app.$mount("#app")
+
+// Init CiderAudio
+if (app.cfg.advanced.AudioContext){
+ CiderAudio.init()
+}
+
+// Import gamepad support
+app.simulateGamepad = simulateGamepad
+
+Events.InitEvents()
\ No newline at end of file
diff --git a/src/renderer/main/cidercache.js b/src/renderer/main/cidercache.js
new file mode 100644
index 00000000..c28550c1
--- /dev/null
+++ b/src/renderer/main/cidercache.js
@@ -0,0 +1,24 @@
+const CiderCache = {
+ async getCache(file) {
+ let cache = await ipcRenderer.sendSync("get-cache", file)
+ if (isJson(cache)) {
+ cache = JSON.parse(cache)
+ if (Object.keys(cache).length === 0) {
+ cache = false
+ }
+ } else {
+ cache = false
+ }
+ return cache
+ },
+ async putCache(file, data) {
+ console.log(`Caching ${file}`)
+ ipcRenderer.invoke("put-cache", {
+ file: file,
+ data: JSON.stringify(data)
+ })
+ return true
+ }
+}
+
+export {CiderCache}
\ No newline at end of file
diff --git a/src/renderer/main/ciderfrontapi.js b/src/renderer/main/ciderfrontapi.js
new file mode 100644
index 00000000..fc51f884
--- /dev/null
+++ b/src/renderer/main/ciderfrontapi.js
@@ -0,0 +1,16 @@
+const CiderFrontAPI = {
+ Objects: {
+ MenuEntry: function () {
+ this.id = ""
+ this.name = ""
+ this.onClick = () => {
+ }
+ }
+ },
+ AddMenuEntry(entry) {
+ app.pluginMenuEntries.push(entry)
+ app.pluginInstalled = true
+ }
+}
+
+export {CiderFrontAPI}
\ No newline at end of file
diff --git a/src/renderer/main/events.js b/src/renderer/main/events.js
new file mode 100644
index 00000000..23236e41
--- /dev/null
+++ b/src/renderer/main/events.js
@@ -0,0 +1,74 @@
+const Events = {
+ InitEvents() {
+ const app = window.app
+ // Key binds
+ document.addEventListener('keydown', function (e) {
+ if (e.keyCode === 70 && e.ctrlKey) {
+ app.$refs.searchInput.focus()
+ app.$refs.searchInput.select()
+ }
+ });
+
+// add event listener for when window.location.hash changes
+ window.addEventListener("hashchange", function () {
+ app.appRoute(window.location.hash)
+ });
+
+ // Key bind to unjam MusicKit in case it fails: CTRL+F10
+
+ document.addEventListener('keydown', function (event) {
+ if (event.ctrlKey && event.keyCode == 121) {
+ try {
+ app.mk._services.mediaItemPlayback._currentPlayer.stop()
+ } catch (e) {
+ }
+ try {
+ app.mk._services.mediaItemPlayback._currentPlayer.destroy()
+ } catch (e) {
+ }
+ }
+ });
+
+ window.addEventListener("mouseup", (e) => {
+ if (e.button === 3) {
+ e.preventDefault()
+ app.navigateBack()
+ } else if (e.button === 4) {
+ e.preventDefault()
+ app.navigateForward()
+ }
+ });
+
+ document.addEventListener('keydown', function (event) {
+ if (event.ctrlKey && event.keyCode == 122) {
+ try {
+ ipcRenderer.send('detachDT', '')
+ } catch (e) {
+ }
+ }
+ });
+
+ // Hang Timer
+ app.hangtimer = setTimeout(() => {
+ if (confirm("Cider is not responding. Reload the app?")) {
+ window.location.reload()
+ }
+ }, 10000)
+
+// Refresh Focus
+ function refreshFocus() {
+ if (document.hasFocus() == false) {
+ app.windowFocus(false)
+ } else {
+ app.windowFocus(true)
+ }
+ setTimeout(refreshFocus, 200);
+ }
+
+ app.getHTMLStyle()
+
+ refreshFocus();
+ }
+}
+
+export {Events}
\ No newline at end of file
diff --git a/src/renderer/main/gamepad.js b/src/renderer/main/gamepad.js
new file mode 100644
index 00000000..2c813df3
--- /dev/null
+++ b/src/renderer/main/gamepad.js
@@ -0,0 +1,326 @@
+function simulateGamepad () {
+ const app = window.app
+ app.chrome.showCursor = true
+ let cursorPos = [0, 0];
+ let intTabIndex = 0
+ const cursorSpeedPvt = 8
+ const cursorSize = 16
+ let scrollSpeed = 8
+ let buttonPressDelay = 500
+ let stickDeadZone = 0.2
+ let scrollGroup = null
+ let scrollGroupY = null
+ let elementFocusEnabled = true
+
+ let cursorSpeed = cursorSpeedPvt
+
+ let lastButtonPress = {
+
+ }
+
+ var sounds = {
+ Confirm: new Audio("./sounds/confirm.ogg"),
+ Menu: new Audio("./sounds/btn1.ogg"),
+ Hover: new Audio("./sounds/hover.ogg")
+ }
+
+ let element = document.elementFromPoint(0, 0)
+ let elementType = 0
+
+ function appLoop() {
+ var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
+ if (!gamepads) {
+ return;
+ }
+
+ var gp = gamepads[0];
+
+ // LEFT STICK
+ if (gp.axes[0] > stickDeadZone) {
+ cursorPos[0] += (gp.axes[0] * cursorSpeed)
+ } else if (gp.axes[0] < -stickDeadZone) {
+ cursorPos[0] += (gp.axes[0] * cursorSpeed)
+ }
+
+ if (gp.axes[1] > stickDeadZone) {
+ cursorPos[1] += (gp.axes[1] * cursorSpeed)
+ } else if (gp.axes[1] < -stickDeadZone) {
+ cursorPos[1] += (gp.axes[1] * cursorSpeed)
+ }
+
+ if (cursorPos[0] < cursorSize) {
+ cursorPos[0] = cursorSize
+ }
+ if (cursorPos[1] < cursorSize) {
+ cursorPos[1] = cursorSize
+ }
+ if (cursorPos[0] > window.innerWidth - cursorSize) {
+ cursorPos[0] = window.innerWidth - cursorSize
+ }
+ if (cursorPos[1] > window.innerHeight - cursorSize) {
+ cursorPos[1] = window.innerHeight - cursorSize
+ }
+
+
+ // RIGHT STICK.
+ if (scrollGroupY) {
+ if (gp.axes[3] > stickDeadZone) {
+ $(scrollGroupY).scrollTop($(scrollGroupY).scrollTop() + (gp.axes[3] * scrollSpeed))
+ elementFocusEnabled = false
+ } else if (gp.axes[3] < -stickDeadZone) {
+ $(scrollGroupY).scrollTop($(scrollGroupY).scrollTop() + (gp.axes[3] * scrollSpeed))
+ elementFocusEnabled = false
+ } else {
+ elementFocusEnabled = true
+ }
+ }
+
+
+
+ if (scrollGroup) {
+ if (gp.axes[2] > stickDeadZone) {
+ $(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + (gp.axes[2] * scrollSpeed))
+ elementFocusEnabled = false
+ } else if (gp.axes[2] < -stickDeadZone) {
+ $(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + (gp.axes[2] * scrollSpeed))
+ elementFocusEnabled = false
+ } else {
+ elementFocusEnabled = true
+ }
+ }
+
+
+ $(".cursor").css({
+ top: cursorPos[1] + "px",
+ left: cursorPos[0] + "px",
+ display: "block"
+ })
+
+ // A BUTTON
+ if (gp.buttons[0].pressed) {
+ if (!lastButtonPress["A"]) {
+ lastButtonPress["A"] = 0
+ }
+ if (Date.now() - lastButtonPress["A"] > buttonPressDelay) {
+ lastButtonPress["A"] = Date.now()
+ sounds.Confirm.play()
+ if (elementType == 0) {
+ document.activeElement.dispatchEvent(new Event("click"))
+ document.activeElement.dispatchEvent(new Event("controller-click"))
+ } else {
+ element.dispatchEvent(new Event("click"))
+ element.dispatchEvent(new Event("controller-click"))
+ }
+ }
+ }
+
+ // B BUTTON
+ if (gp.buttons[1].pressed) {
+
+ if (!lastButtonPress["B"]) {
+ lastButtonPress["B"] = 0
+ }
+ if (Date.now() - lastButtonPress["B"] > buttonPressDelay) {
+ lastButtonPress["B"] = Date.now()
+ if (elementType == 0) {
+ document.activeElement.dispatchEvent(new Event("contextmenu"))
+ setTimeout(() => {
+ if ($(".menu-option").length > 0) {
+ let bounds = $(".menu-option")[0].getBoundingClientRect()
+ cursorPos[0] = bounds.left + (bounds.width / 2)
+ cursorPos[1] = bounds.top + (bounds.height / 2)
+ }
+ }, 100)
+ } else {
+ element.dispatchEvent(new Event("contextmenu"))
+ }
+ }
+
+ }
+
+ // right bumper
+ if (gp.buttons[5].pressed) {
+ if (!lastButtonPress["RB"]) {
+ lastButtonPress["RB"] = 0
+ }
+ if (Date.now() - lastButtonPress["RB"] > buttonPressDelay) {
+ lastButtonPress["RB"] = Date.now()
+ app.navigateForward()
+
+ }
+ }
+
+ // left bumper
+ if (gp.buttons[4].pressed) {
+ if (!lastButtonPress["LB"]) {
+ lastButtonPress["LB"] = 0
+ }
+ if (Date.now() - lastButtonPress["LB"] > buttonPressDelay) {
+ lastButtonPress["LB"] = Date.now()
+ app.navigateBack()
+
+ }
+ }
+
+
+
+ // cursor hover
+ if (elementFocusEnabled) {
+ element = document.elementFromPoint(cursorPos[0], cursorPos[1])
+ }
+
+ if (element) {
+
+ let closest = element.closest("[tabindex], input, button, a")
+
+ // VERT SCROLL
+ let scrollGroupCloY = element.closest(`[scrollaxis="y"]`)
+ if (scrollGroupCloY) {
+ scrollGroupY = scrollGroupCloY
+ }
+
+
+ // HOZ SCROLL
+ let scrollGroupClo = element.closest(".v-hl-container")
+
+ if (scrollGroupClo) {
+ if (scrollGroupClo.classList.contains("v-hl-container")) {
+ scrollGroup = scrollGroupClo
+ scrollGroup.style["scroll-snap-type"] = "unset"
+ } else {
+ scrollGroup.style["scroll-snap-type"] = ""
+ scrollGroup = null
+ }
+ }
+
+ if (closest) {
+ elementType = 0
+ closest.focus()
+ } else {
+ if (closest) {
+ closest.blur()
+ }
+ elementType = 1
+ element.focus()
+ }
+ cursorSpeed = cursorSpeedPvt
+ if (!element.classList.contains("app-chrome")
+ && !element.classList.contains("app-content")) {
+ cursorSpeed = cursorSpeedPvt
+ }
+ // console.log($._data($(element), "events"))
+ } else {
+ cursorSpeed = 12
+ }
+ // console.log(gp.axes[0], gp.axes[1])
+ start = requestAnimationFrame(appLoop);
+ }
+
+// controller pairing
+ notyf.error("Press the button on your controller to pair it to Cider.")
+ window.addEventListener("gamepadconnected", function (e) {
+ console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
+ e.gamepad.index, e.gamepad.id,
+ e.gamepad.buttons.length, e.gamepad.axes.length);
+ notyf.success("Pairing successful!")
+ appLoop()
+ }, { once: true });
+
+ document.addEventListener("keydown", (e) => {
+ sounds.Confirm.currentTime = 0
+ sounds.Menu.currentTime = 0
+ sounds.Hover.currentTime = 0
+ let tabbable = $("[tabindex]")
+ console.log(e.key)
+ switch (e.key) {
+ default:
+ break;
+ case "ArrowLeft":
+ e.preventDefault()
+
+ cursorPos[0] -= cursorSpeed
+ break;
+ case "ArrowRight":
+ e.preventDefault()
+
+ cursorPos[0] += cursorSpeed
+ break;
+ case "ArrowUp":
+ e.preventDefault()
+
+ cursorPos[1] -= cursorSpeed
+ // sounds.Hover.play()
+ // if(intTabIndex <= 0) {
+ // intTabIndex = 0
+ // }else{
+ // intTabIndex--
+ // }
+ // $(tabbable[intTabIndex]).focus()
+ // $("#app-content").scrollTop($(document.activeElement).offset().top)
+ break;
+ case "ArrowDown":
+ e.preventDefault()
+
+ cursorPos[1] += cursorSpeed
+ // if(intTabIndex < tabbable.length) {
+ // intTabIndex++
+ // }else{
+ // intTabIndex = tabbable.length
+ // }
+ // $(tabbable[intTabIndex]).focus()
+ // $("#app-content").scrollTop($(document.activeElement).offset().top)
+ break;
+ case "c":
+ app.resetState()
+ break;
+ case "x":
+ // set cursorPos to the top right of the screen
+ // sounds.Menu.play()
+ if (elementType == 0) {
+ document.activeElement.dispatchEvent(new Event("contextmenu"))
+ } else {
+ element.dispatchEvent(new Event("contextmenu"))
+ }
+
+ e.preventDefault()
+ break;
+ case "z":
+ sounds.Confirm.play()
+ if (elementType == 0) {
+ document.activeElement.dispatchEvent(new Event("click"))
+ document.activeElement.dispatchEvent(new Event("controller-click"))
+ } else {
+ element.dispatchEvent(new Event("click"))
+ element.dispatchEvent(new Event("controller-click"))
+ }
+
+ e.preventDefault()
+ break;
+ }
+
+ $(".cursor").css({
+ top: cursorPos[1] + "px",
+ left: cursorPos[0] + "px"
+ })
+ function lerp(a, b, n) {
+ return (1 - n) * a + n * b
+ }
+
+
+ element = document.elementFromPoint(cursorPos[0], cursorPos[1])
+
+ if (element) {
+ let closest = element.closest("[tabindex], input, button, a")
+ if (closest) {
+ elementType = 0
+ closest.focus()
+ } else {
+ elementType = 1
+ element.focus()
+ }
+ }
+ console.log(element)
+ });
+}
+
+export {simulateGamepad}
\ No newline at end of file
diff --git a/src/renderer/main/vueapp.js b/src/renderer/main/vueapp.js
new file mode 100644
index 00000000..186b62ce
--- /dev/null
+++ b/src/renderer/main/vueapp.js
@@ -0,0 +1,4070 @@
+import {store} from './vuex-store.js';
+
+Vue.use(VueHorizontal);
+Vue.use(VueObserveVisibility);
+
+const app = new Vue({
+ store: store,
+ data: {
+ version: ipcRenderer.sendSync("get-version"),
+ appMode: "player",
+ ipcRenderer: ipcRenderer,
+ cfg: ipcRenderer.sendSync("getStore"),
+ isDev: ipcRenderer.sendSync("is-dev"),
+ drawertest: false,
+ platform: "",
+ mk: {},
+ quickPlayQuery: "",
+ pluginInstalled: false,
+ pluginMenuEntries: [],
+ lz: ipcRenderer.sendSync("get-i18n", "en_US"),
+ lzListing: ipcRenderer.sendSync("get-i18n-listing"),
+ search: {
+ term: "",
+ hints: [],
+ showHints: false,
+ results: {},
+ resultsSocial: {},
+ limit: 10
+ },
+ fullscreenLyrics: false,
+ playerLCD: {
+ playbackDuration: 0,
+ desiredDuration: 0,
+ userInteraction: false
+ },
+ drawer: {
+ open: false,
+ panel: ""
+ },
+ browsepage: [],
+ listennow: [],
+ madeforyou: [],
+ radio: {
+ personal: []
+ },
+ mklang: 'en',
+ webview: {
+ url: "",
+ title: "",
+ loading: false
+ },
+ showingPlaylist: [],
+ appleCurator: [],
+ artistPage: {
+ data: {},
+ },
+ library: {
+ backgroundNotification: {
+ show: false,
+ message: "",
+ total: 0,
+ progress: 0
+ },
+ songs: {
+ sortingOptions: {
+ "albumName": "0",
+ "artistName": "0",
+ "name": "0",
+ "genre": "0",
+ "releaseDate": "0",
+ "durationInMillis": "0",
+ "dateAdded": "0"
+ },
+ sorting: "name",
+ sortOrder: "asc",
+ listing: [],
+ meta: { total: 0, progress: 0 },
+ search: "",
+ displayListing: [],
+ downloadState: 0 // 0 = not started, 1 = in progress, 2 = complete, 3 = empty library
+ },
+ albums: {
+ sortingOptions: {
+ "albumName": "0",
+ "artistName": "0",
+ "name": "0",
+ "genre": "0"
+ },
+ viewAs: 'covers',
+ sorting: ["dateAdded", "name"], // [0] = recentlyadded page, [1] = albums page
+ sortOrder: ["desc", "asc"], // [0] = recentlyadded page, [1] = albums page
+ listing: [],
+ meta: { total: 0, progress: 0 },
+ search: "",
+ displayListing: [],
+ downloadState: 0 // 0 = not started, 1 = in progress, 2 = complete, 3 = empty library
+ },
+ artists: {
+ sortingOptions: {
+ "artistName": "0",
+ "name": "0",
+ "genre": "0",
+ "releaseDate": "0"
+ },
+ viewAs: 'covers',
+ sorting: ["dateAdded", "name"], // [0] = recentlyadded page, [1] = albums page
+ sortOrder: ["desc", "asc"], // [0] = recentlyadded page, [1] = albums page
+ listing: [],
+ meta: { total: 0, progress: 0 },
+ search: "",
+ displayListing: [],
+ downloadState: 0 // 0 = not started, 1 = in progress, 2 = complete, 3 = empty library
+ },
+ },
+ playlists: {
+ listing: [],
+ details: {},
+ loadingState: 0, // 0 loading, 1 loaded, 2 error
+ id: "",
+ trackMapping: {}
+ },
+ webremoteurl: "",
+ webremoteqr: "",
+ mxmtoken: "",
+ mkIsReady: false,
+ playerReady: false,
+ animateBackground: false,
+ currentArtUrl: '',
+ currentArtUrlRaw: '',
+ lyricon: false,
+ currentTrackID: '',
+ currentTrackIDBG: '',
+ lyrics: [],
+ currentLyricsLine: 0,
+ lyriccurrenttime: 0,
+ richlyrics: [],
+ lyricsMediaItem: {},
+ lyricsDebug: {
+ current: 0,
+ start: 0,
+ end: 0
+ },
+ v3: {
+ requestBody: {
+ platform: "web"
+ }
+ },
+ tmpHeight: '',
+ tmpWidth: '',
+ tmpVar: [],
+ notification: false,
+ chrome: {
+ appliedTheme: {
+ location: "",
+ info: {}
+ },
+ desiredPageTransition: "wpfade_transform",
+ hideUserInfo: ipcRenderer.sendSync("is-dev") || false,
+ artworkReady: false,
+ userinfo: {
+ "id": "",
+ "attributes": {
+ "name": "Cider User",
+ "handle": "CiderUser",
+ "artwork": { "url": "./assets/logocut.png" }
+ }
+ },
+ menuOpened: false,
+ maximized: false,
+ drawerOpened: false,
+ drawerState: "queue",
+ topChromeVisible: true,
+ progresshover: false,
+ windowControlPosition: "right",
+ contentAreaScrolling: true,
+ showCursor: false
+ },
+ collectionList: {
+ response: {},
+ title: "",
+ type: ""
+ },
+ prevButtonBackIndicator: false,
+ currentSongInfo: {},
+ page: "",
+ pageHistory: [],
+ songstest: false,
+ hangtimer: null,
+ selectedMediaItems: [],
+ routes: ["browse", "listen_now", "radio"],
+ musicBaseUrl: "https://api.music.apple.com/",
+ modals: {
+ addToPlaylist: false,
+ spatialProperties: false,
+ qrcode: false,
+ equalizer: false,
+ audioSettings: false,
+ pluginMenu: false,
+ audioControls: false,
+ showPlaylist: false,
+ castMenu: false
+ },
+ socialBadges: {
+ badgeMap: {},
+ version: "",
+ mediaItems: [],
+ mediaItemDLState: 0 // 0 = not started, 1 = in progress, 2 = complete
+ },
+ menuPanel: {
+ visible: false,
+ event: null,
+ content: {
+ name: "",
+ items: {},
+ headerItems: {}
+ }
+ },
+ pauseButtonTimer: null,
+ activeCasts: []
+ },
+ watch: {
+ cfg: {
+ handler: function (val, oldVal) {
+ console.log(`cfg changed from ${oldVal} to ${val}`);
+ ipcRenderer.send("setStore", val);
+ },
+ deep: true
+ },
+ page: () => {
+ document.getElementById("app-content").scrollTo(0, 0);
+ app.resetState()
+ },
+ showingPlaylist: () => {
+ if (!app.modals.showPlaylist) {
+ document.getElementById("app-content").scrollTo(0, 0);
+ app.resetState()
+ }
+ },
+ artistPage: () => {
+ document.getElementById("app-content").scrollTo(0, 0);
+ app.resetState()
+ }
+ },
+ methods: {
+ songLinkShare(amUrl) {
+ notyf.open({ type: "info", className: "notyf-info", message: app.getLz('term.song.link.generate') })
+ let self = this
+ let httpRequest = new XMLHttpRequest();
+ httpRequest.open('GET', `https://api.song.link/v1-alpha.1/links?url=${amUrl}&userCountry=US`, true);
+ httpRequest.send();
+ httpRequest.onreadystatechange = function () {
+ if (httpRequest.readyState === 4) {
+ if (httpRequest.status === 200) {
+ let response = JSON.parse(httpRequest.responseText);
+ console.log(response);
+ self.copyToClipboard(response.pageUrl)
+ } else {
+ console.log('There was a problem with the request.');
+ notyf.error(app.getLz('term.requestError'))
+ }
+ }
+ }
+ },
+ mainMenuVisibility(val) {
+ if (val) {
+ (this.mk.isAuthorized) ? this.chrome.menuOpened = !this.chrome.menuOpened : false;
+ if (!this.mk.isAuthorized) {
+ this.mk.authorize()
+ }
+ } else {
+ setTimeout(() => {
+ this.chrome.menuOpened = false
+ }, 100)
+ }
+ },
+ stringTemplateParser(expression, valueObj) {
+ const templateMatcher = /{{\s?([^{}\s]*)\s?}}/g;
+ let text = expression.replace(templateMatcher, (substring, value, index) => {
+ value = valueObj[value];
+ return value;
+ });
+ return text
+ // stringTemplateParser('my name is {{name}} and age is {{age}}', {name: 'Tom', age:100})
+ },
+ async setLz(lang) {
+ if (lang == "") {
+ lang = this.cfg.general.language
+ }
+ this.lz = ipcRenderer.sendSync("get-i18n", lang)
+ this.mklang = await this.MKJSLang()
+ },
+ getLz(message, options = {}) {
+ if (this.lz[message]) {
+ if (options["count"]) {
+ if (typeof this.lz[message] === "object") {
+ let type = window.fastPluralRules.getPluralFormNameForCardinalByLocale(this.cfg.general.language.replace("_", "-"), options["count"]);
+ return this.lz[message][type] ?? ((this.lz[message])[Object.keys(this.lz[message])[0]] ?? this.lz[message])
+ } else {
+ // fallback English plural forms ( old i18n )
+ if (options["count"] > 1) {
+ return this.lz[message + "s"] ?? this.lz[message]
+ } else {
+ return this.lz[message] ?? this.lz[message + "s"]
+ }
+ }
+ } else if (typeof this.lz[message] === "object") {
+ return (this.lz[message])[Object.keys(this.lz[message])[0]]
+ }
+ return this.lz[message]
+ } else {
+ return message
+ }
+ },
+ setLzManual() {
+ app.$data.library.songs.sortingOptions = {
+ "albumName": app.getLz('term.sortBy.album'),
+ "artistName": app.getLz('term.sortBy.artist'),
+ "name": app.getLz('term.sortBy.name'),
+ "genre": app.getLz('term.sortBy.genre'),
+ "releaseDate": app.getLz('term.sortBy.releaseDate'),
+ "durationInMillis": app.getLz('term.sortBy.duration'),
+ "dateAdded": app.getLz('term.sortBy.dateAdded')
+ }
+
+ app.$data.library.albums.sortingOptions = {
+ "albumName": app.getLz('term.sortBy.album'),
+ "artistName": app.getLz('term.sortBy.artist'),
+ "name": app.getLz('term.sortBy.name'),
+ "genre": app.getLz('term.sortBy.genre')
+ }
+
+ app.$data.library.artists.sortingOptions = {
+ "artistName": app.getLz('term.sortBy.artist'),
+ "name": app.getLz('term.sortBy.name'),
+ "genre": app.getLz('term.sortBy.genre'),
+ "releaseDate": app.getLz('term.sortBy.releaseDate')
+ }
+ },
+ async showSocialListeningTo() {
+ let contentIds = Object.keys(app.socialBadges.badgeMap)
+ app.showCollection({ data: this.socialBadges.mediaItems }, "Friends Listening To", "albums")
+ if (this.socialBadges.mediaItemDLState == 1 || this.socialBadges.mediaItemDLState == 2) {
+ return
+ }
+ this.socialBadges.mediaItemDLState = 2
+ await asyncForEach(contentIds, async (item) => {
+ try {
+ let type = "albums"
+ if (item.includes("pl.")) {
+ type = "playlists"
+ }
+ if (item.includes("ra.")) {
+ type = "stations"
+ }
+ let found = await app.mk.api.v3.music(`/v1/catalog/us/${type}/${item}`)
+ this.socialBadges.mediaItems.push(found.data.data[0])
+ } catch (e) {
+
+ }
+ })
+ },
+ async openAppleMusicURL(url) {
+ let properties = MusicKit.formattedMediaURL(url)
+ let item = {
+ id: properties.contentId,
+ attributes: {
+ playParams: {
+ id: properties.contentId,
+ kind: properties.kind,
+ }
+ },
+ type: properties.kind,
+ kind: properties.kind
+ }
+ app.routeView(item)
+ },
+ saveFile(fileName, urlFile) {
+ let a = document.createElement("a");
+ a.style = "display: none";
+ document.body.appendChild(a);
+ a.href = urlFile;
+ a.download = fileName;
+ a.click();
+ window.URL.revokeObjectURL(url);
+ a.remove();
+ },
+ async showMenuPanel(data, event) {
+ app.menuPanel.visible = true;
+ app.menuPanel.content.name = data.name ?? "";
+ app.menuPanel.content.items = data.items ?? {};
+ app.menuPanel.content.headerItems = data.headerItems ?? {};
+ if (event) {
+ app.menuPanel.event = event;
+ }
+ },
+ async getSvgIcon(url) {
+ let response = await fetch(url);
+ let data = await response.text();
+ return data;
+ },
+ getSocialBadges(cb = () => {
+ }) {
+ let self = this
+ try {
+ app.mk.api.v3.music("/v1/social/badging-map").then(data => {
+ self.socialBadges.badgeMap = data.data.results.badgingMap
+ cb(data.data.results.badgingMap)
+ })
+ } catch (ex) {
+ this.socialBadges.badgeMap = {}
+ }
+ },
+ addFavorite(id, type) {
+ this.cfg.home.favoriteItems.push({
+ id: id,
+ type: type
+ });
+ },
+ modularUITest(val = false) {
+ this.fullscreenLyrics = val;
+ if (val) {
+ document.querySelector("#app-main").classList.add("modular-fs")
+ } else {
+ document.querySelector("#app-main").classList.remove("modular-fs")
+ }
+ },
+ navigateBack() {
+ this.chrome.desiredPageTransition = "wpfade_transform_backwards"
+ return new Promise((resolve, reject) => {
+ history.back()
+ setTimeout(() => {
+
+
+ resolve(this.chrome.desiredPageTransition = "wpfade_transform")
+ }, 100)
+ })
+ },
+ navigateForward() {
+ history.forward()
+ },
+ getHTMLStyle() {
+ // document.querySelector("html").style.background = "#222";
+ document.querySelector("body").classList.add("notransparency")
+ },
+ resetState() {
+ this.menuPanel.visible = false;
+ app.selectedMediaItems = [];
+ this.chrome.contentAreaScrolling = true
+ for (let key in app.modals) {
+ app.modals[key] = false;
+ }
+ },
+ promptAddToPlaylist() {
+ app.modals.addToPlaylist = true;
+ },
+ async addSelectedToNewPlaylist() {
+ let self = this
+ let pl_items = []
+ for (let i = 0; i < self.selectedMediaItems.length; i++) {
+ if (self.selectedMediaItems[i].kind == "song" || self.selectedMediaItems[i].kind == "songs") {
+ self.selectedMediaItems[i].kind = "songs"
+ pl_items.push({
+ id: self.selectedMediaItems[i].id,
+ type: self.selectedMediaItems[i].kind
+ })
+ } else if ((self.selectedMediaItems[i].kind == "album" || self.selectedMediaItems[i].kind == "albums") && self.selectedMediaItems[i].isLibrary != true) {
+ self.selectedMediaItems[i].kind = "albums"
+ let res = await self.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/albums/${self.selectedMediaItems[i].id}/tracks`);
+ let ids = res.data.data.map(function (i) {
+ return { id: i.id, type: i.type }
+ })
+ pl_items = pl_items.concat(ids)
+ } else if (self.selectedMediaItems[i].kind == "library-song" || self.selectedMediaItems[i].kind == "library-songs") {
+ self.selectedMediaItems[i].kind = "library-songs"
+ pl_items.push({
+ id: self.selectedMediaItems[i].id,
+ type: self.selectedMediaItems[i].kind
+ })
+ } else if ((self.selectedMediaItems[i].kind == "library-album" || self.selectedMediaItems[i].kind == "library-albums") || (self.selectedMediaItems[i].kind == "album" && self.selectedMediaItems[i].isLibrary == true)) {
+ self.selectedMediaItems[i].kind = "library-albums"
+ let res = await self.mk.api.v3.music(`/v1/me/library/albums/${self.selectedMediaItems[i].id}/tracks`);
+ let ids = res.data.data.map(function (i) {
+ return { id: i.id, type: i.type }
+ })
+ pl_items = pl_items.concat(ids)
+ } else {
+ pl_items.push({
+ id: self.selectedMediaItems[i].id,
+ type: self.selectedMediaItems[i].kind
+ })
+ }
+ }
+ this.modals.addToPlaylist = false
+ app.newPlaylist(app.getLz('term.newPlaylist'), pl_items)
+ },
+ async addSelectedToPlaylist(playlist_id) {
+ let self = this
+ let pl_items = []
+ for (let i = 0; i < self.selectedMediaItems.length; i++) {
+ if (self.selectedMediaItems[i].kind == "song" || self.selectedMediaItems[i].kind == "songs") {
+ self.selectedMediaItems[i].kind = "songs"
+ pl_items.push({
+ id: self.selectedMediaItems[i].id,
+ type: self.selectedMediaItems[i].kind
+ })
+ } else if ((self.selectedMediaItems[i].kind == "album" || self.selectedMediaItems[i].kind == "albums") && self.selectedMediaItems[i].isLibrary != true) {
+ self.selectedMediaItems[i].kind = "albums"
+ let res = await self.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/albums/${self.selectedMediaItems[i].id}/tracks`);
+ let ids = res.data.data.map(function (i) {
+ return { id: i.id, type: i.type }
+ })
+ pl_items = pl_items.concat(ids)
+ } else if (self.selectedMediaItems[i].kind == "library-song" || self.selectedMediaItems[i].kind == "library-songs") {
+ self.selectedMediaItems[i].kind = "library-songs"
+ pl_items.push({
+ id: self.selectedMediaItems[i].id,
+ type: self.selectedMediaItems[i].kind
+ })
+ } else if ((self.selectedMediaItems[i].kind == "library-album" || self.selectedMediaItems[i].kind == "library-albums") || (self.selectedMediaItems[i].kind == "album" && self.selectedMediaItems[i].isLibrary == true)) {
+ self.selectedMediaItems[i].kind = "library-albums"
+ let res = await self.mk.api.v3.music(`/v1/me/library/albums/${self.selectedMediaItems[i].id}/tracks`);
+ let ids = res.data.data.map(function (i) {
+ return { id: i.id, type: i.type }
+ })
+ pl_items = pl_items.concat(ids)
+ } else {
+ pl_items.push({
+ id: self.selectedMediaItems[i].id,
+ type: self.selectedMediaItems[i].kind
+ })
+ }
+
+ }
+ this.modals.addToPlaylist = false
+ await app.mk.api.v3.music(
+ `/v1/me/library/playlists/${playlist_id}/tracks`, {}, {
+ fetchOptions: {
+ method: "POST",
+ body: JSON.stringify({
+ data: pl_items
+ })
+ }
+ }
+ ).then(() => {
+ if (this.page == 'playlist_' + this.showingPlaylist.id) {
+ this.getPlaylistFromID(this.showingPlaylist.id, true)
+ }
+ })
+ },
+ async init() {
+ let self = this
+ if (this.cfg.visual.theme != "default.less" && this.cfg.visual.theme != "") {
+ this.setTheme(this.cfg.visual.theme)
+ }
+ this.setLz(this.cfg.general.language)
+ this.setLzManual()
+ clearTimeout(this.hangtimer)
+ this.mk = MusicKit.getInstance()
+ let needsReload = (typeof localStorage["music.ampwebplay.media-user-token"] == "undefined")
+ this.mk.authorize().then(() => {
+ self.mkIsReady = true
+ if (needsReload) {
+ document.location.reload()
+ }
+ })
+ this.$forceUpdate()
+ if (this.isDev) {
+ this.mk.privateEnabled = true
+ // Hide UserInfo if Dev mode
+ } else {
+ // Get Hide User from Settings
+ this.chrome.hideUserInfo = !this.cfg.visual.showuserinfo
+ this.mk.privateEnabled = this.cfg.general.privateEnabled
+ }
+ if (this.cfg.visual.hw_acceleration == "disabled") {
+ document.body.classList.add("no-gpu")
+ }
+ this.mk._services.timing.mode = 0
+ this.platform = ipcRenderer.sendSync('cider-platform');
+
+ this.mklang = await this.MKJSLang()
+
+ try {
+ // Set profile name
+ this.chrome.userinfo = (await app.mk.api.v3.music(`/v1/me/social-profile`)).data.data[0]
+ } catch (err) {
+ }
+
+ this.mk._bag.features['seamless-audio-transitions'] = this.cfg.audio.seamless_audio
+
+ // API Fallback
+ if (!this.chrome.userinfo) {
+ this.chrome.userinfo = {
+ "id": "",
+ "attributes": {
+ "name": "Cider User",
+ "handle": "CiderUser",
+ "artwork": { "url": "./assets/logocut.png" }
+ }
+ }
+ }
+ MusicKitInterop.init()
+ // Set the volume
+
+ // Check the value of this.cfg.audio.muted
+ if (!this.cfg.audio.muted) {
+ // Set the mk.volume to the last stored volume data
+ this.mk.volume = this.cfg.audio.volume
+ } else if (this.cfg.audio.muted) {
+ // Set mk.volume to -1 (setting to 0 wont work, so temp solution setting to -1)
+ this.mk.volume = -1;
+ }
+ // ipcRenderer.invoke('getStoreValue', 'audio.volume').then((value) => {
+ // self.mk.volume = value
+ // })
+
+ // load cached library
+ let librarySongs = await CiderCache.getCache("library-songs")
+ let libraryAlbums = await CiderCache.getCache("library-albums")
+ if (librarySongs) {
+ this.library.songs.listing = librarySongs
+ this.library.songs.displayListing = this.library.songs.listing
+ }
+ if (libraryAlbums) {
+ this.library.albums.listing = libraryAlbums
+ this.library.albums.displayListing = this.library.albums.listing
+ }
+
+ window.onbeforeunload = function (e) {
+ window.localStorage.setItem("currentTrack", JSON.stringify(app.mk.nowPlayingItem))
+ window.localStorage.setItem("currentTime", JSON.stringify(app.mk.currentPlaybackTime))
+ window.localStorage.setItem("currentQueue", JSON.stringify(app.mk.queue.items))
+ };
+
+ if (typeof MusicKit.PlaybackBitrate[app.cfg.audio.quality] !== "string") {
+ app.mk.bitrate = MusicKit.PlaybackBitrate[app.cfg.audio.quality]
+ } else {
+ app.mk.bitrate = 256
+ app.cfg.audio.quality = "HIGH"
+ }
+
+ switch (this.cfg.general.resumeOnStartupBehavior) {
+ default:
+ case "local":
+ // load last played track
+ try {
+ let lastItem = window.localStorage.getItem("currentTrack")
+ let time = window.localStorage.getItem("currentTime")
+ let queue = window.localStorage.getItem("currentQueue")
+ if (lastItem != null) {
+ lastItem = JSON.parse(lastItem)
+ let kind = lastItem.attributes.playParams.kind;
+ let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
+ app.mk.setQueue({
+ [truekind]: [lastItem.attributes.playParams.id],
+ parameters: { l: app.mklang }
+ })
+ app.mk.mute()
+ setTimeout(() => {
+ app.mk.play().then(() => {
+ app.mk.pause().then(() => {
+ if (time != null) {
+ app.mk.seekToTime(time)
+ }
+ app.mk.unmute()
+ if (queue != null) {
+ queue = JSON.parse(queue)
+ if (queue && queue.length > 0) {
+ let ids = queue.map(e => (e.playParams ? e.playParams.id : (e.attributes.playParams ? e.attributes.playParams.id : '')))
+ let i = 0;
+ if (ids.length > 0) {
+ for (let id of ids) {
+ if (!(i == 0 && ids[0] == lastItem.attributes.playParams.id)) {
+ try {
+ app.mk.playLater({ songs: [id] })
+ } catch (err) {
+ }
+ }
+ i++;
+ }
+ }
+ }
+ }
+
+ })
+
+ })
+ }, 1500)
+
+ }
+
+ } catch (e) {
+ console.log(e)
+ }
+ break;
+ case "history":
+ let history = await app.mk.api.v3.music(`/v1/me/recent/played/tracks`, { l: app.mklang })
+ if (history.data.data.length > 0) {
+ let lastItem = history.data.data[0]
+ let kind = lastItem.attributes.playParams.kind;
+ let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
+ app.mk.setQueue({
+ [truekind]: [lastItem.attributes.playParams.id],
+ parameters: { l: app.mklang }
+ })
+ app.mk.mute()
+ setTimeout(() => {
+ app.mk.play().then(() => {
+ app.mk.pause().then(() => {
+ app.mk.unmute()
+ })
+ })
+ }, 1500)
+ }
+
+ break;
+ case "disabled":
+
+ break;
+ }
+
+ MusicKit.getInstance().videoContainerElement = document.getElementById("apple-music-video-player")
+
+ ipcRenderer.on('theme-update', (event, arg) => {
+ less.refresh(true, true, true)
+ self.setTheme(self.cfg.visual.theme, true)
+ })
+
+ ipcRenderer.on('SoundCheckTag', (event, tag) => {
+ // let replaygain = self.parseSCTagToRG(tag)
+ let soundcheck = tag.split(" ")
+ let numbers = []
+ for (let item of soundcheck) {
+ numbers.push(parseInt(item, 16))
+
+ }
+ numbers.shift()
+ let peak = Math.max(numbers[6], numbers[7]) / 32768.0
+ let gain = Math.pow(10, ((-7.63 - (Math.log10(peak) * 20)) / 20))// EBU R 128 Compliant
+ console.debug(`[Cider][MaikiwiSoundCheck] Peak Gain: ${Math.log10(peak) * 20} | Adjusting '${Math.log10(gain) * 20}' dB`)
+ try {
+ //CiderAudio.audioNodes.gainNode.gain.value = (Math.min(Math.pow(10, (replaygain.gain / 20)), (1 / replaygain.peak)))
+ CiderAudio.audioNodes.gainNode.gain.value = gain
+ } catch (e) {
+ }
+ })
+
+ ipcRenderer.on('play', function (_event, mode, id) {
+ if (mode !== 'url') {
+ self.mk.setQueue({ [mode]: id, parameters: { l: self.mklang } }).then(() => {
+ app.mk.play()
+ })
+
+ } else {
+ app.openAppleMusicURL(id)
+ }
+ });
+
+ this.mk.addEventListener(MusicKit.Events.playbackStateDidChange, () => {
+ ipcRenderer.send('wsapi-updatePlaybackState', wsapi.getAttributes());
+ })
+
+ this.mk.addEventListener(MusicKit.Events.playbackTimeDidChange, (a) => {
+ self.lyriccurrenttime = self.mk.currentPlaybackTime
+ this.currentSongInfo = a
+ self.playerLCD.playbackDuration = (self.mk.currentPlaybackTime)
+ // wsapi
+ ipcRenderer.send('wsapi-updatePlaybackState', wsapi.getAttributes());
+ })
+
+ this.mk.addEventListener(MusicKit.Events.nowPlayingItemDidChange, (a) => {
+ if (self.$refs.queue) {
+ self.$refs.queue.updateQueue();
+ }
+ this.currentSongInfo = a
+
+
+ if (app.cfg.audio.normalization) {
+ // get unencrypted audio previews to get SoundCheck's normalization tag
+ try {
+ let previewURL = null
+ try {
+ previewURL = app.mk.nowPlayingItem.previewURL
+ } catch (e) {
+ }
+ if (previewURL == null && ((app.mk.nowPlayingItem?._songId ?? (app.mk.nowPlayingItem["songId"] ?? app.mk.nowPlayingItem.relationships.catalog.data[0].id)) != -1)) {
+ app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/songs/${app.mk.nowPlayingItem?._songId ?? (app.mk.nowPlayingItem["songId"] ?? app.mk.nowPlayingItem.relationships.catalog.data[0].id)}`).then((response) => {
+ previewURL = response.data.data[0].attributes.previews[0].url
+ if (previewURL)
+ ipcRenderer.send('getPreviewURL', previewURL)
+ })
+ } else {
+ if (previewURL)
+ ipcRenderer.send('getPreviewURL', previewURL)
+ }
+
+ } catch (e) {
+ }
+ }
+
+
+ try {
+ a = a.item.attributes;
+ } catch (_) {
+ }
+ let type = (self.mk.nowPlayingItem != null) ? self.mk.nowPlayingItem["type"] ?? '' : '';
+
+ if (type.includes("musicVideo") || type.includes("uploadedVideo") || type.includes("music-movie")) {
+ document.getElementById("apple-music-video-container").style.display = "block";
+ // app.chrome.topChromeVisible = false
+ } else {
+ document.getElementById("apple-music-video-container").style.display = "none";
+ // app.chrome.topChromeVisible = true
+ }
+ self.chrome.artworkReady = false
+ self.lyrics = []
+ self.richlyrics = []
+ app.getCurrentArtURL();
+ // app.getNowPlayingArtwork(42);
+ app.getNowPlayingArtworkBG(32);
+ app.loadLyrics();
+
+ // Playback Notifications
+ if (this.cfg.general.playbackNotifications && !document.hasFocus() && a.artistName && a.artwork && a.name) {
+ if (this.notification) {
+ this.notification.close()
+ }
+ this.notification = new Notification(a.name, {
+ body: a.artistName,
+ icon: a.artwork.url.replace('/{w}x{h}bb', '/512x512bb').replace('/2000x2000bb', '/35x35bb'),
+ silent: true,
+ });
+ }
+
+ })
+
+
+ this.mk.addEventListener(MusicKit.Events.playbackVolumeDidChange, (_a) => {
+ this.cfg.audio.volume = this.mk.volume
+ })
+
+ this.refreshPlaylists(this.isDev)
+ document.body.removeAttribute("loading")
+ if (window.location.hash != "") {
+ this.appRoute(window.location.hash)
+ } else {
+ this.page = "home"
+ }
+
+ this.mediaKeyFixes()
+
+ setTimeout(() => {
+ this.getSocialBadges()
+ this.getBrowsePage();
+ this.$forceUpdate()
+ }, 500)
+ ipcRenderer.invoke("renderer-ready", true)
+ document.querySelector("#LOADER").remove()
+ if (this.cfg.general.themeUpdateNotification) {
+ this.checkForThemeUpdates()
+ }
+ },
+ async checkForThemeUpdates() {
+ let self = this
+ const themes = ipcRenderer.sendSync("get-themes")
+ await asyncForEach(themes, async (theme) => {
+ if (theme.commit != "") {
+ await fetch(`https://api.github.com/repos/${theme.github_repo}/commits`)
+ .then(res => res.json())
+ .then(res => {
+ if (res[0].sha != theme.commit) {
+ const notify = notyf.open({ className: "notyf-info", type: "info", message: `[Themes] ${theme.name} has an update available.` })
+ notify.on("click", () => {
+ app.appRoute("themes-github")
+ notyf.dismiss(notify)
+ })
+ }
+ })
+ }
+ })
+ },
+ async setTheme(theme = "", onlyPrefs = false) {
+ console.log(theme)
+ if (this.cfg.visual.theme == "") {
+ this.cfg.visual.theme = "default.less"
+ }
+ if (theme == "") {
+ theme = this.cfg.visual.theme
+ } else {
+ this.cfg.visual.theme = ""
+ this.cfg.visual.theme = theme
+ }
+ const info = {}
+ try {
+ const infoResponse = await fetch("themes/" + app.cfg.visual.theme.replace("index.less", "theme.json"))
+ this.chrome.appliedTheme.info = await infoResponse.json()
+ } catch (e) {
+ e = null
+ console.warn("failed to get theme.json")
+ this.chrome.appliedTheme.info = {}
+ }
+
+
+ if (!onlyPrefs) {
+ document.querySelector("#userTheme").href = `themes/${this.cfg.visual.theme}`
+ document.querySelectorAll(`[id*='less']`).forEach(el => {
+ el.remove()
+ });
+ less.refresh()
+ }
+ },
+ getThemeDirective(directive = "") {
+ let directives = {}
+ if (typeof this.chrome.appliedTheme.info.directives == "object") {
+ directives = this.chrome.appliedTheme.info.directives
+ }
+ if (directives[directive]) {
+ return this.chrome.appliedTheme.info.directives[directive].value
+ } else if (this.cfg.visual.directives[directive]) {
+ return this.cfg.visual.directives.windowLayout
+ } else {
+ return ""
+ }
+ },
+ unauthorize() {
+ bootbox.confirm(app.getLz('term.confirmLogout'), function (result) {
+ if (result) {
+ app.mk.unauthorize()
+ document.location.reload()
+ }
+ });
+ },
+ getAppClasses() {
+ let classes = {}
+ if (this.cfg.advanced.experiments.includes('compactui')) {
+ classes.compact = true
+ }
+ if (this.cfg.visual.window_background_style == "none") {
+ classes.simplebg = true
+ }
+
+ if (this.getThemeDirective('windowLayout') == 'twopanel') {
+ classes.twopanel = true
+ }
+ return classes
+ },
+ invokeDrawer(panel) {
+ if (this.drawer.panel == panel && this.drawer.open) {
+ if (panel == "lyrics") {
+ this.lyricon = false
+ }
+ this.drawer.panel = ""
+ this.drawer.open = false
+ } else {
+ if (panel == "lyrics") {
+ this.lyricon = true
+ } else {
+ this.lyricon = false
+ }
+ this.drawer.open = true
+ this.drawer.panel = panel
+ }
+ },
+ select_removeMediaItem(id) {
+ this.selectedMediaItems.filter(item => item.guid == id).forEach(item => {
+ this.selectedMediaItems.splice(this.selectedMediaItems.indexOf(item), 1)
+ })
+ },
+ select_hasMediaItem(id) {
+ let found = this.selectedMediaItems.find(item => item.guid == id)
+ if (found) {
+ return true
+ } else {
+ return false
+ }
+ },
+ select_selectMediaItem(id, kind, index, guid, library) {
+ if (!this.select_hasMediaItem(guid)) {
+ this.selectedMediaItems.push({
+ id: id,
+ kind: kind,
+ index: index,
+ guid: guid,
+ isLibrary: library
+ })
+ }
+ },
+ getPlaylistFolderChildren(id) {
+ return this.playlists.listing.filter(playlist => {
+ if (playlist.parent == id) {
+ return playlist
+ }
+ })
+ },
+ async refreshPlaylists(localOnly = false, trackMap = true) {
+ let self = this
+ let newListing = []
+ let trackMapping = {}
+ const cachedPlaylist = await CiderCache.getCache("library-playlists")
+ const cachedTrackMapping = await CiderCache.getCache("library-playlists-tracks")
+
+ if (cachedPlaylist) {
+ console.log("using cached playlists")
+ this.playlists.listing = cachedPlaylist
+ self.sortPlaylists()
+ } else {
+ console.log("playlist has no cache")
+ }
+
+ if(cachedTrackMapping) {
+ console.log("using cached track mapping")
+ this.playlists.trackMapping = cachedTrackMapping
+ }
+ if (localOnly) {
+ return
+ }
+
+ this.library.backgroundNotification.message = "Building playlist cache..."
+ this.library.backgroundNotification.show = true
+
+ async function deepScan(parent = "p.playlistsroot") {
+ console.log(`scanning ${parent}`)
+ const playlistData = await app.mk.api.v3.music(`/v1/me/library/playlist-folders/${parent}/children/`)
+ await asyncForEach(playlistData.data.data, async (playlist) => {
+ playlist.parent = parent
+ playlist.children = []
+ playlist.tracks = []
+ try {
+ if (trackMap) {
+ let tracks = await app.mk.api.v3.music(playlist.href + "/tracks").catch(e => {
+ // no tracks
+ e = null
+ })
+ tracks.data.data.forEach(track => {
+ if (!trackMapping[track.id]) {
+ trackMapping[track.id] = []
+ }
+ trackMapping[track.id].push(playlist.id)
+
+ if (typeof track.attributes.playParams.catalogId == "string") {
+ if (!trackMapping[track.attributes.playParams.catalogId]) {
+ trackMapping[track.attributes.playParams.catalogId] = []
+ }
+ trackMapping[track.attributes.playParams.catalogId].push(playlist.id)
+ }
+ })
+ }
+ } catch (e) { }
+ if (playlist.type == "library-playlist-folders") {
+ try {
+ await deepScan(playlist.id).catch(e => { })
+ } catch (e) {
+
+ }
+ }
+ newListing.push(playlist)
+ })
+ }
+
+ await deepScan()
+
+ this.library.backgroundNotification.show = false
+ this.playlists.listing = newListing
+ self.sortPlaylists()
+ if (trackMap) {
+ CiderCache.putCache("library-playlists-tracks", trackMapping)
+ this.playlists.trackMapping = trackMapping
+ }
+ CiderCache.putCache("library-playlists", newListing)
+ },
+ sortPlaylists() {
+ this.playlists.listing.sort((a, b) => {
+ if (a.type === "library-playlist-folders" && b.type !== "library-playlist-folders") {
+ return -1
+ } else if (a.type !== "library-playlist-folders" && b.type === "library-playlist-folders") {
+ return 1
+ } else {
+ return 0
+ }
+ })
+ },
+ playlistHeaderContextMenu(event) {
+ let menu = {
+ items: [{
+ name: app.getLz('term.createNewPlaylist'),
+ action: () => {
+ this.newPlaylist()
+ }
+ },
+ {
+ name: app.getLz('term.createNewPlaylistFolder'),
+ action: () => {
+ this.newPlaylistFolder()
+ }
+ }
+ ]
+ }
+ this.showMenuPanel(menu, event)
+ },
+ async editPlaylistFolder(id, name = app.getLz('term.newPlaylist')) {
+ let self = this
+ this.mk.api.v3.music(
+ `/v1/me/library/playlist-folders/${id}`, {}, {
+ fetchOptions: {
+ method: "PATCH",
+ body: JSON.stringify({
+ attributes: { name: name }
+ })
+ }
+ }
+ ).then(res => {
+ self.refreshPlaylists()
+ })
+ },
+ async editPlaylist(id, name = app.getLz('term.newPlaylist')) {
+ let self = this
+ this.mk.api.v3.music(
+ `/v1/me/library/playlists/${id}`, {}, {
+ fetchOptions: {
+ method: "PATCH",
+ body: JSON.stringify({
+ attributes: { name: name }
+ })
+ }
+ }
+ ).then(res => {
+ self.refreshPlaylists()
+ })
+ },
+ copyToClipboard(str) {
+ if (navigator.userAgent.includes('Darwin') || navigator.appVersion.indexOf("Mac") != -1) {
+ this.darwinShare(str)
+ } else {
+ notyf.success(app.getLz('term.share.success'))
+ navigator.clipboard.writeText(str).then(r => console.log("Copied to clipboard."))
+ }
+ },
+ newPlaylist(name = app.getLz('term.newPlaylist'), tracks = []) {
+ let self = this
+ let request = {
+ name: name
+ }
+ if (tracks.length > 0) {
+ request.tracks = tracks
+ }
+ app.mk.api.v3.music(`/v1/me/library/playlists`, {}, {
+ fetchOptions: {
+ method: "POST",
+ body: JSON.stringify({
+ "attributes": { "name": name },
+ "relationships": {
+ "tracks": { "data": tracks },
+ }
+ })
+ }
+ }).then(res => {
+ res = res.data.data[0]
+ console.log(res)
+ self.appRoute(`playlist_` + res.id);
+ self.showingPlaylist = [];
+ self.getPlaylistFromID(app.page.substring(9), true)
+ self.playlists.listing.push({
+ id: res.id,
+ attributes: {
+ name: name
+ },
+ parent: "p.playlistsroot"
+ })
+ self.sortPlaylists()
+ setTimeout(() => {
+ app.refreshPlaylists()
+ }, 8000)
+ })
+ },
+ deletePlaylist(id) {
+ let self = this
+ if (confirm(app.getLz('term.deletePlaylist'))) {
+ app.mk.api.v3.music(`/v1/me/library/playlists/${id}`, {}, {
+ fetchOptions: {
+ method: "DELETE"
+ }
+ }).then(res => {
+ // remove this playlist from playlists.listing if it exists
+ let found = self.playlists.listing.find(item => item.id == id)
+ if (found) {
+ self.playlists.listing.splice(self.playlists.listing.indexOf(found), 1)
+ }
+ })
+ }
+ },
+ async showCollection(response, title, type, requestBody = {}) {
+ let self = this
+ console.log(response)
+ this.collectionList.requestBody = {}
+ this.collectionList.response = response
+ this.collectionList.title = title
+ this.collectionList.type = type
+ this.collectionList.requestBody = requestBody
+ app.appRoute("collection-list")
+ },
+ async showArtistView(artist, title, view) {
+ let response = (await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists/${artist}/view/${view}?l=${this.mklang}`, {}, { includeResponseMeta: !0 })).data
+ console.log(response)
+ await this.showCollection(response, title, "artists")
+ },
+ async showRecordLabelView(label, title, view) {
+ let response = (await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/record-labels/${label}/view/${view}?l=${this.mklang}`)).data
+ await this.showCollection(response, title, "record-labels")
+ },
+ async showSearchView(term, group, title) {
+
+ let requestBody = {
+ platform: "web",
+ groups: group,
+ types: "activities,albums,apple-curators,artists,curators,editorial-items,music-movies,music-videos,playlists,songs,stations,tv-episodes,uploaded-videos,record-labels",
+ limit: 25,
+ relate: {
+ editorialItems: ["contents"]
+ },
+ include: {
+ albums: ["artists"],
+ songs: ["artists"],
+ "music-videos": ["artists"]
+ },
+ extend: "artistUrl",
+ fields: {
+ artists: "url,name,artwork,hero",
+ albums: "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url"
+ },
+ with: "serverBubbles,lyricHighlights",
+ art: {
+ "url": "cf"
+ },
+ omit: {
+ resource: ["autos"]
+ },
+ l: this.mklang
+ }
+ let response = await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/search?term=${term}`, requestBody, {
+ includeResponseMeta: !0
+ })
+
+ console.log('searchres', response)
+ let responseFormat = {
+ data: response.data.results[group].data,
+ next: response.data.results[group].next,
+ groups: group
+ }
+ await this.showCollection(responseFormat, title, "search", requestBody)
+ },
+ async getPlaylistContinuous(response, transient = false) {
+ response = response.data.data[0]
+ let self = this
+ let playlistId = response.id
+ this.playlists.loadingState = (!transient) ? 0 : 1
+ this.showingPlaylist = response
+ if (!response.relationships?.tracks?.next) {
+ this.playlists.loadingState = 1
+ return
+ }
+
+ function getPlaylistTracks(next) {
+ app.apiCall(app.musicBaseUrl + next, res => {
+ if (self.showingPlaylist.id != playlistId) {
+ return
+ }
+ self.showingPlaylist.relationships.tracks.data = self.showingPlaylist.relationships.tracks.data.concat(res.data)
+ if (res.next) {
+ getPlaylistTracks(res.next)
+ } else {
+ self.playlists.loadingState = 1
+ }
+ })
+ }
+
+ getPlaylistTracks(response.relationships.tracks.next)
+
+ },
+ async getPlaylistFromID(id, transient = false) {
+ let self = this
+ const params = {
+ include: "tracks",
+ platform: "web",
+ "include[library-playlists]": "catalog,tracks",
+ "fields[playlists]": "curatorName,playlistType,name,artwork,url,playParams",
+ "include[library-songs]": "catalog,artists,albums,playParams,name,artwork,url",
+ "fields[catalog]": "artistUrl,albumUrl,url",
+ "fields[songs]": "artistUrl,albumUrl,playParams,name,artwork,url,artistName,albumName,durationInMillis",
+ l: this.mklang
+ }
+ if (!transient) {
+ this.playlists.loadingState = 0;
+ }
+ app.mk.api.v3.music(`/v1/me/library/playlists/${id}`, params).then(res => {
+ self.getPlaylistContinuous(res, transient)
+ }).catch((e) => {
+ console.log(e);
+ try {
+ app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/playlists/${id}`, params).then(res => {
+ self.getPlaylistContinuous(res, transient)
+ })
+ } catch (err) {
+ console.log(err)
+ }
+ })
+
+ },
+ async getArtistFromID(id) {
+ this.page = ""
+ const artistData = await this.mkapi("artists", false, id, {
+ "views": "featured-release,full-albums,appears-on-albums,featured-albums,featured-on-albums,singles,compilation-albums,live-albums,latest-release,top-music-videos,similar-artists,top-songs,playlists,more-to-hear,more-to-see",
+ "extend": "artistBio,bornOrFormed,editorialArtwork,editorialVideo,isGroup,origin,hero",
+ "extend[playlists]": "trackCount",
+ "include[songs]": "albums",
+ "fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url,trackCount",
+ "limit[artists:top-songs]": 20,
+ "art[url]": "f",
+ l: this.mklang
+ }, { includeResponseMeta: !0 })
+ console.log(artistData.data.data[0])
+ this.artistPage.data = artistData.data.data[0]
+ this.page = "artist-page"
+ },
+ progressBarStyle() {
+ let val = this.playerLCD.playbackDuration
+ if (this.playerLCD.desiredDuration > 0) {
+ val = this.playerLCD.desiredDuration
+ }
+ let min = 0
+ let max = this.mk.currentPlaybackDuration
+ let value = (val - min) / (max - min) * 100
+ return {
+ 'background': ('linear-gradient(to right, var(--songProgressColor) 0%, var(--songProgressColor) ' + value + '%, var(--songProgressBackground) ' + value + '%, var(--songProgressBackground) 100%)')
+ }
+ },
+ async getRecursive(response) {
+ // if response has a .next() property run it and keep running until .next is null or undefined
+ // and then return the response concatenated with the results of the next() call
+ function executeRequest() {
+ if (response.next) {
+ return response.next().then(executeRequest)
+ } else {
+ return response
+ }
+ }
+
+ return executeRequest()
+ },
+ async getRecursive2(response, sendTo) {
+ let returnData = {
+ "data": [],
+ "meta": {}
+ }
+ if (response.next) {
+ console.log("has next")
+ returnData.data.concat(response.data)
+ returnData.meta = response.meta
+ return await this.getRecursive(await response.next())
+ } else {
+ console.log("no next")
+ returnData.data.concat(response.data)
+ return returnData
+ }
+ },
+ async getSearchHints() {
+ if (this.search.term == "") {
+ this.search.hints = []
+ return
+ }
+ let hints = await (await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/search/hints?term=${this.search.term}`)).data.results
+ this.search.hints = hints ? hints.terms : []
+ },
+ getSongProgress() {
+ if (this.playerLCD.userInteraction) {
+ return this.playerLCD.desiredDuration
+ } else {
+ return this.playerLCD.playbackDuration
+ }
+ },
+ /**
+ * Converts seconds to dd:hh:mm:ss
+ * @param time (in seconds)
+ * @param format (short, long)
+ * @returns {string}
+ */
+ convertTime(time = 0, format = 'short') {
+
+ if(isNaN(time)) {
+ time = 0
+ }
+ if (typeof time !== "number") {
+ time = parseInt(time)
+ }
+
+ const timeGates = {
+ 600: 15, // 10 Minutes
+ 3600: 14, // Hour
+ 36000: 12, // 10 Hours
+ }
+
+ const datetime = new Date(time * 1000)
+
+ let returnTime = datetime.toISOString().substring(11, 19);
+ for (let key in timeGates) {
+ if (time < key) {
+ returnTime = datetime.toISOString().substring(timeGates[key], 19)
+ break
+ }
+ }
+
+ // Add the days on the front
+ let day;
+ if (time >= 86400) {
+ day = datetime.toISOString().substring(8, 10)
+ day = parseInt(day) - 1
+ returnTime = day + ":" + returnTime
+ }
+
+ if (format === 'long') {
+ const longFormat = []
+
+ // Seconds
+ if (datetime.getSeconds() !== 0) {
+ longFormat.push(`${datetime.getSeconds()} ${app.getLz('term.time.seconds')}`)
+ }
+
+ // Minutes
+ if (time >= 60) {
+ longFormat.push(`${datetime.getMinutes()} ${app.getLz('term.time.minute', options = { count: datetime.getMinutes() })}`)
+ }
+
+ // Hours
+ if (time >= 3600) {
+ longFormat.push(`${datetime.getHours()} ${app.getLz('term.time.hour', options = { count: datetime.getHours() })}`)
+ }
+
+ // Days
+ if (time >= 86400) {
+ longFormat.push(`${day} ${app.getLz('term.time.day', options = { count: day })}`)
+ }
+ returnTime = longFormat.reverse().join(', ')
+ }
+
+ return returnTime
+ },
+ hashCode(str) {
+ let hash = 0,
+ i, chr;
+ if (str.length === 0) return hash;
+ for (i = 0; i < str.length; i++) {
+ chr = str.charCodeAt(i);
+ hash = ((hash << 5) - hash) + chr;
+ hash |= 0; // Convert to 32bit integer
+ }
+ return hash;
+ },
+ appRoute(route) {
+ if (route == "" || route == "#" || route == "/") {
+ return;
+ }
+ route = route.replace(/#/g, "")
+ // if the route contains does not include a / then route to the page directly
+ if (route.indexOf("/") == -1) {
+ this.page = route
+ window.location.hash = this.page
+ // if (this.page == "settings") {
+ // this.version
+ // }
+ return
+ }
+ let hash = route.split("/")
+ let page = hash[0]
+ let id = hash[1]
+ let isLibrary = hash[2] ?? false
+ this.routeView({
+ kind: page,
+ id: id,
+ attributes: {
+ playParams: { kind: page, id: id, isLibrary: isLibrary }
+ }
+ })
+ },
+ routeView(item) {
+ let kind = (item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')) : (item.type ?? ''));
+ let id = (item.attributes.playParams ? (item.attributes.playParams.id ?? (item.id ?? '')) : (item.id ?? ''));
+ ;
+ let isLibrary = item.attributes.playParams ? (item.attributes.playParams.isLibrary ?? false) : false;
+ if (kind.includes("playlist") || kind.includes("album")) {
+ app.showingPlaylist = [];
+ }
+ if (kind.toString().includes("apple-curator")) {
+ kind = "appleCurator"
+ app.getTypeFromID("appleCurator", (id), false, {
+ platform: "web",
+ include: "grouping,playlists",
+ extend: "editorialArtwork",
+ "art[url]": "f"
+ });
+ window.location.hash = `${kind}/${id}`
+ document.querySelector("#app-content").scrollTop = 0
+ } else if (kind.toString().includes("artist")) {
+ app.getArtistInfo(id, isLibrary)
+ window.location.hash = `${kind}/${id}${isLibrary ? "/" + isLibrary : ''}`
+ document.querySelector("#app-content").scrollTop = 0
+
+ } else if (kind.toString().includes("record-label") || kind.toString().includes("curator")) {
+ if (kind.toString().includes("record-label")) {
+ kind = "recordLabel"
+ } else {
+ kind = "curator"
+ }
+ app.page = (kind) + "_" + (id);
+ app.getTypeFromID((kind), (id), (isLibrary), {
+ extend: "editorialVideo",
+ include: 'grouping,playlists',
+ views: 'top-releases,latest-releases,top-artists'
+ });
+ window.location.hash = `${kind}/${id}`
+ document.querySelector("#app-content").scrollTop = 0
+ } else if (!kind.toString().includes("radioStation") && !kind.toString().includes("song") && !kind.toString().includes("musicVideo") && !kind.toString().includes("uploadedVideo") && !kind.toString().includes("music-movie")) {
+ let params = {
+ extend: "offers,editorialVideo",
+ "views": "appears-on,more-by-artist,related-videos,other-versions,you-might-also-like,video-extras,audio-extras",
+ }
+ if (kind.includes("playlist")) {
+ params["include"] = "tracks";
+ }
+ if (kind.includes("album")) {
+ params["include[albums]"] = "artists"
+ params["fields[artists]"] = "name,url"
+ params["fields[albums]"] = "artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url,copyright"
+ }
+
+ if (this.cfg.advanced.experiments.includes('inline-playlists')) {
+ let showModal = kind.toString().includes("album") || kind.toString().includes("playlist")
+ if (app.page.includes("playlist") || app.page.includes("album")) {
+ showModal = false
+ }
+ if (showModal) {
+ app.modals.showPlaylist = true
+ app.chrome.contentAreaScrolling = false
+ } else {
+ app.page = (kind) + "_" + (id);
+ window.location.hash = `${kind}/${id}${isLibrary ? "/" + isLibrary : ''}`
+ }
+ } else {
+ app.page = (kind) + "_" + (id);
+ window.location.hash = `${kind}/${id}${isLibrary ? "/" + isLibrary : ''}`
+ }
+
+
+ app.getTypeFromID((kind), (id), (isLibrary), params);
+ // document.querySelector("#app-content").scrollTop = 0
+ } else {
+ app.playMediaItemById((id), (kind), (isLibrary), item.attributes.url ?? '')
+ }
+ },
+ prevButton() {
+ if (!app.prevButtonBackIndicator && app.mk.nowPlayingItem && app.mk.currentPlaybackTime > 2) {
+ app.prevButtonBackIndicator = true;
+ try {
+ clearTimeout(app.pauseButtonTimer)
+ } catch (e) {
+ }
+ app.mk.seekToTime(0);
+ app.pauseButtonTimer = setTimeout(() => {
+ app.prevButtonBackIndicator = false
+ }, 3000);
+ } else {
+ try {
+ clearTimeout(app.pauseButtonTimer)
+ } catch (e) {
+ }
+ app.prevButtonBackIndicator = false;
+ app.skipToPreviousItem()
+ }
+ },
+ async getNowPlayingItemDetailed(target) {
+ try {
+ let u = await app.mkapi(app.mk.nowPlayingItem.playParams.kind,
+ (app.mk.nowPlayingItem.songId == -1),
+ (app.mk.nowPlayingItem.songId != -1) ? app.mk.nowPlayingItem.songId : app.mk.nowPlayingItem["id"],
+ { "include[songs]": "albums,artists", l: app.mklang });
+ app.searchAndNavigate(u.data.data[0], target)
+ } catch (e) {
+ app.searchAndNavigate(app.mk.nowPlayingItem, target)
+ }
+ },
+ async searchAndNavigate(item, target) {
+ let self = this
+ app.tmpVar = item;
+ switch (target) {
+ case "artist":
+ let artistId = '';
+ try {
+ if (item.relationships.artists && item.relationships.artists.data.length > 0 && !item.relationships.artists.data[0].type.includes("library")) {
+ if (item.relationships.artists.data[0].type === "artist" || item.relationships.artists.data[0].type === "artists") {
+ artistId = item.relationships.artists.data[0].id
+ }
+ }
+ if (artistId == '') {
+ const url = (item.relationships.catalog.data[0].attributes.artistUrl);
+ artistId = (url).substring(url.lastIndexOf('/') + 1)
+ if (artistId.includes('viewCollaboration')) {
+ artistId = artistId.substring(artistId.lastIndexOf('ids=') + 4, artistId.lastIndexOf('-'))
+ }
+ }
+ } catch (_) {
+ }
+
+ if (artistId == "") {
+ let artistQuery = (await app.mk.api.v3.music(`v1/catalog/${app.mk.storefrontId}/search?term=${item.attributes.artistName}`, {
+ limit: 1,
+ types: 'artists'
+ })).data.results;
+ try {
+ if (artistQuery.artists.data.length > 0) {
+ artistId = artistQuery.artists.data[0].id;
+ console.log(artistId)
+ }
+ } catch (e) {
+ }
+ }
+ console.log(artistId);
+ if (artistId != "")
+ self.appRoute(`artist/${artistId}`)
+ break;
+ case "album":
+ let albumId = '';
+ try {
+ if ((item.type ?? item.playParams?.kind ?? "") == "albums") {
+ albumId = item.id ?? ""
+ } else if (item.relationships.albums && item.relationships.albums.data.length > 0 && !item.relationships.albums.data[0].type.includes("library")) {
+ if (item.relationships.albums.data[0].type === "album" || item.relationships.albums.data[0].type === "albums") {
+ albumId = item.relationships.albums.data[0].id
+ }
+ }
+ if (albumId == '') {
+ const url = (item.relationships.catalog.data[0].attributes.url);
+ albumId = (url).substring(url.lastIndexOf('/') + 1)
+ if (albumId.includes("?i=")) {
+ albumId = albumId.substring(0, albumId.indexOf("?i="))
+ }
+ }
+ } catch (_) {
+ }
+
+ if (albumId == "") {
+ try {
+ let albumQuery = (await app.mk.api.v3.music(`v1/catalog/${app.mk.storefrontId}/search?term=${(item.attributes.albumName ?? item.attributes.name ?? "") + " " + (item.attributes.artistName ?? "")}`, {
+ limit: 1,
+ types: 'albums'
+ })).data.results;
+ if (albumQuery.albums.data.length > 0) {
+ albumId = albumQuery.albums.data[0].id;
+ console.log(albumId)
+ }
+ } catch (e) {
+ }
+ }
+ if (albumId != "") {
+ self.appRoute(`album/${albumId}`)
+ }
+ break;
+ case "recordLabel":
+ let labelId = '';
+ try {
+ labelId = item.relationships['record-labels'].data[0].id
+ } catch (_) {
+ }
+
+ if (labelId == "") {
+ try {
+ let labelQuery = (await app.mk.api.v3.music(`v1/catalog/${app.mk.storefrontId}/search?term=${item.attributes.recordLabel}`, {
+ limit: 1,
+ types: 'record-labels'
+ })).data.results;
+ if (labelQuery["record-labels"].data.length > 0) {
+ labelId = labelQuery["record-labels"].data[0].id;
+ console.log(labelId)
+ }
+ } catch (e) {
+ }
+ }
+ if (labelId != "") {
+ app.showingPlaylist = []
+ await app.getTypeFromID("recordLabel", labelId, false, { views: 'top-releases,latest-releases,top-artists' });
+ app.page = "recordLabel_" + labelId;
+ }
+
+ break;
+ }
+ },
+ exitMV() {
+ MusicKit.getInstance().stop()
+ document.getElementById("apple-music-video-container").style.display = "none";
+ },
+ getArtistInfo(id, isLibrary) {
+ this.getArtistFromID(id)
+ //this.getTypeFromID("artist",id,isLibrary,query)
+ },
+ followArtistById(id, follow) {
+ if (follow && !this.followingArtist(id)) {
+ this.cfg.home.followedArtists.push(id)
+ } else {
+ let index = this.cfg.home.followedArtists.indexOf(id)
+ if (index > -1) {
+ this.cfg.home.followedArtists.splice(index, 1)
+ }
+ }
+ },
+ followingArtist(id) {
+ console.log(`check for ${id}`)
+ return this.cfg.home.followedArtists.includes(id)
+ },
+ playMediaItem(item) {
+ let kind = (item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')) : (item.type ?? ''));
+ let id = (item.attributes.playParams ? (item.attributes.playParams.id ?? (item.id ?? '')) : (item.id ?? ''));
+ ;
+ let isLibrary = item.attributes.playParams ? (item.attributes.playParams.isLibrary ?? false) : false;
+ let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
+ // console.log(kind, id, isLibrary)
+ app.mk.stop().then(() => {
+ if (kind.includes("artist")) {
+ app.mk.setStationQueue({ artist: 'a-' + id }).then(() => {
+ app.mk.play()
+ })
+ }
+ // else if (kind.includes("playlist") && (id.startsWith("p.") || id.startsWith("pl."))){
+ // /* Randomize array in-place using Durstenfeld shuffle algorithm */
+ // function shuffleArray(array) {
+ // for (var i = array.length - 1; i > 0; i--) {
+ // var j = Math.floor(Math.random() * (i + 1));
+ // var temp = array[i];
+ // array[i] = array[j];
+ // array[j] = temp;
+ // }
+ // }
+ // app.mk.clearQueue().then(function () { {
+ // app.mk.setQueue({[truekind]: [item.attributes.playParams.id ?? item.id]}).then(function () {
+ // app.mk.play().then(function (){
+ // app.mk.clearQueue().then(function (){
+ // var playlistId = id
+ // const params = {
+ // include: "tracks",
+ // platform: "web",
+ // "include[library-playlists]": "catalog,tracks",
+ // "fields[playlists]": "curatorName,playlistType,name,artwork,url",
+ // "include[library-songs]": "catalog,artists,albums",
+ // "fields[catalog]": "artistUrl,albumUrl",
+ // "fields[songs]": "artistUrl,albumUrl"
+ // }
+ // var playlistId = ''
+
+ // try {
+ // function getPlaylist(id, params, isLibrary){
+ // if (isLibrary){
+ // return app.mk.api.library.playlist(id, params)
+ // } else { return app.mk.api.playlist(id, params)}
+ // }
+ // getPlaylist(id, params, isLibrary).then(res => {
+ // let query = res.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
+ // if (app.mk.shuffleMode == 1){shuffleArray(query); console.log('shf')}
+ // app.mk.queue.append(query)
+ // if (!res.relationships.tracks.next) {
+ // return
+ // } else {
+ // getPlaylistTracks(res.relationships.tracks.next)
+ // }
+
+ // function getPlaylistTracks(next) {
+ // app.apiCall(app.musicBaseUrl + next, res => {
+ // if (res.id != playlistId) {
+ // return
+ // }
+ // let query = res.data.map(item => new MusicKit.MediaItem(item))
+ // if (app.mk.shuffleMode == 1){shuffleArray(query); console.log('shf')}
+ // app.mk.queue.append(query)
+
+ // if (res.next) {
+ // getPlaylistTracks(res.next)
+ // }
+ // })
+ // }
+ // })
+ // } catch (e) {}
+
+
+ // })
+ // })
+ // })
+ // }
+ // })
+ // }
+ else {
+ app.playMediaItemById((id), (kind), (isLibrary), item.attributes.url ?? '')
+ }
+ })
+ },
+ async getTypeFromID(kind, id, isLibrary = false, params = {}, params2 = {}) {
+ let a;
+ if (kind == "album" | kind == "albums") {
+ params["include"] = "tracks,artists,record-labels,catalog";
+ }
+ params['l'] = this.mklang;
+ try {
+ a = await this.mkapi(kind.toString(), isLibrary, id.toString(), params, params2);
+ } catch (e) {
+ console.log(e);
+ try {
+ a = await this.mkapi(kind.toString(), !isLibrary, id.toString(), params, params2);
+ } catch (err) {
+ console.log(err);
+ a = []
+ } finally {
+ if (kind == "appleCurator") {
+ app.appleCurator = a.data.data[0]
+ } else {
+ this.getPlaylistContinuous(a, true)
+ }
+ }
+ } finally {
+ if (kind == "appleCurator") {
+ app.appleCurator = a.data.data[0]
+ } else {
+ this.getPlaylistContinuous(a, true)
+ }
+ }
+ ;
+ },
+ searchLibrarySongs() {
+ let self = this
+ let prefs = this.cfg.libraryPrefs.songs
+ let albumAdded = self.library?.albums?.listing?.map(function (i) {
+ return { [i.id]: i.attributes?.dateAdded }
+ })
+ let startTime = new Date().getTime()
+
+ function sortSongs() {
+ // sort this.library.songs.displayListing by song.attributes[self.library.songs.sorting] in descending or ascending order based on alphabetical order and numeric order
+ // check if song.attributes[self.library.songs.sorting] is a number and if so, sort by number if not, sort by alphabetical order ignoring case
+ self.library.songs.displayListing.sort((a, b) => {
+ let aa = a.attributes[prefs.sort]
+ let bb = b.attributes[prefs.sort]
+ if (prefs.sort == "genre") {
+ aa = a.attributes.genreNames[0]
+ bb = b.attributes.genreNames[0]
+ }
+ if (prefs.sort == "dateAdded") {
+ let albumida = a.relationships?.albums?.data[0]?.id ?? '1970-01-01T00:01:01Z'
+ let albumidb = b.relationships?.albums?.data[0]?.id ?? '1970-01-01T00:01:01Z'
+ aa = startTime - new Date(((albumAdded.find(i => i[albumida])) ?? [])[albumida] ?? '1970-01-01T00:01:01Z').getTime()
+ bb = startTime - new Date(((albumAdded.find(i => i[albumidb])) ?? [])[albumidb] ?? '1970-01-01T00:01:01Z').getTime()
+ }
+ if (aa == null) {
+ aa = ""
+ }
+ if (bb == null) {
+ bb = ""
+ }
+ if (prefs.sortOrder == "asc") {
+ if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) {
+ return aa - bb
+ } else {
+ return aa.toString().toLowerCase().localeCompare(bb.toString().toLowerCase())
+ }
+ } else if (prefs.sortOrder == "desc") {
+ if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) {
+ return bb - aa
+ } else {
+ return bb.toString().toLowerCase().localeCompare(aa.toString().toLowerCase())
+ }
+ }
+ })
+ }
+
+ if (this.library.songs.search == "") {
+ this.library.songs.displayListing = this.library.songs.listing
+ sortSongs()
+ } else {
+ this.library.songs.displayListing = this.library.songs.listing.filter(item => {
+ let itemName = item.attributes.name.toLowerCase()
+ let searchTerm = this.library.songs.search.toLowerCase()
+ let artistName = ""
+ let albumName = ""
+ if (item.attributes.artistName != null) {
+ artistName = item.attributes.artistName.toLowerCase()
+ }
+ if (item.attributes.albumName != null) {
+ albumName = item.attributes.albumName.toLowerCase()
+ }
+
+ // remove any non-alphanumeric characters and spaces from search term and item name
+ searchTerm = searchTerm.replace(/[^a-z0-9 ]/gi, "")
+ itemName = itemName.replace(/[^a-z0-9 ]/gi, "")
+ artistName = artistName.replace(/[^a-z0-9 ]/gi, "")
+ albumName = albumName.replace(/[^a-z0-9 ]/gi, "")
+
+ if (itemName.includes(searchTerm) || artistName.includes(searchTerm) || albumName.includes(searchTerm)) {
+ return item
+ }
+ })
+ sortSongs()
+ }
+ },
+ getAlbumSort() {
+ this.library.albums.sortOrder[1] = this.cfg.libraryPrefs.albums.sortOrder;
+ this.library.albums.sorting[1] = this.cfg.libraryPrefs.albums.sort;
+ },
+ // make a copy of searchLibrarySongs except use Albums instead of Songs
+ searchLibraryAlbums(index) {
+ let self = this
+
+ function sortAlbums() {
+ // sort this.library.albums.displayListing by album.attributes[self.library.albums.sorting[index]] in descending or ascending order based on alphabetical order and numeric order
+ // check if album.attributes[self.library.albums.sorting[index]] is a number and if so, sort by number if not, sort by alphabetical order ignoring case
+ self.library.albums.displayListing.sort((a, b) => {
+ let aa = a.attributes[self.library.albums.sorting[index]]
+ let bb = b.attributes[self.library.albums.sorting[index]]
+ if (self.library.albums.sorting[index] == "genre") {
+ aa = a.attributes.genreNames[0]
+ bb = b.attributes.genreNames[0]
+ }
+ if (aa == null) {
+ aa = ""
+ }
+ if (bb == null) {
+ bb = ""
+ }
+ if (self.library.albums.sortOrder[index] == "asc") {
+ if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) {
+ return aa - bb
+ } else {
+ return aa.toString().toLowerCase().localeCompare(bb.toString().toLowerCase())
+ }
+ } else if (self.library.albums.sortOrder[index] == "desc") {
+ if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) {
+ return bb - aa
+ } else {
+ return bb.toString().toLowerCase().localeCompare(aa.toString().toLowerCase())
+ }
+ }
+ })
+ }
+
+ if (this.library.albums.search == "") {
+ this.library.albums.displayListing = this.library.albums.listing
+ sortAlbums()
+ } else {
+ this.library.albums.displayListing = this.library.albums.listing.filter(item => {
+ let itemName = item.attributes.name.toLowerCase()
+ let searchTerm = this.library.albums.search.toLowerCase()
+ let artistName = ""
+ let albumName = ""
+ if (item.attributes.artistName != null) {
+ artistName = item.attributes.artistName.toLowerCase()
+ }
+ if (item.attributes.albumName != null) {
+ albumName = item.attributes.albumName.toLowerCase()
+ }
+
+ // remove any non-alphanumeric characters and spaces from search term and item name
+ searchTerm = searchTerm.replace(/[^a-z0-9 ]/gi, "")
+ itemName = itemName.replace(/[^a-z0-9 ]/gi, "")
+ artistName = artistName.replace(/[^a-z0-9 ]/gi, "")
+ albumName = albumName.replace(/[^a-z0-9 ]/gi, "")
+
+ if (itemName.includes(searchTerm) || artistName.includes(searchTerm) || albumName.includes(searchTerm)) {
+ return item
+ }
+ })
+ sortAlbums()
+ }
+ },
+ // make a copy of searchLibrarySongs except use Albums instead of Songs
+ searchLibraryArtists(index) {
+ let self = this
+
+ function sortArtists() {
+ // sort this.library.albums.displayListing by album.attributes[self.library.albums.sorting[index]] in descending or ascending order based on alphabetical order and numeric order
+ // check if album.attributes[self.library.albums.sorting[index]] is a number and if so, sort by number if not, sort by alphabetical order ignoring case
+ self.library.artists.displayListing.sort((a, b) => {
+ let aa = a.attributes[self.library.artists.sorting[index]]
+ let bb = b.attributes[self.library.artists.sorting[index]]
+ if (self.library.artists.sorting[index] == "genre") {
+ aa = a.attributes.genreNames[0]
+ bb = b.attributes.genreNames[0]
+ }
+ if (aa == null) {
+ aa = ""
+ }
+ if (bb == null) {
+ bb = ""
+ }
+ if (self.library.artists.sortOrder[index] == "asc") {
+ if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) {
+ return aa - bb
+ } else {
+ return aa.toString().toLowerCase().localeCompare(bb.toString().toLowerCase())
+ }
+ } else if (self.library.artists.sortOrder[index] == "desc") {
+ if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) {
+ return bb - aa
+ } else {
+ return bb.toString().toLowerCase().localeCompare(aa.toString().toLowerCase())
+ }
+ }
+ })
+ }
+
+ if (this.library.artists.search == "") {
+ this.library.artists.displayListing = this.library.artists.listing
+ sortArtists()
+ } else {
+ this.library.artists.displayListing = this.library.artists.listing.filter(item => {
+ let itemName = item.attributes.name.toLowerCase()
+ let searchTerm = this.library.artists.search.toLowerCase()
+ let artistName = ""
+ let albumName = ""
+ // if (item.attributes.artistName != null) {
+ // artistName = item.attributes.artistName.toLowerCase()
+ // }
+ // if (item.attributes.albumName != null) {
+ // albumName = item.attributes.albumName.toLowerCase()
+ // }
+
+ // remove any non-alphanumeric characters and spaces from search term and item name
+ searchTerm = searchTerm.replace(/[^a-z0-9 ]/gi, "")
+ itemName = itemName.replace(/[^a-z0-9 ]/gi, "")
+
+
+ if (itemName.includes(searchTerm) || artistName.includes(searchTerm) || albumName.includes(searchTerm)) {
+ return item
+ }
+ })
+ sortArtists()
+ }
+ },
+ getSidebarItemClass(page) {
+ if (this.page == page) {
+ return ["active"]
+ } else {
+ return []
+ }
+ },
+ async mkapi(method, library = false, term, params = {}, params2 = {}, attempts = 0) {
+ if (method.includes(`recordLabel`)) {
+ method = `record-labels`
+ }
+ if (method.includes(`appleCurator`)) {
+ method = `apple-curators`
+ }
+ if (attempts > 3) {
+ return
+ }
+ let truemethod = (!method.endsWith("s")) ? (method + "s") : method;
+ try {
+ if (library) {
+ return await this.mk.api.v3.music(`v1/me/library/${truemethod}/${term.toString()}`, params, params2)
+ } else {
+ return await this.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/${truemethod}/${term.toString()}`, params, params2)
+ }
+ } catch (e) {
+ console.log(e)
+ return await this.mkapi(method, library, term, params, params2, attempts + 1)
+ }
+ },
+ getLibraryGenres() {
+ let genres = []
+ genres = []
+ this.library.songs.listing.forEach((item) => {
+ item.attributes.genreNames.forEach((genre) => {
+ if (!genres.includes(genre)) {
+ genres.push(genre)
+ }
+ })
+ })
+ return genres
+ },
+ async getLibrarySongsFull(force = false) {
+ let self = this
+ let library = []
+ let cacheId = "library-songs"
+ let downloaded = null;
+ if ((this.library.songs.downloadState == 2) && !force) {
+ return
+ }
+ if (this.library.songs.downloadState == 1) {
+ return
+ }
+ let librarySongs = await CiderCache.getCache(cacheId)
+ if (librarySongs) {
+ this.library.songs.listing = librarySongs
+ this.searchLibrarySongs()
+ }
+ if (this.songstest) {
+ return
+ }
+ this.library.songs.downloadState = 1
+ this.library.backgroundNotification.show = true
+ this.library.backgroundNotification.message = app.getLz('notification.updatingLibrarySongs')
+
+ function downloadChunk() {
+ const params = {
+ "include[library-songs]": "catalog,artists,albums",
+ "fields[artists]": "name,url,id",
+ "fields[albums]": "name,url,id",
+ platform: "web",
+ "fields[catalog]": "artistUrl,albumUrl",
+ "fields[songs]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url",
+ limit: 100,
+ l: self.mklang
+ }
+ const safeparams = {
+ "platform": "web",
+ "limit": 80
+ }
+ self.library.songs.downloadState = 1
+ if (downloaded == null) {
+ app.mk.api.v3.music(`/v1/me/library/songs/`, params).then((response) => {
+ processChunk(response.data)
+ }).catch((error) => {
+ console.log('safe loading');
+ app.mk.api.v3.music(`/v1/me/library/songs/`, safeparams).then((response) => {
+ processChunk(response.data)
+ }).catch((error) => {
+ console.log('safe loading failed', error)
+ app.library.songs.downloadState = 2
+ app.library.backgroundNotification.show = false
+ })
+ })
+ } else {
+ if (downloaded.next != null) {
+ app.mk.api.v3.music(downloaded.next, params).then((response) => {
+ processChunk(response.data)
+ }).catch((error) => {
+ console.log('safe loading');
+ app.mk.api.v3.music(downloaded.next, safeparams).then((response) => {
+ processChunk(response.data)
+ }).catch((error) => {
+ console.log('safe loading failed', error)
+ app.library.songs.downloadState = 2
+ app.library.backgroundNotification.show = false
+ })
+ })
+ } else {
+ console.log("Download next", downloaded.next)
+ }
+ }
+ }
+
+ function processChunk(response) {
+ downloaded = response
+ library = library.concat(downloaded.data)
+ self.library.backgroundNotification.show = true
+ self.library.backgroundNotification.message = app.getLz('notification.updatingLibrarySongs')
+ self.library.backgroundNotification.total = downloaded.meta.total
+ self.library.backgroundNotification.progress = library.length
+
+ if (downloaded.meta.total == 0) {
+ self.library.songs.downloadState = 3
+ return
+ }
+ if (typeof downloaded.next == "undefined") {
+ console.log("downloaded.next is undefined")
+ self.library.songs.listing = library
+ self.library.songs.downloadState = 2
+ self.library.backgroundNotification.show = false
+ self.searchLibrarySongs()
+ CiderCache.putCache(cacheId, library)
+ }
+ if (downloaded.meta.total > library.length || typeof downloaded.meta.next != "undefined") {
+ console.log(`downloading next chunk - ${library.length} songs so far`)
+ downloadChunk()
+ } else {
+ self.library.songs.listing = library
+ self.library.songs.downloadState = 2
+ self.library.backgroundNotification.show = false
+ self.searchLibrarySongs()
+ CiderCache.putCache(cacheId, library)
+ // console.log(library)
+ }
+ }
+
+ downloadChunk()
+ },
+ // copy the getLibrarySongsFull function except change Songs to Albums
+ async getLibraryAlbumsFull(force = false, index) {
+ let self = this
+ let library = []
+ let cacheId = "library-albums"
+ let downloaded = null;
+ if ((this.library.albums.downloadState == 2 || this.library.albums.downloadState == 1) && !force) {
+ return
+ }
+ let libraryAlbums = await CiderCache.getCache(cacheId)
+ if (libraryAlbums) {
+ this.library.albums.listing = libraryAlbums
+ this.searchLibraryAlbums(index)
+ }
+ if (this.songstest) {
+ return
+ }
+ this.library.albums.downloadState = 1
+ this.library.backgroundNotification.show = true
+ this.library.backgroundNotification.message = app.getLz('notification.updatingLibraryAlbums')
+
+ function downloadChunk() {
+ self.library.albums.downloadState = 1
+ const params = {
+ "include[library-albums]": "catalog,artists,albums",
+ "fields[artists]": "name,url,id",
+ // "fields[albums]": "name,url,id",
+ platform: "web",
+ "fields[catalog]": "artistUrl,albumUrl",
+ "fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url",
+ limit: 100,
+ l: self.mklang
+ }
+ const safeparams = {
+ platform: "web",
+ limit: "60",
+ "include[library-albums]": "artists",
+ "include[library-artists]": "catalog",
+ "include[albums]": "artists",
+ "fields[artists]": "name,url",
+ "fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url",
+ "includeOnly": "catalog,artists"
+ }
+ if (downloaded == null) {
+ app.mk.api.v3.music(`/v1/me/library/albums/`, params).then((response) => {
+ processChunk(response.data)
+ }).catch((error) => {
+ console.log('safe loading');
+ app.mk.api.v3.music(`/v1/me/library/albums/`, safeparams).then((response) => {
+ processChunk(response.data)
+ }).catch((error) => {
+ console.log('safe loading failed', error)
+ app.library.albums.downloadState = 2
+ app.library.backgroundNotification.show = false
+ })
+ })
+ } else {
+ if (downloaded.next != null) {
+ app.mk.api.v3.music(downloaded.next, params).then((response) => {
+ processChunk(response.data)
+ }).catch((error) => {
+ console.log('safe loading');
+ app.mk.api.v3.music(downloaded.next, safeparams).then((response) => {
+ processChunk(response.data)
+ }).catch((error) => {
+ console.log('safe loading failed', error);
+ app.library.albums.downloadState = 2
+ app.library.backgroundNotification.show = false
+ })
+ })
+ } else {
+ console.log("Download next", downloaded.next)
+ }
+ }
+ }
+
+ function processChunk(response) {
+ downloaded = response
+ library = library.concat(downloaded.data)
+ self.library.backgroundNotification.show = true
+ self.library.backgroundNotification.message = app.getLz('notification.updatingLibraryAlbums')
+ self.library.backgroundNotification.total = downloaded.meta.total
+ self.library.backgroundNotification.progress = library.length
+ if (downloaded.meta.total == 0) {
+ self.library.albums.downloadState = 3
+ return
+ }
+ if (typeof downloaded.next == "undefined") {
+ console.log("downloaded.next is undefined")
+ self.library.albums.listing = library
+ self.library.albums.downloadState = 2
+ self.library.backgroundNotification.show = false
+ CiderCache.putCache(cacheId, library)
+ self.searchLibraryAlbums(index)
+ }
+ if (downloaded.meta.total > library.length || typeof downloaded.meta.next != "undefined") {
+ console.log(`downloading next chunk - ${library.length
+ } albums so far`)
+ downloadChunk()
+ } else {
+ self.library.albums.listing = library
+ self.library.albums.downloadState = 2
+ self.library.backgroundNotification.show = false
+ CiderCache.putCache(cacheId, library)
+ self.searchLibraryAlbums(index)
+ // console.log(library)
+ }
+ }
+
+ downloadChunk()
+ },
+ // copy the getLibrarySongsFull function except change Songs to Albums
+ async getLibraryArtistsFull(force = false, index) {
+ let self = this
+ let library = []
+ let cacheId = "library-artists"
+ let downloaded = null;
+ if ((this.library.artists.downloadState == 2 || this.library.artists.downloadState == 1) && !force) {
+ return
+ }
+ let libraryArtists = await CiderCache.getCache(cacheId)
+ if (libraryArtists) {
+ this.library.artists.listing = libraryArtists
+ this.searchLibraryArtists(index)
+ }
+ if (this.songstest) {
+ return
+ }
+ this.library.artists.downloadState = 1
+ this.library.backgroundNotification.show = true
+ this.library.backgroundNotification.message = app.getLz('notification.updatingLibraryArtists')
+
+ function downloadChunk() {
+ self.library.artists.downloadState = 1
+ const params = {
+ include: "catalog",
+ // "include[library-artists]": "catalog,artists,albums",
+ // "fields[artists]": "name,url,id",
+ // "fields[albums]": "name,url,id",
+ platform: "web",
+ // "fields[catalog]": "artistUrl,albumUrl",
+ // "fields[artists]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url",
+ limit: 100,
+ l: self.mklang
+ }
+ const safeparams = {
+ include: "catalog",
+ platform: "web",
+ limit: 50,
+ }
+ if (downloaded == null) {
+ app.mk.api.v3.music(`/v1/me/library/artists/`, params).then((response) => {
+ processChunk(response.data)
+ }).catch((error) => {
+ console.log('safe loading');
+ app.mk.api.v3.music(`/v1/me/library/artists/`, safeparams).then((response) => {
+ processChunk(response.data)
+ }).catch((error) => {
+ console.log('safe loading failed', error)
+ app.library.artists.downloadState = 2
+ app.library.backgroundNotification.show = false
+ })
+ })
+
+ } else {
+ if (downloaded.next != null) {
+ app.mk.api.v3.music(downloaded.next, params).then((response) => {
+ processChunk(response.data)
+ }).catch((error) => {
+ console.log('safe loading');
+ app.mk.api.v3.music(downloaded.next, safeparams).then((response) => {
+ processChunk(response.data)
+ }).catch((error) => {
+ console.log('safe loading failed', error)
+ app.library.artists.downloadState = 2
+ app.library.backgroundNotification.show = false
+ })
+ })
+ } else {
+ console.log("Download next", downloaded.next)
+ }
+
+ }
+ }
+
+ function processChunk(response) {
+ downloaded = response
+ library = library.concat(downloaded.data)
+ self.library.backgroundNotification.show = true
+ self.library.backgroundNotification.message = app.getLz('notification.updatingLibraryArtists')
+ self.library.backgroundNotification.total = downloaded.meta.total
+ self.library.backgroundNotification.progress = library.length
+ if (downloaded.meta.total == 0) {
+ self.library.albums.downloadState = 3
+ return
+ }
+ if (typeof downloaded.next == "undefined") {
+ console.log("downloaded.next is undefined")
+ self.library.artists.listing = library
+ self.library.artists.downloadState = 2
+ self.library.artists.show = false
+ CiderCache.putCache(cacheId, library)
+ self.searchLibraryArtists(index)
+ }
+ if (downloaded.meta.total > library.length || typeof downloaded.meta.next != "undefined") {
+ console.log(`downloading next chunk - ${library.length
+ } artists so far`)
+ downloadChunk()
+ } else {
+ self.library.artists.listing = library
+ self.library.artists.downloadState = 2
+ self.library.backgroundNotification.show = false
+ CiderCache.putCache(cacheId, library)
+ self.searchLibraryArtists(index)
+ // console.log(library)
+ }
+ }
+
+ downloadChunk()
+ },
+ getTotalTime() {
+ try {
+ if (app.showingPlaylist.relationships.tracks.data.length > 0) {
+ const timeInSeconds = Math.round([].concat(...app.showingPlaylist.relationships.tracks.data).reduce((a, { attributes: { durationInMillis } }) => a + durationInMillis, 0) / 1000);
+ return `${app.showingPlaylist.relationships.tracks.data.length} ${app.getLz('term.track', options = { count: app.showingPlaylist.relationships.tracks.data.length })}, ${this.convertTime(timeInSeconds, 'long')}`
+ } else return ""
+ } catch (err) {
+ return ""
+ }
+ },
+ async getLibrarySongs() {
+ let response = await this.mkapi("songs", true, "", { limit: 100, l: this.mklang }, { includeResponseMeta: !0 })
+ this.library.songs.listing = response.data.data
+ this.library.songs.meta = response.data.meta
+ },
+ async getLibraryAlbums() {
+ let response = await this.mkapi("albums", true, "", { limit: 100, l: this.mklang }, { includeResponseMeta: !0 })
+ this.library.albums.listing = response.data.data
+ this.library.albums.meta = response.data.meta
+ },
+ async getListenNow(attempt = 0) {
+ if (this.listennow.timestamp > Date.now() - 120000) {
+ return
+ }
+
+ if (attempt > 3) {
+ return
+ }
+ try {
+ this.listennow = (await this.mk.api.v3.music(`v1/me/recommendations?timezone=${encodeURIComponent(this.formatTimezoneOffset())}`, {
+ name: "listen-now",
+ with: "friendsMix,library,social",
+ "art[social-profiles:url]": "c",
+ "art[url]": "c,f",
+ "omit[resource]": "autos",
+ "relate[editorial-items]": "contents",
+ extend: ["editorialCard", "editorialVideo"],
+ "extend[albums]": ["artistUrl"],
+ "extend[library-albums]": ["artistUrl", "editorialVideo"],
+ "extend[playlists]": ["artistNames", "editorialArtwork", "editorialVideo"],
+ "extend[library-playlists]": ["artistNames", "editorialArtwork", "editorialVideo"],
+ "extend[social-profiles]": "topGenreNames",
+ "include[albums]": "artists",
+ "include[songs]": "artists",
+ "include[music-videos]": "artists",
+ "fields[albums]": ["artistName", "artistUrl", "artwork", "contentRating", "editorialArtwork", "editorialVideo", "name", "playParams", "releaseDate", "url"],
+ "fields[artists]": ["name", "url"],
+ "extend[stations]": ["airDate", "supportsAirTimeUpdates"],
+ "meta[stations]": "inflectionPoints",
+ types: "artists,albums,editorial-items,library-albums,library-playlists,music-movies,music-videos,playlists,stations,uploaded-audios,uploaded-videos,activities,apple-curators,curators,tv-shows,social-upsells",
+ platform: "web",
+ l: this.mklang
+ }, {
+ includeResponseMeta: !0,
+ reload: !0
+ })).data;
+ this.listennow.timestamp = Date.now()
+ console.log(this.listennow)
+ } catch (e) {
+ console.log(e)
+ this.getListenNow(attempt + 1)
+ }
+ },
+ async getBrowsePage(attempt = 0) {
+ if (this.browsepage.timestamp > Date.now() - 120000) {
+ return
+ }
+ if (attempt > 3) {
+ return
+ }
+ try {
+ let browse = await app.mk.api.v3.music(`/v1/editorial/${app.mk.storefrontId}/groupings`, {
+ platform: "web",
+ name: "music",
+ "omit[resource:artists]": "relationships",
+ "include[albums]": "artists",
+ "include[songs]": "artists",
+ "include[music-videos]": "artists",
+ extend: "editorialArtwork,artistUrl",
+ "fields[artists]": "name,url,artwork,editorialArtwork,genreNames,editorialNotes",
+ "art[url]": "f",
+ l: this.mklang
+ });
+ this.browsepage = browse.data.data[0];
+ this.browsepage.timestamp = Date.now()
+ console.log(this.browsepage)
+ } catch (e) {
+ console.log(e)
+ this.getBrowsePage(attempt + 1)
+ }
+ },
+ async getRadioStations(attempt = 0) {
+ if (attempt > 3) {
+ return
+ }
+ try {
+ this.radio.personal = (await app.mk.api.v3.music(`/v1/me/recent/radio-stations`, {
+ "platform": "web",
+ "art[url]": "f",
+ l: this.mklang
+ })).data.data;
+ } catch (e) {
+ console.log(e)
+ this.getRadioStations(attempt + 1)
+ }
+ },
+ async getMadeForYou(attempt = 0) {
+ if (attempt > 3) {
+ return
+ }
+ try {
+ let mfu = await app.mk.api.v3.music("/v1/me/library/playlists?platform=web&extend=editorialVideo&fields%5Bplaylists%5D=lastModifiedDate&filter%5Bfeatured%5D=made-for-you&include%5Blibrary-playlists%5D=catalog&fields%5Blibrary-playlists%5D=artwork%2Cname%2CplayParams%2CdateAdded")
+ this.madeforyou = mfu.data
+ } catch (e) {
+ console.log(e)
+ this.getMadeForYou(attempt + 1)
+ }
+ },
+ newPlaylistFolder(name = app.getLz('term.newPlaylistFolder')) {
+ let self = this
+ this.mk.api.v3.music(
+ "/v1/me/library/playlist-folders/", {}, {
+ fetchOptions: {
+ method: "POST",
+ body: JSON.stringify({
+ attributes: { name: name }
+ })
+ }
+ }
+ ).then((res) => {
+ let playlist = (res.data.data[0])
+ self.playlists.listing.push({
+ id: playlist.id,
+ attributes: {
+ name: playlist.attributes.name
+ },
+ type: "library-playlist-folders",
+ parent: "p.playlistsroot"
+ })
+ self.sortPlaylists()
+ setTimeout(() => {
+ app.refreshPlaylists()
+ }, 13000)
+ })
+ },
+ showSearch() {
+ this.page = "search"
+ },
+ loadLyrics() {
+ const musicType = (MusicKit.getInstance().nowPlayingItem != null) ? MusicKit.getInstance().nowPlayingItem["type"] ?? '' : '';
+ // console.log("mt", musicType)
+ if (musicType === "musicVideo") {
+ this.loadYTLyrics();
+ } else {
+ if (app.cfg.lyrics.enable_mxm) {
+ this.loadMXM();
+ } else {
+ this.loadAMLyrics();
+ }
+ }
+ },
+ loadAMLyrics() {
+ const songID = (this.mk.nowPlayingItem != null) ? this.mk.nowPlayingItem["_songId"] ?? (this.mk.nowPlayingItem["songId"] ?? -1) : -1;
+ // this.getMXM( trackName, artistName, 'en', duration);
+ if (songID != -1) {
+ this.mk.api.v3.music(`v1/catalog/${this.mk.storefrontId}/songs/${songID}/lyrics`)
+ .then((response) => {
+ this.lyricsMediaItem = response.data?.data[0]?.attributes["ttml"]
+ this.parseTTML()
+ })
+ }
+ },
+ addToLibrary(id) {
+ let self = this
+ this.mk.addToLibrary(id).then((data) => {
+ self.getLibrarySongsFull(true)
+ })
+ notyf.success(app.getLz('action.addToLibrary.success'));
+ },
+ removeFromLibrary(kind, id) {
+ let self = this
+ let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
+ app.mk.api.v3.music(`v1/me/library/${truekind}/${id.toString()}`, {}, {
+ fetchOptions: {
+ method: "DELETE"
+ }
+ }).then((data) => {
+ self.getLibrarySongsFull(true)
+ })
+ notyf.success(app.getLz('action.removeFromLibrary.success'))
+ },
+ async loadYTLyrics() {
+ const track = (this.mk.nowPlayingItem != null) ? this.mk.nowPlayingItem.title ?? '' : '';
+ const artist = (this.mk.nowPlayingItem != null) ? this.mk.nowPlayingItem.artistName ?? '' : '';
+ const time = (this.mk.nowPlayingItem != null) ? (Math.round((this.mk.nowPlayingItem.attributes["durationInMillis"] ?? -1000) / 1000) ?? -1) : -1;
+ ipcRenderer.invoke('getYTLyrics', track, artist).then((result) => {
+ if (result.length > 0) {
+ let ytid = result[0]['id']['videoId'];
+ if (app.cfg.lyrics.enable_yt) {
+ loadYT(ytid, app.cfg.lyrics.mxm_language ?? "en")
+ } else {
+ app.loadMXM()
+ }
+ } else {
+ app.loadMXM()
+ }
+
+ function loadYT(id, lang) {
+ let req = new XMLHttpRequest();
+ let url = `https://www.youtube.com/watch?&v=${id}`;
+ req.open('GET', url, true);
+ req.onerror = function (e) {
+ this.loadMXM();
+ }
+ req.onload = function () {
+ // console.log(this.responseText);
+ let res = this.responseText;
+ let captionurl1 = res.substring(res.indexOf(`{"playerCaptionsRenderer":{"baseUrl":"`) + (`{"playerCaptionsRenderer":{"baseUrl":"`).length);
+ let captionurl = captionurl1.substring(0, captionurl1.indexOf(`"`));
+ if (captionurl.includes("timedtext")) {
+ let json = JSON.parse(`{"url": "${captionurl}"}`);
+ let newurl = json.url + `&lang=${lang}&format=ttml`
+
+ let req2 = new XMLHttpRequest();
+
+ req2.open('GET', newurl, true);
+ req2.onerror = function (e) {
+ app.loadMXM();
+ }
+ req2.onload = function () {
+ try {
+ const ttmlLyrics = this.responseText;
+ if (ttmlLyrics) {
+ this.lyricsMediaItem = ttmlLyrics
+ this.parseTTML()
+ }
+ } catch (e) {
+ app.loadMXM();
+ }
+
+ }
+ req2.send();
+ } else {
+
+ app.loadMXM();
+
+ }
+ }
+ req.send();
+ }
+
+ })
+
+ },
+ loadMXM() {
+ let attempt = 0;
+ const track = encodeURIComponent((this.mk.nowPlayingItem != null) ? this.mk.nowPlayingItem.title ?? '' : '');
+ const artist = encodeURIComponent((this.mk.nowPlayingItem != null) ? this.mk.nowPlayingItem.artistName ?? '' : '');
+ const time = encodeURIComponent((this.mk.nowPlayingItem != null) ? (Math.round((this.mk.nowPlayingItem.attributes["durationInMillis"] ?? -1000) / 1000) ?? -1) : -1);
+ const id = encodeURIComponent((this.mk.nowPlayingItem != null) ? app.mk.nowPlayingItem._songId ?? (app.mk.nowPlayingItem["songId"] ?? '') : '');
+ let lrcfile = "";
+ let richsync = [];
+ const lang = app.cfg.lyrics.mxm_language // translation language
+ function revisedRandId() {
+ return Math.random().toString(36).replace(/[^a-z]+/g, '').slice(2, 10);
+ }
+
+ /* get token */
+ function getToken(mode, track, artist, songid, lang, time, id) {
+ if (attempt > 2) {
+ app.loadAMLyrics();
+ } else {
+ attempt = attempt + 1;
+ let url = "https://apic-desktop.musixmatch.com/ws/1.1/token.get?app_id=web-desktop-app-v1.0&t=" + revisedRandId();
+ let req = new XMLHttpRequest();
+ req.overrideMimeType("application/json");
+ req.open('GET', url, true);
+ req.setRequestHeader("authority", "apic-desktop.musixmatch.com");
+ req.onload = function () {
+ let jsonResponse = JSON.parse(this.responseText);
+ let status2 = jsonResponse["message"]["header"]["status_code"];
+ if (status2 == 200) {
+ let token = jsonResponse["message"]["body"]["user_token"] ?? '';
+ if (token != "" && token != "UpgradeOnlyUpgradeOnlyUpgradeOnlyUpgradeOnly") {
+ console.log('200 token', mode);
+ // token good
+ app.mxmtoken = token;
+
+ if (mode == 1) {
+ getMXMSubs(track, artist, app.mxmtoken, lang, time, id);
+ } else {
+ getMXMTrans(songid, lang, app.mxmtoken);
+ }
+ } else {
+ console.log('fake 200 token');
+ getToken(mode, track, artist, songid, lang, time)
+ }
+ } else {
+ // console.log('token 4xx');
+ getToken(mode, track, artist, songid, lang, time)
+ }
+
+ };
+ req.onerror = function () {
+ console.log('error');
+ app.loadAMLyrics();
+ };
+ req.send();
+ }
+ }
+
+ function getMXMSubs(track, artist, token, lang, time, id) {
+ let usertoken = encodeURIComponent(token);
+ let richsyncQuery = (app.cfg.lyrics.mxm_karaoke) ? "&optional_calls=track.richsync" : ""
+ let timecustom = (!time || (time && time < 0)) ? '' : `&f_subtitle_length=${time}&q_duration=${time}&f_subtitle_length_max_deviation=40`;
+ let itunesid = (id && id != "") ? `&track_itunes_id=${id}` : '';
+ let url = "https://apic-desktop.musixmatch.com/ws/1.1/macro.subtitles.get?format=json&namespace=lyrics_richsynched" + richsyncQuery + "&subtitle_format=lrc&q_artist=" + artist + "&q_track=" + track + itunesid + "&usertoken=" + usertoken + timecustom + "&app_id=web-desktop-app-v1.0&t=" + revisedRandId();
+ let req = new XMLHttpRequest();
+ req.overrideMimeType("application/json");
+ req.open('GET', url, true);
+ req.setRequestHeader("authority", "apic-desktop.musixmatch.com");
+ req.onload = function () {
+ let jsonResponse = JSON.parse(this.responseText);
+ console.log(jsonResponse);
+ let status1 = jsonResponse["message"]["header"]["status_code"];
+
+ if (status1 == 200) {
+ let id = '';
+ try {
+ if (jsonResponse["message"]["body"]["macro_calls"]["matcher.track.get"]["message"]["header"]["status_code"] == 200 && jsonResponse["message"]["body"]["macro_calls"]["track.subtitles.get"]["message"]["header"]["status_code"] == 200) {
+ id = jsonResponse["message"]["body"]["macro_calls"]["matcher.track.get"]["message"]["body"]["track"]["track_id"] ?? '';
+ lrcfile = jsonResponse["message"]["body"]["macro_calls"]["track.subtitles.get"]["message"]["body"]["subtitle_list"][0]["subtitle"]["subtitle_body"];
+
+ try {
+ let lrcrich = jsonResponse["message"]["body"]["macro_calls"]["track.richsync.get"]["message"]["body"]["richsync"]["richsync_body"];
+ richsync = JSON.parse(lrcrich);
+ app.richlyrics = richsync;
+ } catch (_) {
+ }
+ }
+
+ if (lrcfile == "") {
+ app.loadAMLyrics()
+ } else {
+ if (richsync == [] || richsync.length == 0) {
+ console.log("ok");
+ // process lrcfile to json here
+ app.lyricsMediaItem = lrcfile
+ let u = app.lyricsMediaItem.split(/[\r\n]/);
+ let preLrc = []
+ for (var i = u.length - 1; i >= 0; i--) {
+ let xline = (/(\[[0-9.:\[\]]*\])+(.*)/).exec(u[i])
+ let end = (preLrc.length > 0) ? ((preLrc[preLrc.length - 1].startTime) ?? 99999) : 99999
+ preLrc.push({
+ startTime: app.toMS(xline[1].substring(1, xline[1].length - 2)) ?? 0,
+ endTime: end,
+ line: xline[2],
+ translation: ''
+ })
+ }
+ if (preLrc.length > 0)
+ preLrc.push({
+ startTime: 0,
+ endTime: preLrc[preLrc.length - 1].startTime,
+ line: "lrcInstrumental",
+ translation: ''
+ });
+ app.lyrics = preLrc.reverse();
+ } else {
+ let preLrc = richsync.map(function (item) {
+ return {
+ startTime: item.ts,
+ endTime: item.te,
+ line: item.x,
+ translation: ''
+ }
+ })
+ if (preLrc.length > 0)
+ preLrc.unshift({
+ startTime: 0,
+ endTime: preLrc[0].startTime,
+ line: "lrcInstrumental",
+ translation: ''
+ });
+ app.lyrics = preLrc;
+ }
+ if (lrcfile != null && lrcfile != '' && lang != "disabled") {
+ // load translation
+ getMXMTrans(id, lang, token);
+ } else {
+ app.loadAMLyrics()
+ }
+ }
+ } catch (e) {
+ console.log(e);
+ app.loadAMLyrics()
+ }
+ } else { //4xx rejected
+ getToken(1, track, artist, '', lang, time);
+ }
+ }
+ req.onerror = function () {
+ console.log('error');
+ app.loadAMLyrics();
+ };
+ req.send();
+ }
+
+ function getMXMTrans(id, lang, token) {
+ if (lang != "disabled" && id != '') {
+ let usertoken = encodeURIComponent(token);
+ let url2 = "https://apic-desktop.musixmatch.com/ws/1.1/crowd.track.translations.get?translation_fields_set=minimal&selected_language=" + lang + "&track_id=" + id + "&comment_format=text&part=user&format=json&usertoken=" + usertoken + "&app_id=web-desktop-app-v1.0&t=" + revisedRandId();
+ let req2 = new XMLHttpRequest();
+ req2.overrideMimeType("application/json");
+ req2.open('GET', url2, true);
+ req2.setRequestHeader("authority", "apic-desktop.musixmatch.com");
+ req2.onload = function () {
+ let jsonResponse2 = JSON.parse(this.responseText);
+ console.log(jsonResponse2);
+ let status2 = jsonResponse2["message"]["header"]["status_code"];
+ if (status2 == 200) {
+ try {
+ let preTrans = []
+ let u = app.lyrics;
+ let translation_list = jsonResponse2["message"]["body"]["translations_list"];
+ if (translation_list.length > 0) {
+ for (var i = 0; i < u.length - 1; i++) {
+ preTrans[i] = ""
+ for (var trans_line of translation_list) {
+ if (u[i].line == " " + trans_line["translation"]["matched_line"] || u[i].line == trans_line["translation"]["matched_line"]) {
+ u[i].translation = trans_line["translation"]["description"];
+ break;
+ }
+ }
+ }
+ app.lyrics = u;
+ }
+ } catch (e) {
+ /// not found trans -> ignore
+ }
+ } else { //4xx rejected
+ getToken(2, '', '', id, lang, '');
+ }
+ }
+ req2.send();
+ }
+
+ }
+
+ if (track != "" & track != "No Title Found") {
+ if (app.mxmtoken != null && app.mxmtoken != '') {
+ getMXMSubs(track, artist, app.mxmtoken, lang, time, id)
+ } else {
+ getToken(1, track, artist, '', lang, time);
+ }
+ }
+ },
+ toMS(str) {
+ let rawTime = str.match(/(\d+:)?(\d+:)?(\d+)(\.\d+)?/);
+ let hours = (rawTime[2] != null) ? (rawTime[1].replace(":", "")) : 0;
+ let minutes = (rawTime[2] != null) ? (hours * 60 + rawTime[2].replace(":", "") * 1) : ((rawTime[1] != null) ? rawTime[1].replace(":", "") : 0);
+ let seconds = (rawTime[3] != null) ? (rawTime[3]) : 0;
+ let milliseconds = (rawTime[4] != null) ? (rawTime[4].replace(".", "")) : 0
+ return parseFloat(`${minutes * 60 + seconds * 1}.${milliseconds * 1}`);
+ },
+ parseTTML() {
+ this.lyrics = [];
+ let preLrc = [];
+ let xml = this.stringToXml(this.lyricsMediaItem);
+ let lyricsLines = xml.getElementsByTagName('p');
+ let synced = true;
+ let endTimes = [];
+ if (xml.getElementsByTagName('tt')[0].getAttribute("itunes:timing") === "None") {
+ synced = false;
+ }
+ endTimes.push(0);
+ if (synced) {
+ for (let element of lyricsLines) {
+ let start = this.toMS(element.getAttribute('begin'))
+ let end = this.toMS(element.getAttribute('end'))
+ if (start - endTimes[endTimes.length - 1] > 5 && endTimes[endTimes.length - 1] != 0) {
+ preLrc.push({
+ startTime: endTimes[endTimes.length - 1],
+ endTime: start,
+ line: "lrcInstrumental"
+ });
+ }
+ preLrc.push({ startTime: start, endTime: end, line: element.textContent });
+ endTimes.push(end);
+ }
+ // first line dot
+ if (preLrc.length > 0)
+ preLrc.unshift({ startTime: 0, endTime: preLrc[0].startTime, line: "lrcInstrumental" });
+ } else {
+ for (let element of lyricsLines) {
+ preLrc.push({ startTime: 9999999, endTime: 9999999, line: element.textContent });
+ }
+ }
+ this.lyrics = preLrc;
+
+ },
+ parseLyrics() {
+ let xml = this.stringToXml(this.lyricsMediaItem)
+ let json = xmlToJson(xml);
+ this.lyrics = json
+ },
+ stringToXml(st) {
+ // string to xml
+ let xml = (new DOMParser()).parseFromString(st, "text/xml");
+ return xml;
+
+ },
+ getCurrentTime() {
+ return parseFloat(this.hmsToSecondsOnly(this.parseTime(this.mk.nowPlayingItem.attributes.durationInMillis - app.mk.currentPlaybackTimeRemaining * 1000)));
+ },
+ seekTo(time) {
+ this.mk.seekToTime(time);
+ },
+ parseTime(value) {
+ let minutes = Math.floor(value / 60000);
+ let seconds = ((value % 60000) / 1000).toFixed(0);
+ return minutes + ":" + (seconds < 10 ? '0' : '') + seconds;
+ },
+ parseTimeDecimal(value) {
+ let minutes = Math.floor(value / 60000);
+ let seconds = ((value % 60000) / 1000).toFixed(0);
+ return minutes + "." + (seconds < 10 ? '0' : '') + seconds;
+ },
+ hmsToSecondsOnly(str) {
+ let p = str.split(':'),
+ s = 0,
+ m = 1;
+
+ while (p.length > 0) {
+ s += m * parseInt(p.pop(), 10);
+ m *= 60;
+ }
+
+ return s;
+ },
+ getLyricBGStyle(start, end) {
+ let currentTime = this.getCurrentTime();
+ // let duration = this.mk.nowPlayingItem.attributes.durationInMillis
+ let start2 = this.hmsToSecondsOnly(start)
+ let end2 = this.hmsToSecondsOnly(end)
+ // let currentProgress = ((100 * (currentTime)) / (end2))
+ // check if currenttime is between start and end
+ this.player.lyricsDebug.start = start2
+ this.player.lyricsDebug.end = end2
+ this.player.lyricsDebug.current = currentTime
+ if (currentTime >= start2 && currentTime <= end2) {
+ return {
+ "--bgSpeed": `${(end2 - start2)}s`
+ }
+ } else {
+ return {}
+ }
+ },
+ playMediaItemById(id, kind, isLibrary, raurl = "") {
+ let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
+ console.log(id, truekind, isLibrary)
+ try {
+ if (truekind.includes("artist")) {
+ app.mk.setStationQueue({ artist: 'a-' + id }).then(() => {
+ app.mk.play()
+ })
+ } else if (truekind == "radioStations") {
+ this.mk.setStationQueue({ url: raurl }).then(function (queue) {
+ MusicKit.getInstance().play()
+ });
+ } else {
+ this.mk.setQueue({
+ [truekind]: [id],
+ parameters: { l: this.mklang }
+ }).then(function (queue) {
+ MusicKit.getInstance().play()
+ })
+ }
+ } catch (err) {
+ console.log(err)
+ this.playMediaItemById(id, kind, isLibrary, raurl)
+ }
+ },
+ queueParentandplayChild(parent, childIndex, item) {
+
+ /* Randomize array in-place using Durstenfeld shuffle algorithm */
+ function shuffleArray(array) {
+ for (var i = array.length - 1; i > 0; i--) {
+ var j = Math.floor(Math.random() * (i + 1));
+ var temp = array[i];
+ array[i] = array[j];
+ array[j] = temp;
+ }
+ }
+
+ let kind = parent.substring(0, parent.indexOf(":"))
+ let id = parent.substring(parent.indexOf(":") + 1, parent.length)
+ let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
+ console.log(truekind, id)
+
+ try {
+ if (app.library.songs.displayListing.length > childIndex && parent == "librarysongs") {
+ console.log(item)
+ if (item && ((app.library.songs.displayListing[childIndex].id != item.id))) {
+ childIndex = app.library.songs.displayListing.indexOf(item)
+ }
+
+ let query = app.library.songs.displayListing.map(item => new MusicKit.MediaItem(item));
+
+
+ app.mk.stop().then(() => {
+ if (item) {
+ app.mk.setQueue({
+ [item.attributes.playParams.kind ?? item.type]: item.attributes.playParams.id ?? item.id,
+ parameters: { l: app.mklang }
+ }).then(function () {
+ app.mk.play().then(() => {
+ if (app.mk.shuffleMode == 1) {
+ shuffleArray(query)
+ } else {
+ for (let i = 0; i < query.length; i++) {
+ if (query[i].id == item.id) {
+ query.splice(0, i + 1);
+ break;
+ }
+ }
+ }
+ app.mk.queue.append(query)
+ })
+ })
+ } else {
+ app.mk.queue.splice(0, app.mk.queue._itemIDs.length)
+ if (app.mk.shuffleMode == 1) {
+ shuffleArray(query)
+ }
+ app.mk.queue.append(query)
+ if (childIndex != -1) {
+ app.mk.changeToMediaAtIndex(childIndex)
+ } else {
+ app.mk.play()
+ }
+
+ }
+ })
+ } else if (parent.startsWith('listitem-hr')) {
+ app.mk.stop().then(() => {
+ if (app.mk.shuffleMode == 1) {
+ app.mk.setQueue({
+ [item.attributes.playParams.kind ?? item.type]: item.attributes.playParams.id ?? item.id
+ }).then(function () {
+ app.mk.play().then(() => {
+ let data = JSON.parse(parent.split('listitem-hr')[1] ?? '[]')
+ let itemsToPlay = {}
+ let u = data.map(x => x.id)
+ try {
+ data.splice(u.indexOf(item.attributes.playParams.id ?? item.id), 1)
+ } catch (e) {
+ }
+ if (app.mk.shuffleMode == 1) {
+ shuffleArray(data)
+ }
+ data.forEach(item => {
+ if (!itemsToPlay[item.kind]) {
+ itemsToPlay[item.kind] = []
+ }
+ itemsToPlay[item.kind].push(item.id)
+ })
+ // loop through itemsToPlay
+ for (let kind in itemsToPlay) {
+ let ids = itemsToPlay[kind]
+ if (ids.length > 0) {
+ app.mk.playLater({ [kind + "s"]: itemsToPlay[kind] })
+ }
+ }
+ })
+ })
+ } else {
+ let data = JSON.parse(parent.split('listitem-hr')[1] ?? '[]')
+ let itemsToPlay = {}
+ data.forEach(item => {
+ if (!itemsToPlay[item.kind]) {
+ itemsToPlay[item.kind] = []
+ }
+ itemsToPlay[item.kind].push(item.id)
+ })
+ // loop through itemsToPlay
+ app.mk.queue.splice(0, app.mk.queue._itemIDs.length)
+ let ind = 0;
+ for (let kind in itemsToPlay) {
+ let ids = itemsToPlay[kind]
+ if (ids.length > 0) {
+ if (app.mk.queue._itemIDs.length > 0) {
+ app.mk.playLater({ [kind + "s"]: itemsToPlay[kind] }).then(function () {
+ ind += 1;
+ console.log(ind, Object.keys(itemsToPlay).length)
+ if (ind >= Object.keys(itemsToPlay).length) {
+ app.mk.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(item.attributes.playParams.id ?? item.id))
+ }
+ }
+ )
+ } else {
+ app.mk.setQueue({ [kind + "s"]: itemsToPlay[kind] }).then(function () {
+ ind += 1;
+ console.log(ind, Object.keys(itemsToPlay).length)
+ if (ind >= Object.keys(itemsToPlay).length) {
+ app.mk.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(item.attributes.playParams.id ?? item.id))
+ }
+ }
+ )
+ }
+ }
+
+ }
+ }
+ })
+ } else {
+ app.mk.stop().then(() => {
+ if (truekind == "playlists" && (id.startsWith("p.") || id.startsWith("pl.u"))) {
+ app.mk.setQueue({
+ [item.attributes.playParams.kind ?? item.type]: item.attributes.playParams.id ?? item.id,
+ parameters: { l: app.mklang }
+ }).then(function () {
+ app.mk.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(item.id) ?? 1).then(function () {
+ if ((app.showingPlaylist && app.showingPlaylist.id == id)) {
+ let query = app.showingPlaylist.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
+ let u = query;
+ if (app.mk.shuffleMode == 1) {
+ shuffleArray(u)
+ } else {
+ for (let i = 0; i < app.showingPlaylist.relationships.tracks.data.length; i++) {
+ if (app.showingPlaylist.relationships.tracks.data[i].id == item.id) {
+ u.splice(0, i + 1);
+ break;
+ }
+ }
+ }
+ app.mk.queue.append(u)
+ } else {
+ app.getPlaylistFromID(id, true).then(function () {
+ let query = app.showingPlaylist.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
+ let u = query;
+ if (app.mk.shuffleMode == 1) {
+ shuffleArray(u)
+ } else {
+ for (let i = 0; i < app.showingPlaylist.relationships.tracks.data.length; i++) {
+ if (app.showingPlaylist.relationships.tracks.data[i].id == item.id) {
+ u.splice(0, i + 1);
+ break;
+ }
+ }
+ }
+ app.mk.queue.append(u)
+ })
+ }
+ })
+
+ })
+ } else {
+ this.mk.setQueue({
+ [truekind]: [id],
+ parameters: { l: this.mklang }
+ }).then(function (queue) {
+ if (item && ((queue._itemIDs[childIndex] != item.id))) {
+ childIndex = queue._itemIDs.indexOf(item.id)
+ }
+ if (childIndex != -1) {
+ app.mk.changeToMediaAtIndex(childIndex)
+ } else if (item) {
+ app.mk.playNext({
+ [item.attributes.playParams.kind ?? item.type]: item.attributes.playParams.id ?? item.id
+ }).then(function () {
+ app.mk.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(item.id) ?? 1)
+ app.mk.play()
+ })
+ } else {
+ app.mk.play()
+ }
+ })
+ }
+ })
+ }
+ } catch (err) {
+ console.log(err)
+ try {
+ app.mk.stop()
+ } catch (e) {
+ }
+ this.playMediaItemById(item.attributes.playParams.id ?? item.id, item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.url)
+ }
+
+ },
+ friendlyTypes(type) {
+ // use switch statement to return friendly name for media types "songs,artists,albums,playlists,music-videos,stations,apple-curators,curators"
+ switch (type) {
+ case "song":
+ return app.getLz('term.songs')
+ break;
+ case "artist":
+ return app.getLz('term.artists')
+ break;
+ case "album":
+ return app.getLz('term.albums')
+ break;
+ case "playlist":
+ return app.getLz('term.playlists')
+ break;
+ case "music_video":
+ return app.getLz('term.musicVideos')
+ break;
+ case "station":
+ return app.getLz('term.stations')
+ break;
+ case "apple-curator":
+ return app.getLz('term.appleCurators')
+ break;
+ case "radio_show":
+ return app.getLz('term.radioShows')
+ break;
+ case "record_label":
+ return app.getLz('term.recordLabels')
+ break;
+ case "radio_episode":
+ return app.getLz('podcast.episodes')
+ break;
+ case "video_extra":
+ return app.getLz('term.videoExtras')
+ break;
+ case "curator":
+ return app.getLz('term.curators')
+ break;
+ case "top":
+ return app.getLz('term.top')
+ break;
+ default:
+ return type
+ break;
+ }
+ },
+ async searchQuery(term = this.search.term) {
+ let self = this
+ if (term == "") {
+ return
+ }
+ //this.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/search?term=${this.search.term}`
+ this.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/search?term=${encodeURIComponent(this.search.term)}`, {
+ types: "activities,albums,apple-curators,artists,curators,editorial-items,music-movies,music-videos,playlists,songs,stations,tv-episodes,uploaded-videos,record-labels",
+ "relate[editorial-items]": "contents",
+ "include[editorial-items]": "contents",
+ "include[albums]": "artists",
+ "include[artists]": "artists",
+ "include[songs]": "artists,albums",
+ "include[music-videos]": "artists",
+ "extend": "artistUrl",
+ "fields[artists]": "url,name,artwork,hero",
+ "fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url",
+ "with": "serverBubbles,lyricHighlights",
+ "art[url]": "c,f",
+ "omit[resource]": "autos",
+ "platform": "web",
+ limit: 25,
+ l: this.mklang
+ }).then(function (results) {
+ results.data.results["meta"] = results.data.meta
+ self.search.results = results.data.results
+ })
+
+ await app.mk.api.v3.music(`v1/social/${app.mk.storefrontId}/search?term=${app.search.term}`, {
+ types: ["playlists", "social-profiles"],
+ limit: 25,
+ with: ["serverBubbles", "lyricSnippet"],
+ "art[url]": "f",
+ "art[social-profiles:url]": "c"
+ }, { includeResponseMeta: !0 }).then(function (results) {
+ results.data.results["meta"] = results.data.meta
+ self.search.resultsSocial = results.data.results
+ })
+ },
+ async inLibrary(items = []) {
+ let types = []
+
+ for (let item of items) {
+ let type = item.type
+ if (type.slice(-1) != "s") {
+ type += "s"
+ }
+ type = type.replace("library-", "")
+ let id = item.attributes.playParams.catalogId ?? item.attributes.playParams.id ?? item.id
+
+ let index = types.findIndex(function (type) {
+ return type.type == this
+ }, type)
+ if (index == -1) {
+ types.push({ type: type, id: [id] })
+ } else {
+ types[index].id.push(id)
+ }
+ }
+ let types2 = types.map(function (item) {
+ return {
+ [`ids[${item.type}]`]: [item.id]
+ }
+ })
+ types2 = types2.reduce(function (result, item) {
+ var key = Object.keys(item)[0]; //first property: a, b, c
+ result[key] = item[key];
+ return result;
+ }, {});
+ return (await this.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}`, {
+ ...{
+ "omit[resource]": "autos",
+ relate: "library",
+ fields: "inLibrary"
+ },
+ ...types2
+ })).data.data
+ },
+ isInLibrary(playParams) {
+ let self = this
+ let id = ""
+ // ugly code to check if current playback item is in library
+ if (typeof playParams == "undefined") {
+ return true
+ }
+ if (playParams["isLibrary"]) {
+ return true
+ } else if (playParams["catalogId"]) {
+ id = playParams["catalogId"]
+ } else if (playParams["id"]) {
+ id = playParams["id"]
+ }
+ let found = this.library.songs.listing.filter((item) => {
+ if (item["attributes"]) {
+ if (item["attributes"]["playParams"] && (item["attributes"]["playParams"]["catalogId"] == id)) {
+ return item;
+ }
+ }
+ })
+ if (found.length != 0) {
+ return true
+ } else {
+ return false
+ }
+ },
+ mkReady() {
+ if (this.mk["nowPlayingItem"]) {
+ return true
+ } else {
+ return false
+ }
+ },
+ getMediaItemArtwork(url, height = 64, width) {
+ if (typeof url == "undefined" || url == "") {
+ return "https://beta.music.apple.com/assets/product/MissingArtworkMusic.svg"
+ }
+ let newurl = `${url.replace('{w}', width ?? height).replace('{h}', height).replace('{f}', "webp").replace('{c}', ((width === 900) ? "sr" : "cc"))}`;
+
+ if (newurl.includes("900x516")) {
+ newurl = newurl.replace("900x516cc", "900x516sr").replace("900x516bb", "900x516sr");
+ }
+ return newurl
+ },
+ _rgbToRgb(rgb = [0, 0, 0]) {
+ // if rgb
+ return `rgb(${rgb[0]},${rgb[1]},${rgb[2]})`
+ },
+ getNowPlayingArtworkBG(size = 32, force = false) {
+ let self = this
+ if (typeof this.mk.nowPlayingItem === "undefined") return;
+ let bginterval = setInterval(() => {
+ if (!this.mkReady()) {
+ return ""
+ }
+
+ try {
+ if ((this.mk.nowPlayingItem && this.mk.nowPlayingItem["id"] != this.currentTrackID && document.querySelector('.bg-artwork')) || force) {
+ if (document.querySelector('.bg-artwork')) {
+ clearInterval(bginterval);
+ }
+ this.currentTrackID = this.mk.nowPlayingItem["id"];
+ document.querySelector('.bg-artwork').src = "";
+ if (this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"]) {
+ getBase64FromUrl(this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"].replace('{w}', size).replace('{h}', size)).then(img => {
+ document.querySelectorAll('.bg-artwork').forEach(artwork => {
+ artwork.src = img;
+ })
+ self.$store.commit("setLCDArtwork", img)
+ })
+ try {
+ clearInterval(bginterval);
+ } catch (err) {
+ }
+ } else {
+ this.setLibraryArtBG()
+ }
+ } else if (this.mk.nowPlayingItem["id"] == this.currentTrackID) {
+ try {
+ clearInterval(bginterval);
+ } catch (err) {
+ }
+ }
+ } catch (e) {
+ if (this.mk.nowPlayingItem && this.mk.nowPlayingItem["id"] && document.querySelector('.bg-artwork')) {
+ this.setLibraryArtBG()
+ try {
+ clearInterval(bginterval);
+ } catch (err) {
+ }
+ }
+ }
+ }, 200)
+ },
+ async getCurrentArtURL() {
+ try {
+ let artworkSize = 50
+ if (app.getThemeDirective("lcdArtworkSize") != "") {
+ artworkSize = app.getThemeDirective("lcdArtworkSize")
+ } else if (this.cfg.visual.directives.windowLayout == "twopanel") {
+ artworkSize = 70
+ }
+ this.currentArtUrl = '';
+ this.currentArtUrlRaw = '';
+ if (app.mk.nowPlayingItem != null && app.mk.nowPlayingItem.attributes != null && app.mk.nowPlayingItem.attributes.artwork != null && app.mk.nowPlayingItem.attributes.artwork.url != null && app.mk.nowPlayingItem.attributes.artwork.url != '') {
+ this.currentArtUrlRaw = (this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"] ?? '')
+ this.currentArtUrl = (this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"] ?? '').replace('{w}', artworkSize).replace('{h}', artworkSize);
+ try {
+ document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);
+ } catch (e) {
+ }
+ } else {
+ let data = await this.mk.api.v3.music(`/v1/me/library/songs/${this.mk.nowPlayingItem.id}`);
+ data = data.data.data[0];
+ if (data != null && data !== "" && data.attributes != null && data.attributes.artwork != null) {
+ this.currentArtUrlRaw = (this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"] ?? '')
+ this.currentArtUrl = (data["attributes"]["artwork"]["url"] ?? '').replace('{w}', artworkSize).replace('{h}', artworkSize);
+ ipcRenderer.send('updateRPCImage', this.currentArtUrl ?? '');
+ try {
+ document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);
+ } catch (e) {
+ }
+ } else {
+ this.currentArtUrlRaw = ''
+ this.currentArtUrl = '';
+ try {
+ document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);
+ } catch (e) {
+ }
+ }
+ }
+ } catch (e) {
+
+ }
+ },
+ async setLibraryArt() {
+ if (typeof this.mk.nowPlayingItem === "undefined") return;
+ try {
+ let data = await this.mk.api.v3.music(`/v1/me/library/songs/${this.mk.nowPlayingItem.id}`);
+ data = data.data.data[0];
+
+ if (data != null && data !== "") {
+ document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', 'url("' + (data["attributes"]["artwork"]["url"]).toString() + '")');
+ } else {
+ document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("")`);
+ }
+ } catch (e) {
+ }
+ },
+ async setLibraryArtBG() {
+ if (typeof this.mk.nowPlayingItem === "undefined") return;
+ try {
+ let data = await this.mk.api.v3.music(`/v1/me/library/songs/${this.mk.nowPlayingItem.id}`);
+ data = data.data.data[0];
+
+ if (data != null && data !== "") {
+ getBase64FromUrl((data["attributes"]["artwork"]["url"]).toString()).then(img => {
+ document.querySelector('.bg-artwork').forEach(artwork => {
+ artwork.src = img;
+ })
+ self.$store.commit("setLCDArtwork", img)
+ })
+ }
+ } catch (e) {
+ }
+
+ },
+ quickPlay(query) {
+ let self = this
+ MusicKit.getInstance().api.search(query, { limit: 2, types: 'songs' }).then(function (data) {
+ MusicKit.getInstance().setQueue({
+ song: data["songs"]['data'][0]["id"],
+ parameters: { l: app.mklang }
+ }).then(function (queue) {
+ MusicKit.getInstance().play()
+ setTimeout(() => {
+ self.$forceUpdate()
+ }, 1000)
+ })
+ })
+ },
+ async getRating(item) {
+ let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
+ let id = item.attributes?.playParams?.catalogId ? item.attributes.playParams.catalogId : (item.attributes?.playParams?.id ?? item.id)
+ if (item.id != null && (item.id.toString()).startsWith("i.")) {
+ if (!type.startsWith("library-")) {
+ type = "library-" + type
+ }
+ id = item.id
+ }
+ let response = await this.mk.api.v3.music(`/v1/me/ratings/${type}?platform=web&ids=${type.includes('library') ? item.id : id}`)
+ if (response.data.data.length != 0) {
+ let value = response.data.data[0].attributes.value
+ return value
+ } else {
+ return 0
+ }
+ },
+ love(item) {
+ let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
+ let id = item.attributes?.playParams?.catalogId ? item.attributes.playParams.catalogId : (item.attributes?.playParams?.id ?? item.id)
+ if (item.id != null && (item.id.toString()).startsWith("i.")) {
+ if (!type.startsWith("library-")) {
+ type = "library-" + type
+ }
+ id = item.id
+ }
+ this.mk.api.v3.music(`/v1/me/ratings/${type}/${id}`, {}, {
+ fetchOptions: {
+ method: "PUT",
+ body: JSON.stringify({
+ "type": "rating",
+ "attributes": {
+ "value": 1
+ }
+ })
+ }
+ })
+ },
+ dislike(item) {
+ let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
+ let id = item.attributes?.playParams?.catalogId ? item.attributes.playParams.catalogId : (item.attributes?.playParams?.id ?? item.id)
+ if (item.id != null && (item.id.toString()).startsWith("i.")) {
+ if (!type.startsWith("library-")) {
+ type = "library-" + type
+ }
+ id = item.id
+ }
+ this.mk.api.v3.music(`/v1/me/ratings/${type}/${id}`, {}, {
+ fetchOptions: {
+ method: "PUT",
+ body: JSON.stringify({
+ "type": "rating",
+ "attributes": {
+ "value": -1
+ }
+ })
+ }
+ })
+ },
+ unlove(item) {
+ let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
+ let id = item.attributes.playParams.catalogId ? item.attributes.playParams.catalogId : item.id
+ if (item.id.startsWith("i.")) {
+ if (!type.startsWith("library-")) {
+ type = "library-" + type
+ }
+ id = item.id
+ }
+ this.mk.api.v3.music(`/v1/me/ratings/${type}/${id}`, {}, {
+ fetchOptions: {
+ method: "DELETE",
+ }
+ })
+ },
+ checkScrollDirectionIsUp(event) {
+ if (event.wheelDelta) {
+ return event.wheelDelta > 0;
+ }
+ return event.deltaY < 0;
+ },
+ volumeUp() {
+ if ((app.mk.volume + app.cfg.audio.volumeStep) > 1) {
+ app.mk.volume = app.cfg.audio.maxVolume;
+ console.log('setting max volume')
+ } else {
+ console.log('volume up')
+ app.mk.volume = ((app.mk.volume * 100) + (app.cfg.audio.volumeStep * 100)) / 100
+ }
+ },
+ volumeDown() {
+ if ((app.mk.volume - app.cfg.audio.volumeStep) < 0) {
+ app.mk.volume = 0;
+ console.log('setting volume to 0')
+ } else {
+ console.log('volume down')
+ app.mk.volume = ((app.mk.volume * 100) - (app.cfg.audio.volumeStep * 100)) / 100
+ }
+ },
+ volumeWheel(event) {
+ app.checkScrollDirectionIsUp(event) ? this.volumeUp() : this.volumeDown()
+ },
+ muteButtonPressed() {
+ if (this.cfg.audio.muted) {
+ this.mk.volume = this.cfg.audio.lastVolume;
+ this.cfg.audio.muted = false;
+ } else {
+ this.cfg.audio.lastVolume = this.cfg.audio.volume;
+ this.mk.volume = 0;
+ this.cfg.audio.muted = true;
+ }
+ },
+ checkMuteChange() {
+ if (this.cfg.audio.muted) {
+ this.cfg.audio.muted = false;
+ }
+ },
+ async apiCall(url, callback) {
+ const xmlHttp = new XMLHttpRequest();
+
+ xmlHttp.onreadystatechange = (e) => {
+ if (xmlHttp.readyState !== 4) {
+ return;
+ }
+
+ if (xmlHttp.status === 200) {
+ // console.log('SUCCESS', xmlHttp.responseText);
+ callback(JSON.parse(xmlHttp.responseText));
+ } else {
+ console.warn('request_error');
+ }
+ };
+
+ xmlHttp.open("GET", url);
+ xmlHttp.setRequestHeader("Authorization", "Bearer " + MusicKit.getInstance().developerToken);
+ xmlHttp.setRequestHeader("Music-User-Token", "" + MusicKit.getInstance().musicUserToken);
+ xmlHttp.setRequestHeader("Accept", "application/json");
+ xmlHttp.setRequestHeader("Content-Type", "application/json");
+ xmlHttp.responseType = "text";
+ xmlHttp.send();
+ },
+ fetchPlaylist(id, callback) {
+ // id can be found in playlist.attributes.playParams.globalId
+ // this.mk.api.
+ this.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/playlists/${id}`).then(res => {
+ callback(res.data.data[0])
+ })
+
+ // tracks are found in relationship.data
+ },
+ windowFocus(val) {
+ if (val) {
+ document.querySelectorAll(".animated-artwork-video").forEach(el => {
+ el.play()
+ })
+ document.querySelector("body").classList.remove("stopanimation")
+ this.animateBackground = true
+ } else {
+ document.querySelectorAll(".animated-artwork-video").forEach(el => {
+ el.pause()
+ })
+ document.querySelector("body").classList.add("stopanimation")
+ this.animateBackground = false
+ }
+ },
+ async nowPlayingContextMenu(event) {
+ let self = this
+ let data_type = this.mk.nowPlayingItem.playParams.kind
+ let item_id = this.mk.nowPlayingItem.attributes.playParams.id ?? this.mk.nowPlayingItem.id
+ let isLibrary = this.mk.nowPlayingItem.attributes.playParams.isLibrary ?? false
+ let params = { "fields[songs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library", "t": "1" }
+ app.selectedMediaItems = []
+ app.select_selectMediaItem(item_id, data_type, 0, '12344', isLibrary)
+ let useMenu = "normal"
+ let menus = {
+ multiple: {
+ items: []
+ },
+ normal: {
+ headerItems: [{
+ "icon": "./assets/feather/heart.svg",
+ "id": "love",
+ "name": app.getLz('action.love'),
+ "hidden": false,
+ "disabled": true,
+ "action": function () {
+ app.love(app.mk.nowPlayingItem)
+ }
+ },
+ {
+ "icon": "./assets/feather/heart.svg",
+ "id": "unlove",
+ "active": true,
+ "name": app.getLz('action.unlove'),
+ "hidden": true,
+ "action": function () {
+ app.unlove(app.mk.nowPlayingItem)
+ }
+ },
+ {
+ "icon": "./assets/feather/thumbs-down.svg",
+ "id": "dislike",
+ "name": app.getLz('action.dislike'),
+ "hidden": false,
+ "disabled": true,
+ "action": function () {
+ app.dislike(app.mk.nowPlayingItem)
+ }
+ },
+ {
+ "icon": "./assets/feather/thumbs-down.svg",
+ "id": "undo_dislike",
+ "name": app.getLz('action.undoDislike'),
+ "active": true,
+ "hidden": true,
+ "action": function () {
+ app.unlove(app.mk.nowPlayingItem)
+ }
+ },
+ ],
+ items: [
+ {
+ "icon": "./assets/feather/plus.svg",
+ "id": "addToLibrary",
+ "name": app.getLz('action.addToLibrary') + " ...",
+ "disabled": true,
+ "action": function () {
+ app.addToLibrary(app.mk.nowPlayingItem.id);
+ }
+ },
+ {
+ "id": "removeFromLibrary",
+ "icon": "./assets/feather/x-circle.svg",
+ "name": app.getLz('action.removeFromLibrary'),
+ "hidden": true,
+ "action": function () {
+ self.removeFromLibrary()
+ }
+ },
+ {
+ "icon": "./assets/feather/list.svg",
+ "name": app.getLz('action.addToPlaylist') + " ...",
+ "action": function () {
+ app.promptAddToPlaylist()
+ }
+ },
+ {
+ "icon": "./assets/feather/radio.svg",
+ "name": app.getLz('action.startRadio'),
+ "action": function () {
+ app.mk.setStationQueue({ song: app.mk.nowPlayingItem.id }).then(() => {
+ app.mk.play()
+ app.selectedMediaItems = []
+ })
+ }
+ },
+ {
+ "icon": "./assets/feather/user.svg",
+ "name": app.getLz('action.goToArtist'),
+ "action": function () {
+ app.appRoute(`artist/${app.mk.nowPlayingItem.relationships.artists.data[0].id}`)
+ }
+ },
+ {
+ "icon": "./assets/feather/disc.svg",
+ "name": app.getLz('action.goToAlbum'),
+ "action": function () {
+ app.appRoute(`album/${app.mk.nowPlayingItem.relationships.albums.data[0].id}`)
+ }
+ },
+ {
+ "icon": "./assets/feather/share.svg",
+ "name": app.getLz('action.share'),
+ "action": function () {
+ app.mkapi(app.mk.nowPlayingItem.attributes?.playParams?.kind ?? app.mk.nowPlayingItem.type ?? 'songs', false, app.mk.nowPlayingItem._songId ?? (app.mk.nowPlayingItem.songId ?? app.mk.nowPlayingItem.id) ?? '').then(u => {
+ app.copyToClipboard((u.data.data.length && u.data.data.length > 0) ? u.data.data[0].attributes.url : u.data.data.attributes.url)
+ })
+ }
+ },
+ {
+ "icon": "./assets/feather/share.svg",
+ "name": `${app.getLz('action.share')} (song.link)`,
+ "action": function () {
+ app.mkapi(app.mk.nowPlayingItem.attributes?.playParams?.kind ?? app.mk.nowPlayingItem.type ?? 'songs', false, app.mk.nowPlayingItem._songId ?? (app.mk.nowPlayingItem.songId ?? app.mk.nowPlayingItem.id) ?? '').then(u => {
+ app.songLinkShare((u.data.data.length && u.data.data.length > 0) ? u.data.data[0].attributes.url : u.data.data.attributes.url)
+ })
+ }
+ }
+ ]
+ }
+ }
+ if (this.contextExt) {
+ if (this.contextExt.normal) {
+ menus.normal.items = menus.normal.items.concat(this.contextExt.normal)
+ }
+ }
+ this.showMenuPanel(menus[useMenu], event)
+
+ try {
+ let result = await this.inLibrary([this.mk.nowPlayingItem])
+ if (result[0].attributes.inLibrary) {
+ menus.normal.items.find(x => x.id == 'addToLibrary').hidden = true
+ menus.normal.items.find(x => x.id == 'removeFromLibrary').hidden = false
+ } else {
+ menus.normal.items.find(x => x.id == 'addToLibrary').disabled = false
+ }
+ } catch (e) {
+ e = null
+ }
+
+ try {
+ let rating = await app.getRating(app.mk.nowPlayingItem)
+ if (rating == 0) {
+ menus.normal.headerItems.find(x => x.id == 'love').disabled = false
+ menus.normal.headerItems.find(x => x.id == 'dislike').disabled = false
+ } else if (rating == 1) {
+ menus.normal.headerItems.find(x => x.id == 'unlove').hidden = false
+ menus.normal.headerItems.find(x => x.id == 'love').hidden = true
+ } else if (rating == -1) {
+ menus.normal.headerItems.find(x => x.id == 'undo_dislike').hidden = false
+ menus.normal.headerItems.find(x => x.id == 'dislike').hidden = true
+ }
+ } catch (err) {
+
+ }
+ },
+ LastFMDeauthorize() {
+ ipcRenderer.invoke('setStoreValue', 'lastfm.enabled', false).catch((e) => console.error(e));
+ ipcRenderer.invoke('setStoreValue', 'lastfm.auth_token', '').catch((e) => console.error(e));
+ app.cfg.lastfm.auth_token = "";
+ app.cfg.lastfm.enabled = false;
+ const element = document.getElementById('lfmConnect');
+ element.innerHTML = app.getLz('term.connect');
+ element.onclick = app.LastFMAuthenticate;
+ },
+ LastFMAuthenticate() {
+ console.log("[LastFM] Received LastFM authentication callback")
+ const element = document.getElementById('lfmConnect');
+ // new key : f9986d12aab5a0fe66193c559435ede3
+ window.open('https://www.last.fm/api/auth?api_key=f9986d12aab5a0fe66193c559435ede3&cb=cider://auth/lastfm');
+ element.innerText = app.getLz('term.connecting') + '...';
+
+ /* Just a timeout for the button */
+ setTimeout(() => {
+ if (element.innerText === app.getLz('term.connecting') + '...') {
+ element.innerText = app.getLz('term.connect');
+ console.warn('[LastFM] Attempted connection timed out.');
+ }
+ }, 20000);
+
+ ipcRenderer.on('LastfmAuthenticated', function (_event, lfmAuthKey) {
+ app.cfg.lastfm.auth_token = lfmAuthKey;
+ app.cfg.lastfm.enabled = true;
+ element.innerHTML = `${app.getLz('term.disconnect')}\n(${app.getLz('term.authed')}: ${lfmAuthKey})
`;
+ element.onclick = app.LastFMDeauthorize;
+ });
+ },
+ /**
+ parseSCTagToRG: function (tag) {
+ let soundcheck = tag.split(" ")
+ let numbers = []
+ for (item of soundcheck) {
+ numbers.push(parseInt(item, 16))
+
+ }
+ numbers.shift()
+ //let gain = Math.log10((Math.max(numbers[0], numbers[1]) ?? 1000) / 1000.0) * -10
+ let peak = Math.max(numbers[6], numbers[7]) / 32768.0
+ let gain = Math.pow(10, ((-7.63 - (Math.log10(peak) * 20)) / 20))// EBU R 128 Compliant
+ return {
+ gain: gain,
+ peak: peak
+ }
+ },*/
+ fullscreen(flag) {
+ if (flag) {
+ ipcRenderer.send('setFullScreen', true);
+ if (app.mk.nowPlayingItem.type && app.mk.nowPlayingItem.type.toLowerCase().includes("video")) {
+ document.querySelector('video#apple-music-video-player').requestFullscreen()
+ } else {
+ app.appMode = 'fullscreen';
+ }
+ document.addEventListener('keydown', event => {
+ if (event.key === 'Escape' && app.appMode === 'fullscreen') {
+ this.fullscreen(false);
+ }
+ });
+ } else {
+ ipcRenderer.send('setFullScreen', false);
+ app.appMode = 'player';
+ }
+ },
+ miniPlayer(flag) {
+ if (flag) {
+ this.tmpWidth = window.innerWidth;
+ this.tmpHeight = window.innerHeight;
+ ipcRenderer.send('unmaximize');
+ ipcRenderer.send('windowmin', 250, 250)
+ ipcRenderer.send('windowresize', 300, 300, false)
+ app.appMode = 'mini';
+ } else {
+ ipcRenderer.send('windowmin', 844, 410)
+ ipcRenderer.send('windowresize', this.tmpWidth, this.tmpHeight, false)
+ ipcRenderer.send('windowontop', false)
+ this.cfg.visual.miniplayer_top_toggle = true;
+ app.appMode = 'player';
+ }
+ },
+ pinMiniPlayer() {
+ if (this.cfg.visual.miniplayer_top_toggle) {
+ ipcRenderer.send('windowontop', true)
+ this.cfg.visual.miniplayer_top_toggle = false
+ } else {
+ ipcRenderer.send('windowontop', false)
+ this.cfg.visual.miniplayer_top_toggle = true;
+ }
+ },
+ formatTimezoneOffset: (e = new Date) => {
+ let leadingZeros = (e, s = 2) => {
+ let n = "" + e;
+ for (; n.length < s;)
+ n = "0" + n;
+ return n
+ }
+
+ const s = e.getTimezoneOffset(),
+ n = Math.floor(Math.abs(s) / 60),
+ d = Math.round(Math.abs(s) % 60);
+ let h = "+";
+ return 0 !== s && (h = s > 0 ? "-" : "+"),
+ `${h}${leadingZeros(n, 2)}:${leadingZeros(d, 2)}`
+ },
+ toggleHideUserInfo() {
+ if (this.chrome.hideUserInfo) {
+ this.cfg.visual.showuserinfo = true
+ this.chrome.hideUserInfo = false
+ } else {
+ this.cfg.visual.showuserinfo = false
+ this.chrome.hideUserInfo = true
+ }
+ },
+ isElementOverflowing(selector) {
+ try {
+ let element = document.querySelector(selector);
+ var overflowX = element.offsetWidth < element.scrollWidth,
+ overflowY = element.offsetHeight < element.scrollHeight;
+ element.setAttribute('data-value', '\xa0\xa0\xa0\xa0' + element.textContent);
+
+ return (overflowX || overflowY);
+ } catch (e) {
+ return false
+ }
+ },
+ async showWebRemoteQR() {
+ //this.webremoteqr = await ipcRenderer.invoke('setRemoteQR','')
+ this.webremoteurl = await ipcRenderer.invoke('showQR', '')
+ //this.modals.qrcode = true;
+ },
+ checkMarquee() {
+ if (isElementOverflowing('#app-main > div.app-chrome > div.app-chrome--center > div > div > div.playback-info > div.song-artist') == true) {
+ document.getElementsByClassName('song-artist')[0].classList.add('marquee');
+ document.getElementsByClassName('song-artist')[1].classList.add('marquee-after');
+ }
+ if (isElementOverflowing('#app-main > div.app-chrome > div.app-chrome--center > div > div > div.playback-info > div.song-name') == true) {
+ document.getElementsByClassName('song-name')[0].classList.add('marquee');
+ document.getElementsByClassName('song-name')[1].classList.add('marquee-after');
+ }
+ },
+ closeWindow() {
+ ipcRenderer.send('close');
+ },
+ darwinShare(url) {
+ ipcRenderer.send('share-menu', url)
+ },
+ arrayToChunk(arr, chunkSize) {
+ let R = [];
+ for (let i = 0, len = arr.length; i < len; i += chunkSize) {
+ R.push(arr.slice(i, i + chunkSize));
+ }
+ return R;
+ },
+ SpacePause() {
+ const elems = document.querySelectorAll('input');
+ for (let elem of elems) {
+ if (elem === document.activeElement) {
+ return;
+ }
+ }
+ if (!this.isDev) // disable in dev mode to keep my sanity
+ MusicKitInterop.playPause();
+ },
+ async MKJSLang() {
+ let u = this.cfg.general.language;
+ // use MusicKit.getInstance or crash
+ try {
+ let item = await MusicKit.getInstance().api.v3.music(`v1/storefronts/${app.mk.storefrontId}`)
+ let langcodes = item.data.data[0].attributes.supportedLanguageTags;
+ if (langcodes) langcodes = langcodes.map(function (u) {
+ return u.replace(/-Han[s|t]/i, "").toLowerCase()
+ })
+ console.log(langcodes)
+ let sellang = ""
+ if (u && langcodes.includes(u.toLowerCase().replace('_', "-"))) {
+ sellang = ((u.toLowerCase()).replace('_', "-"))
+ } else if (u && u.includes('_') && langcodes.includes(((u.toLowerCase()).replace('_', "-")).split("-")[0])) {
+ sellang = ((u.toLowerCase()).replace('_', "-")).split("-")[0]
+ }
+ if (sellang == "") sellang = (item.data.data[0].attributes.defaultLanguageTag).toLowerCase()
+
+ // Fix weird locales:
+ if (sellang == "iw") sellang = "iw-il"
+ sellang = sellang.replace(/-Han[s|t]/i, "").toLowerCase()
+
+ console.log(sellang)
+ return await sellang
+ } catch (err) {
+ console.log('locale err', err)
+ let langcodes = ['af', 'sq', 'ar', 'eu', 'bg', 'be', 'ca', 'zh', 'zh-tw', 'zh-cn', 'zh-hk', 'zh-sg', 'hr', 'cs', 'da', 'nl', 'nl-be', 'en', 'en-us', 'en-eg', 'en-au', 'en-gb', 'en-ca', 'en-nz', 'en-ie', 'en-za', 'en-jm', 'en-bz', 'en-tt', 'en-001', 'et', 'fo', 'fa', 'fi', 'fr', 'fr-ca', 'gd', 'de', 'de-ch', 'el', 'he', 'hi', 'hu', 'is', 'id', 'it', 'ja', 'ko', 'lv', 'lt', 'mk', 'mt', 'no', 'nb', 'nn', 'pl', 'pt-br', 'pt', 'rm', 'ro', 'ru', 'sr', 'sk', 'sl', 'es', 'es-mx', 'es-419', 'sv', 'th', 'ts', 'tn', 'tr', 'uk', 'ur', 've', 'vi', 'xh', 'yi', 'zu', 'ms', 'iw', 'lo', 'tl', 'kk', 'ta', 'te', 'bn', 'ga', 'ht', 'la', 'pa', 'sa'];
+ let sellang = "en"
+ if (u && langcodes.includes(u.toLowerCase().replace('_', "-"))) {
+ sellang = ((u.toLowerCase()).replace('_', "-"))
+ } else if (u && u.includes('_') && langcodes.includes(((u.toLowerCase()).replace('_', "-")).split("-")[0])) {
+ sellang = ((u.toLowerCase()).replace('_', "-")).split("-")[0]
+ }
+ if (sellang.startsWith("en") && this.mk.storefrontId != "us") sellang = "en-gb"
+ return await sellang
+ }
+ },
+ skipToNextItem() {
+ app.prevButtonBackIndicator = false;
+ // app.mk.skipToNextItem() is buggy somehow so use this
+ if (this.mk.queue.nextPlayableItemIndex != -1 && this.mk.queue.nextPlayableItemIndex != null)
+ this.mk.changeToMediaAtIndex(this.mk.queue.nextPlayableItemIndex);
+ },
+ skipToPreviousItem() {
+ // app.mk.skipToPreviousItem() is buggy somehow so use this
+ if (this.mk.queue.previousPlayableItemIndex != -1 && this.mk.queue.previousPlayableItemIndex != null)
+ this.mk.changeToMediaAtIndex(this.mk.queue.previousPlayableItemIndex);
+ },
+ mediaKeyFixes() {
+ navigator.mediaSession.setActionHandler('previoustrack', function () {
+ app.prevButton()
+ });
+ navigator.mediaSession.setActionHandler('nexttrack', function () {
+ app.skipToNextItem()
+ });
+ },
+ checkForUpdate() {
+ ipcRenderer.send('check-for-update')
+ ipcRenderer.on('update-response', (event, res) => {
+ if (res === "update-not-available") {
+ notyf.error(app.getLz(`settings.notyf.updateCider.${res}`))
+ } else if (res === "update-downloaded") {
+ notyf.success(app.getLz(`settings.notyf.updateCider.${res}`))
+ } else if (res === "update-error") {
+ notyf.error(app.getLz(`settings.notyf.updateCider.${res}`))
+ } else if (res === "update-timeout") {
+ notyf.error(app.getLz(`settings.notyf.updateCider.${res}`))
+ }
+
+ })
+ },
+ }
+})
+
+
+export { app }
\ No newline at end of file
diff --git a/src/renderer/main/vuex-store.js b/src/renderer/main/vuex-store.js
new file mode 100644
index 00000000..fb2b774f
--- /dev/null
+++ b/src/renderer/main/vuex-store.js
@@ -0,0 +1,20 @@
+const store = new Vuex.Store({
+ state: {
+ library: {
+ // songs: ipcRenderer.sendSync("get-library-songs"),
+ // albums: ipcRenderer.sendSync("get-library-albums"),
+ // recentlyAdded: ipcRenderer.sendSync("get-library-recentlyAdded"),
+ // playlists: ipcRenderer.sendSync("get-library-playlists")
+ },
+ artwork: {
+ playerLCD: ""
+ }
+ },
+ mutations: {
+ setLCDArtwork(state, artwork) {
+ state.artwork.playerLCD = artwork
+ }
+ }
+})
+
+export {store}
\ No newline at end of file
diff --git a/src/renderer/js/WSAPI_Interop.js b/src/renderer/main/wsapi_interop.js
similarity index 92%
rename from src/renderer/js/WSAPI_Interop.js
rename to src/renderer/main/wsapi_interop.js
index 16002fae..563b2e30 100644
--- a/src/renderer/js/WSAPI_Interop.js
+++ b/src/renderer/main/wsapi_interop.js
@@ -49,13 +49,13 @@ const wsapi = {
},
musickitApi(method, id, params, library = false) {
if (library) {
- MusicKit.getInstance().api.library[method](id, params).then((results)=>{
- ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method)
- })
+ MusicKit.getInstance().api.library[method](id, params).then((results)=>{
+ ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method)
+ })
} else {
- MusicKit.getInstance().api[method](id, params).then((results)=>{
- ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method)
- })
+ MusicKit.getInstance().api[method](id, params).then((results)=>{
+ ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method)
+ })
}
},
getPlaybackState () {
@@ -111,4 +111,6 @@ const wsapi = {
getmaxVolume() {
ipcRenderer.send('wsapi-returnvolumeMax',JSON.stringify(app.cfg.audio.maxVolume));
}
-}
\ No newline at end of file
+}
+
+export {wsapi}
\ No newline at end of file
diff --git a/src/renderer/views/main.ejs b/src/renderer/views/main.ejs
index 5d18d61a..0f230cb4 100644
--- a/src/renderer/views/main.ejs
+++ b/src/renderer/views/main.ejs
@@ -17,24 +17,24 @@
Cider
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
- <%- include("../assets/cider-round.svg") %>
-
-
-
-
- <%- include('app/chrome-top'); %>
- <%- include('app/app-navigation'); %>
- <%- include('app/chrome-bottom'); %>
-
-
-
-
-
-
-
-
-
-
-
-
- <%- include('app/panels'); %>
-
-
+
+ <%- include("../assets/cider-round.svg") %>
+
+
+
+
+ <%- include('app/chrome-top'); %>
+ <%- include('app/app-navigation'); %>
+ <%- include('app/chrome-bottom'); %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <%- include('app/panels'); %>
+
+
-<% for(var i=0; i < Object.keys(env.components).length ; i++) {%>
- <%- include(env.components[i]); %>
-<% } %>
+ <% for(var i=0; i < Object.keys(env.components).length ; i++) {%>
+ <%- include(env.components[i]); %>
+ <% } %>
-
-
-
+
+
-
-
-
\ No newline at end of file
diff --git a/src/renderer/views/components/mediaitem-square.ejs b/src/renderer/views/components/mediaitem-square.ejs
index 3d8724cb..7fcc7dfe 100644
--- a/src/renderer/views/components/mediaitem-square.ejs
+++ b/src/renderer/views/components/mediaitem-square.ejs
@@ -36,7 +36,7 @@
-
{{ item.attributes.name }}
+
{{ item.attributes?.name ?? (item.relationships?.contents?.data[0]?.attributes?.name ?? '') }}
Date: Sat, 5 Mar 2022 21:20:39 +0700
Subject: [PATCH 35/37] fix miniplayer on top, add blur toggle on windows
---
package.json | 3 ++-
src/i18n/en_US.json | 3 +++
src/main/base/browserwindow.ts | 16 ++++++++++++++--
src/main/base/store.ts | 1 +
src/renderer/main/vueapp.js | 13 ++++++++-----
.../views/components/mediaitem-square.ejs | 2 +-
src/renderer/views/components/miniplayer.ejs | 3 +++
src/renderer/views/pages/settings.ejs | 16 ++++++++++++++++
8 files changed, 48 insertions(+), 9 deletions(-)
diff --git a/package.json b/package.json
index be0b3b75..89aeaf73 100644
--- a/package.json
+++ b/package.json
@@ -48,6 +48,7 @@
"electron-notarize": "^1.1.1",
"electron-store": "^8.0.1",
"electron-updater": "^4.6.5",
+ "electron-vibrancy-updated": "git+https://github.com/ciderapp/electron-vibrancy-updated",
"electron-window-state": "^5.0.3",
"express": "^4.17.3",
"get-port": "^5.1.1",
@@ -75,7 +76,7 @@
"@types/express": "^4.17.13",
"@types/qrcode-terminal": "^0.12.0",
"@types/ws": "^8.5.1",
- "electron": "git+https://github.com/castlabs/electron-releases.git",
+ "electron": "git+https://github.com/castlabs/electron-releases.git#16-x-y",
"electron-builder": "^22.14.13",
"electron-builder-notarize-pkg": "^1.2.0",
"electron-webpack": "^2.8.2",
diff --git a/src/i18n/en_US.json b/src/i18n/en_US.json
index 7e402929..d224beb8 100644
--- a/src/i18n/en_US.json
+++ b/src/i18n/en_US.json
@@ -201,6 +201,7 @@
"action.unfollow": "Unfollow",
"action.unfollow.success": "Unfollowed",
"action.unfollow.error": "Error Unfollowing",
+ "action.relaunch.confirm": "Do you want to relaunch Cider?",
"action.playNext": "Play Next",
"action.playLater": "Play Later",
"action.startRadio": "Start Radio",
@@ -315,6 +316,8 @@
"settings.option.visual.hardwareAcceleration.description": "Requires relaunch",
"settings.header.visual.hardwareAcceleration.default": "Default",
"settings.header.visual.hardwareAcceleration.webGPU": "WebGPU",
+ "settings.option.visual.transparent": "Transparent frame",
+ "settings.option.visual.transparent.description": "Transparent frame (needs Theme Support , requires relaunch)",
"settings.header.visual.theme": "Theme",
"settings.option.visual.theme.github.download": "Install from GitHub URL",
"settings.option.visual.theme.github.explore": "Explore GitHub Themes",
diff --git a/src/main/base/browserwindow.ts b/src/main/base/browserwindow.ts
index 316a0e01..82dc46dd 100644
--- a/src/main/base/browserwindow.ts
+++ b/src/main/base/browserwindow.ts
@@ -301,8 +301,9 @@ export class BrowserWindow {
break;
case "win32":
- this.options.backgroundColor = "#1E1E1E";
- this.options.transparent = false;
+ if (!(utils.getStoreValue('visual.transparent') ?? false)){
+ this.options.backgroundColor = "#1E1E1E";} else {
+ this.options.transparent = true;}
break;
case "linux":
this.options.backgroundColor = "#1E1E1E";
@@ -321,6 +322,11 @@ export class BrowserWindow {
this.startWebServer();
BrowserWindow.win = new bw(this.options);
+ if (process.platform === "win32" && (utils.getStoreValue('visual.transparent') ?? false)) {
+ var electronVibrancy = require('electron-vibrancy-updated');
+ electronVibrancy.SetVibrancy(BrowserWindow.win, 0);
+
+ }
const ws = new wsapi(BrowserWindow.win)
ws.InitWebSockets()
// and load the renderer.
@@ -797,9 +803,15 @@ export class BrowserWindow {
ipcMain.on('setFullScreen', (_event, flag) => {
BrowserWindow.win.setFullScreen(flag)
})
+
//Fullscreen
ipcMain.on('detachDT', (_event, _) => {
BrowserWindow.win.webContents.openDevTools({mode: 'detach'});
+ })
+
+ ipcMain.on('relaunchApp',(_event, _) => {
+ app.relaunch()
+ app.exit()
})
diff --git a/src/main/base/store.ts b/src/main/base/store.ts
index 0a77de97..f1ea8dc2 100644
--- a/src/main/base/store.ts
+++ b/src/main/base/store.ts
@@ -105,6 +105,7 @@ export class Store {
"bg_artwork_rotation": false,
"hw_acceleration": "default", // default, webgpu, disabled
"showuserinfo": true,
+ "transparent": false,
"miniplayer_top_toggle": true,
"directives": {
"windowLayout": "default"
diff --git a/src/renderer/main/vueapp.js b/src/renderer/main/vueapp.js
index c2d2e163..f4ca97b2 100644
--- a/src/renderer/main/vueapp.js
+++ b/src/renderer/main/vueapp.js
@@ -3863,17 +3863,20 @@ const app = new Vue({
ipcRenderer.send('windowmin', 844, 410)
ipcRenderer.send('windowresize', this.tmpWidth, this.tmpHeight, false)
ipcRenderer.send('windowontop', false)
- this.cfg.visual.miniplayer_top_toggle = true;
+ //this.cfg.visual.miniplayer_top_toggle = true;
app.appMode = 'player';
}
},
- pinMiniPlayer() {
- if (this.cfg.visual.miniplayer_top_toggle) {
+ pinMiniPlayer(status = false) {
+ if (!status){
+ if (!this.cfg.visual.miniplayer_top_toggle) {
ipcRenderer.send('windowontop', true)
- this.cfg.visual.miniplayer_top_toggle = false
+ this.cfg.visual.miniplayer_top_toggle = true;
} else {
ipcRenderer.send('windowontop', false)
- this.cfg.visual.miniplayer_top_toggle = true;
+ this.cfg.visual.miniplayer_top_toggle = false;
+ }} else {
+ ipcRenderer.send('windowontop', this.cfg.visual.miniplayer_top_toggle ?? false)
}
},
formatTimezoneOffset: (e = new Date) => {
diff --git a/src/renderer/views/components/mediaitem-square.ejs b/src/renderer/views/components/mediaitem-square.ejs
index 7fcc7dfe..296d1780 100644
--- a/src/renderer/views/components/mediaitem-square.ejs
+++ b/src/renderer/views/components/mediaitem-square.ejs
@@ -76,7 +76,7 @@
isVisible: false,
addedToLibrary: false,
guid: this.uuidv4(),
- noplay: ["apple-curators"],
+ noplay: ["apple-curators", "editorial-elements"],
nomenu: ["artists", "stations", "apple-curators", "editorial-elements"],
app: this.$root,
badges: this.$root.socialBadges.badgeMap,
diff --git a/src/renderer/views/components/miniplayer.ejs b/src/renderer/views/components/miniplayer.ejs
index 6ee98ae4..ca961cac 100644
--- a/src/renderer/views/components/miniplayer.ejs
+++ b/src/renderer/views/components/miniplayer.ejs
@@ -155,6 +155,9 @@
beforeDestroy() {
window.removeEventListener('keyup', this.onEscapeKeyUp)
},
+ mounted() {
+ app.pinMiniPlayer(true)
+ },
methods: {
onEscapeKeyUp(event) {
if (event.which === 27) {
diff --git a/src/renderer/views/pages/settings.ejs b/src/renderer/views/pages/settings.ejs
index f8b3d5f2..67be8cb1 100644
--- a/src/renderer/views/pages/settings.ejs
+++ b/src/renderer/views/pages/settings.ejs
@@ -752,6 +752,15 @@
+
+
+ {{$root.getLz('settings.option.visual.transparent')}}
+ ({{$root.getLz('settings.option.visual.transparent.description')}})
+
+
+
+
+