This commit is contained in:
N0chteil 2022-01-07 17:40:12 +01:00
parent 1f507900b5
commit fc87a2fd6e
39 changed files with 4500 additions and 3275 deletions

View file

@ -18,12 +18,14 @@
</p>
#### Links
* [Wiki](https://github.com/ciderapp/Cider/wiki)
* [Request Feature](https://github.com/ciderapp/Cider/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=%5BEnhancement%5D)
* [Report Bug](https://github.com/ciderapp/Cider/issues/new?assignees=&labels=bug&template=bug_report.md&title=%5BBUG%5D+)
* [**View The Releases**](https://github.com/ciderapp/Cider/releases/latest)
- [Wiki](https://github.com/ciderapp/Cider/wiki)
- [Request Feature](https://github.com/ciderapp/Cider/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=%5BEnhancement%5D)
- [Report Bug](https://github.com/ciderapp/Cider/issues/new?assignees=&labels=bug&template=bug_report.md&title=%5BBUG%5D+)
- [**View The Releases**](https://github.com/ciderapp/Cider/releases/latest)
### Install Sources
[![Get it from Github](https://img.shields.io/badge/Get_It_From_GitHub-100000?style=for-the-badge&logo=github&logoColor=white)](https://github.com/ciderapp/cider/releases/latest)
[![Get it from the Microsoft Store](https://img.shields.io/badge/Get_It_From_The_Microsoft_Store-100000?style=for-the-badge&logo=microsoft)](https://www.microsoft.com/store/apps/9P21XJ9D9G66)
@ -34,17 +36,21 @@
[![Get it from the Snap Store](https://img.shields.io/badge/Get_It_From_The_Snap_Store-100000?style=for-the-badge&logo=snapcraft)](https://snapcraft.io/apple-music-electron)
-->
[![Get it from the AUR](https://img.shields.io/badge/Get_It_From_The_AUR-100000?style=for-the-badge&logo=archlinux)](https://aur.archlinux.org/packages/cider)
### Compiling and Configuration
For more information surrounding configuration, compiling and other developer documentation, see the [compilation docs](https://cider.sh/docs/compile).
### Credits
![Contributors](https://contrib.rocks/image?repo=ciderapp/Cider)
### Disclaimer
*This project is NOT affiliated with Apple in any way shape or form. The project is open source and free to use (with an Apple Music subscription)
for any legal concerns contact me at <a href="mailto:cryptofyre@cryptofyre.org">cryptofyre@cryptofyre.org</a>.*
_This project is NOT affiliated with Apple in any way shape or form. The project is open source and free to use (with an Apple Music subscription)
for any legal concerns contact me at <a href="mailto:cryptofyre@cryptofyre.org">cryptofyre@cryptofyre.org</a>._
<p align="center">
<br>
@ -53,4 +59,4 @@ for any legal concerns contact me at <a href="mailto:cryptofyre@cryptofyre.org">
<br>
<img href="https://www.jetbrains.com/" width="120px" height="125px" src="https://logonoid.com/images/jetbrains-logo.png" alt="JetBrains">
<img href="https://www.macstadium.com/" width="300px" src="https://user-images.githubusercontent.com/33162551/124784795-df5d4c80-df0b-11eb-99a7-dc2b1cfb81bd.png" alt="MacStadium">
</p>
</p>

View file

@ -1,9 +1,9 @@
const { BrowserWindow, ipcMain, shell, app, screen } = require("electron")
const { join } = require("path")
const getPort = require("get-port");
const express = require("express");
const path = require("path");
const windowStateKeeper = require("electron-window-state");
const { BrowserWindow, ipcMain, shell, app, screen } = require('electron');
const { join } = require('path');
const getPort = require('get-port');
const express = require('express');
const path = require('path');
const windowStateKeeper = require('electron-window-state');
const os = require('os');
const yt = require('youtube-search-without-api-key');
const discord = require('./discordrpc');
@ -16,8 +16,10 @@ const fetch = require('electron-fetch').default;
const { Stream } = require('stream');
// Analytics for debugging.
const ElectronSentry = require("@sentry/electron");
ElectronSentry.init({ dsn: "https://68c422bfaaf44dea880b86aad5a820d2@o954055.ingest.sentry.io/6112214" });
const ElectronSentry = require('@sentry/electron');
ElectronSentry.init({
dsn: 'https://68c422bfaaf44dea880b86aad5a820d2@o954055.ingest.sentry.io/6112214'
});
const CiderBase = {
win: null,
@ -25,18 +27,18 @@ const CiderBase = {
audiostream: new Stream.PassThrough(),
async Start() {
this.clientPort = await getPort({ port: 9000 });
this.win = this.CreateBrowserWindow()
this.win = this.CreateBrowserWindow();
},
clientPort: 0,
CreateBrowserWindow() {
this.VerifyFiles()
this.VerifyFiles();
// Set default window sizes
const mainWindowState = windowStateKeeper({
defaultWidth: 1024,
defaultHeight: 600
});
let win = null
let win = null;
const options = {
icon: join(__dirname, `../../resources/icons/icon.ico`),
width: mainWindowState.width,
@ -46,7 +48,7 @@ const CiderBase = {
minWidth: 844,
minHeight: 410,
frame: false,
title: "Cider",
title: 'Cider',
vibrancy: 'dark',
// transparent: true,
hasShadow: false,
@ -63,135 +65,172 @@ const CiderBase = {
contextIsolation: false,
preload: join(__dirname, '../preload/cider-preload.js')
}
}
};
CiderBase.InitWebServer()
CiderBase.InitWebServer();
// Create the BrowserWindow
if (process.platform === "darwin" || process.platform === "linux") {
win = new BrowserWindow(options)
if (process.platform === 'darwin' || process.platform === 'linux') {
win = new BrowserWindow(options);
} else {
// i don't know why but we have to do this for acrylic to work properly
if (app.cfg.get("visual.window_transparency") !== "disabled") {
const { BrowserWindow } = require("electron-acrylic-window");
win = new BrowserWindow(options)
win.setVibrancy("dark")
if (app.cfg.get('visual.window_transparency') !== 'disabled') {
const { BrowserWindow } = require('electron-acrylic-window');
win = new BrowserWindow(options);
win.setVibrancy('dark');
} else {
win = new BrowserWindow(options)
win.setVibrancy("dark")
win = new BrowserWindow(options);
win.setVibrancy('dark');
}
}
// intercept "https://js-cdn.music.apple.com/hls.js/2.141.0/hls.js/hls.js" and redirect to local file "./apple-hls.js" instead
win.webContents.session.webRequest.onBeforeRequest(
{
urls: ["https://*/*.js"]
urls: ['https://*/*.js']
},
(details, callback) => {
if (details.url.includes("hls.js")) {
if (details.url.includes('hls.js')) {
callback({
redirectURL: `http://localhost:${CiderBase.clientPort}/apple-hls.js`
})
});
} else {
callback({
cancel: false
})
});
}
}
)
);
win.webContents.session.webRequest.onBeforeSendHeaders(async (details, callback) => {
if (details.url === "https://buy.itunes.apple.com/account/web/info") {
details.requestHeaders['sec-fetch-site'] = 'same-site';
details.requestHeaders['DNT'] = '1';
let itspod = await win.webContents.executeJavaScript(`window.localStorage.getItem("music.ampwebplay.itspod")`)
if (itspod != null)
details.requestHeaders['Cookie'] = `itspod=${itspod}`
win.webContents.session.webRequest.onBeforeSendHeaders(
async (details, callback) => {
if (
details.url ===
'https://buy.itunes.apple.com/account/web/info'
) {
details.requestHeaders['sec-fetch-site'] = 'same-site';
details.requestHeaders['DNT'] = '1';
let itspod = await win.webContents.executeJavaScript(
`window.localStorage.getItem("music.ampwebplay.itspod")`
);
if (itspod != null)
details.requestHeaders['Cookie'] = `itspod=${itspod}`;
}
callback({ requestHeaders: details.requestHeaders });
}
callback({ requestHeaders: details.requestHeaders })
})
);
let location = `http://localhost:${CiderBase.clientPort}/`
win.loadURL(location)
win.on("closed", () => {
win = null
})
let location = `http://localhost:${CiderBase.clientPort}/`;
win.loadURL(location);
win.on('closed', () => {
win = null;
});
// Register listeners on Window to track size and position of the Window.
mainWindowState.manage(win);
// IPC stuff (senders)
ipcMain.on("cider-platform", (event) => {
event.returnValue = process.platform
})
ipcMain.on('cider-platform', (event) => {
event.returnValue = process.platform;
});
ipcMain.on("get-gpu-mode", (event) => {
event.returnValue = process.platform
})
ipcMain.on('get-gpu-mode', (event) => {
event.returnValue = process.platform;
});
ipcMain.on("is-dev", (event) => {
event.returnValue = !app.isPackaged
})
ipcMain.on('is-dev', (event) => {
event.returnValue = !app.isPackaged;
});
// IPC stuff (listeners)
ipcMain.on('close', () => { // listen for close event
ipcMain.on('close', () => {
// listen for close event
win.close();
})
});
ipcMain.on('put-library-songs', (event, arg) => {
fs.writeFileSync(join(app.paths.ciderCache, "library-songs.json"), JSON.stringify(arg))
})
fs.writeFileSync(
join(app.paths.ciderCache, 'library-songs.json'),
JSON.stringify(arg)
);
});
ipcMain.on('put-library-artists', (event, arg) => {
fs.writeFileSync(join(app.paths.ciderCache, "library-artists.json"), JSON.stringify(arg))
})
fs.writeFileSync(
join(app.paths.ciderCache, 'library-artists.json'),
JSON.stringify(arg)
);
});
ipcMain.on('put-library-albums', (event, arg) => {
fs.writeFileSync(join(app.paths.ciderCache, "library-albums.json"), JSON.stringify(arg))
})
fs.writeFileSync(
join(app.paths.ciderCache, 'library-albums.json'),
JSON.stringify(arg)
);
});
ipcMain.on('put-library-playlists', (event, arg) => {
fs.writeFileSync(join(app.paths.ciderCache, "library-playlists.json"), JSON.stringify(arg))
})
fs.writeFileSync(
join(app.paths.ciderCache, 'library-playlists.json'),
JSON.stringify(arg)
);
});
ipcMain.on('put-library-recentlyAdded', (event, arg) => {
fs.writeFileSync(join(app.paths.ciderCache, "library-recentlyAdded.json"), JSON.stringify(arg))
})
fs.writeFileSync(
join(app.paths.ciderCache, 'library-recentlyAdded.json'),
JSON.stringify(arg)
);
});
ipcMain.on('get-library-songs', (event) => {
let librarySongs = fs.readFileSync(join(app.paths.ciderCache, "library-songs.json"), "utf8")
event.returnValue = JSON.parse(librarySongs)
})
let librarySongs = fs.readFileSync(
join(app.paths.ciderCache, 'library-songs.json'),
'utf8'
);
event.returnValue = JSON.parse(librarySongs);
});
ipcMain.on('get-library-artists', (event) => {
let libraryArtists = fs.readFileSync(join(app.paths.ciderCache, "library-artists.json"), "utf8")
event.returnValue = JSON.parse(libraryArtists)
})
let libraryArtists = fs.readFileSync(
join(app.paths.ciderCache, 'library-artists.json'),
'utf8'
);
event.returnValue = JSON.parse(libraryArtists);
});
ipcMain.on('get-library-albums', (event) => {
let libraryAlbums = fs.readFileSync(join(app.paths.ciderCache, "library-albums.json"), "utf8")
event.returnValue = JSON.parse(libraryAlbums)
})
let libraryAlbums = fs.readFileSync(
join(app.paths.ciderCache, 'library-albums.json'),
'utf8'
);
event.returnValue = JSON.parse(libraryAlbums);
});
ipcMain.on('get-library-playlists', (event) => {
let libraryPlaylists = fs.readFileSync(join(app.paths.ciderCache, "library-playlists.json"), "utf8")
event.returnValue = JSON.parse(libraryPlaylists)
})
let libraryPlaylists = fs.readFileSync(
join(app.paths.ciderCache, 'library-playlists.json'),
'utf8'
);
event.returnValue = JSON.parse(libraryPlaylists);
});
ipcMain.on('get-library-recentlyAdded', (event) => {
let libraryRecentlyAdded = fs.readFileSync(join(app.paths.ciderCache, "library-recentlyAdded.json"), "utf8")
event.returnValue = JSON.parse(libraryRecentlyAdded)
})
let libraryRecentlyAdded = fs.readFileSync(
join(app.paths.ciderCache, 'library-recentlyAdded.json'),
'utf8'
);
event.returnValue = JSON.parse(libraryRecentlyAdded);
});
ipcMain.handle('getYTLyrics', async (event, track, artist) => {
var u = track + " " + artist + " official video";
var u = track + ' ' + artist + ' official video';
const videos = await yt.search(u);
return videos
})
return videos;
});
ipcMain.handle('getStoreValue', (event, key, defaultValue) => {
return (defaultValue ? app.cfg.get(key, true) : app.cfg.get(key));
return defaultValue ? app.cfg.get(key, true) : app.cfg.get(key);
});
ipcMain.handle('setStoreValue', (event, key, value) => {
@ -199,181 +238,209 @@ const CiderBase = {
});
ipcMain.on('getStore', (event) => {
event.returnValue = app.cfg.store
})
ipcMain.on('setStore', (event, store) => {
app.cfg.store = store
})
ipcMain.handle('setVibrancy', (event, key, value) => {
win.setVibrancy(value)
event.returnValue = app.cfg.store;
});
ipcMain.on('maximize', () => { // listen for maximize event
ipcMain.on('setStore', (event, store) => {
app.cfg.store = store;
});
ipcMain.handle('setVibrancy', (event, key, value) => {
win.setVibrancy(value);
});
ipcMain.on('maximize', () => {
// listen for maximize event
if (win.isMaximized()) {
win.unmaximize()
win.unmaximize();
} else {
win.maximize()
win.maximize();
}
})
});
ipcMain.on('minimize', () => { // listen for minimize event
ipcMain.on('minimize', () => {
// listen for minimize event
win.minimize();
})
});
if (process.platform === "win32") {
if (process.platform === 'win32') {
let WND_STATE = {
MINIMIZED: 0,
NORMAL: 1,
MAXIMIZED: 2,
FULL_SCREEN: 3
}
let wndState = WND_STATE.NORMAL
};
let wndState = WND_STATE.NORMAL;
win.on("resize", (_event) => {
const isMaximized = win.isMaximized()
const isMinimized = win.isMinimized()
const isFullScreen = win.isFullScreen()
win.on('resize', (_event) => {
const isMaximized = win.isMaximized();
const isMinimized = win.isMinimized();
const isFullScreen = win.isFullScreen();
const state = wndState;
if (isMinimized && state !== WND_STATE.MINIMIZED) {
wndState = WND_STATE.MINIMIZED
wndState = WND_STATE.MINIMIZED;
} else if (isFullScreen && state !== WND_STATE.FULL_SCREEN) {
wndState = WND_STATE.FULL_SCREEN
wndState = WND_STATE.FULL_SCREEN;
} else if (isMaximized && state !== WND_STATE.MAXIMIZED) {
wndState = WND_STATE.MAXIMIZED
win.webContents.executeJavaScript(`app.chrome.maximized = true`)
wndState = WND_STATE.MAXIMIZED;
win.webContents.executeJavaScript(
`app.chrome.maximized = true`
);
} else if (state !== WND_STATE.NORMAL) {
wndState = WND_STATE.NORMAL
win.webContents.executeJavaScript(`app.chrome.maximized = false`)
wndState = WND_STATE.NORMAL;
win.webContents.executeJavaScript(
`app.chrome.maximized = false`
);
}
})
});
}
// Set window Handler
win.webContents.setWindowOpenHandler(({ url }) => {
if (url.includes("apple") || url.includes("localhost")) {
return { action: "allow" }
if (url.includes('apple') || url.includes('localhost')) {
return { action: 'allow' };
}
shell.openExternal(url).catch(() => {
})
shell.openExternal(url).catch(() => {});
return {
action: 'deny'
}
})
};
});
// Set scale
ipcMain.on('setScreenScale', (event, scale) => {
win.webContents.setZoomFactor(parseFloat(scale))
})
win.webContents.setZoomFactor(parseFloat(scale));
});
win.webContents.setZoomFactor(screen.getPrimaryDisplay().scaleFactor)
win.webContents.setZoomFactor(screen.getPrimaryDisplay().scaleFactor);
mpris.connect(win)
mpris.connect(win);
lastfm.authenticate()
lastfm.authenticate();
// Discord
discord.connect((app.cfg.get("general.discord_rpc") == 1) ? '911790844204437504' : '886578863147192350');
discord.connect(
app.cfg.get('general.discord_rpc') == 1
? '911790844204437504'
: '886578863147192350'
);
ipcMain.on('playbackStateDidChange', (_event, a) => {
app.media = a;
discord.updateActivity(a)
mpris.updateState(a)
lastfm.scrobbleSong(a)
lastfm.updateNowPlayingSong(a)
discord.updateActivity(a);
mpris.updateState(a);
lastfm.scrobbleSong(a);
lastfm.updateNowPlayingSong(a);
});
ipcMain.on('nowPlayingItemDidChange', (_event, a) => {
app.media = a;
discord.updateActivity(a)
mpris.updateAttributes(a)
lastfm.scrobbleSong(a)
lastfm.updateNowPlayingSong(a)
discord.updateActivity(a);
mpris.updateAttributes(a);
lastfm.scrobbleSong(a);
lastfm.updateNowPlayingSong(a);
});
ipcMain.on("getPreviewURL", (_event, url) => {
ipcMain.on('getPreviewURL', (_event, url) => {
fetch(url)
.then(res => res.buffer())
.then((res) => res.buffer())
.then(async (buffer) => {
try {
const metadata = await mm.parseBuffer(buffer, 'audio/x-m4a');
SoundCheckTag = metadata.native.iTunes[1].value
win.webContents.send('SoundCheckTag', SoundCheckTag)
const metadata = await mm.parseBuffer(
buffer,
'audio/x-m4a'
);
SoundCheckTag = metadata.native.iTunes[1].value;
win.webContents.send('SoundCheckTag', SoundCheckTag);
} catch (error) {
console.error(error.message);
}
})
});
});
ipcMain.on('writeAudio', function (event, buffer) {
CiderBase.audiostream.write(Buffer.from(buffer));
})
});
return win
return win;
},
VerifyFiles() {
const expectedDirectories = [
"CiderCache"
]
const expectedDirectories = ['CiderCache'];
const expectedFiles = [
"library-songs.json",
"library-artists.json",
"library-albums.json",
"library-playlists.json",
"library-recentlyAdded.json",
]
'library-songs.json',
'library-artists.json',
'library-albums.json',
'library-playlists.json',
'library-recentlyAdded.json'
];
for (let i = 0; i < expectedDirectories.length; i++) {
if (!existsSync(path.join(app.getPath("userData"), expectedDirectories[i]))) {
mkdirSync(path.join(app.getPath("userData"), expectedDirectories[i]))
if (
!existsSync(
path.join(app.getPath('userData'), expectedDirectories[i])
)
) {
mkdirSync(
path.join(app.getPath('userData'), expectedDirectories[i])
);
}
}
for (let i = 0; i < expectedFiles.length; i++) {
const file = path.join(app.paths.ciderCache, expectedFiles[i])
const file = path.join(app.paths.ciderCache, expectedFiles[i]);
if (!existsSync(file)) {
writeFileSync(file, JSON.stringify([]))
writeFileSync(file, JSON.stringify([]));
}
}
},
EnvironmentVariables: {
"env": {
env: {
platform: os.platform(),
dev: app.isPackaged
}
},
LinkHandler: (startArgs) => {
if (!startArgs) return;
console.log("lfmtoken", String(startArgs))
console.log('lfmtoken', String(startArgs));
if (String(startArgs).includes('auth')) {
let authURI = String(startArgs).split('/auth/')[1]
if (authURI.startsWith('lastfm')) { // If we wanted more auth options
let authURI = String(startArgs).split('/auth/')[1];
if (authURI.startsWith('lastfm')) {
// If we wanted more auth options
const authKey = authURI.split('lastfm?token=')[1];
app.cfg.set('lastfm.enabled', true);
app.cfg.set('lastfm.auth_token', authKey);
CiderBase.win.webContents.send('LastfmAuthenticated', authKey);
lastfm.authenticate()
lastfm.authenticate();
}
} else {
const formattedSongID = startArgs.replace('ame://', '').replace('/', '');
console.warn(`[LinkHandler] Attempting to load song id: ${formattedSongID}`);
const formattedSongID = startArgs
.replace('ame://', '')
.replace('/', '');
console.warn(
`[LinkHandler] Attempting to load song id: ${formattedSongID}`
);
// setQueue can be done with album, song, url, playlist id
this.win.webContents.executeJavaScript(`
this.win.webContents
.executeJavaScript(
`
MusicKit.getInstance().setQueue({ song: '${formattedSongID}'}).then(function(queue) {
MusicKit.getInstance().play();
});
`).catch((err) => console.error(err));
`
)
.catch((err) => console.error(err));
}
},
async InitWebServer() {
const webapp = express();
const webRemotePath = path.join(__dirname, '../renderer/');
webapp.set("views", path.join(webRemotePath, "views"));
webapp.set("view engine", "ejs");
webapp.set('views', path.join(webRemotePath, 'views'));
webapp.set('view engine', 'ejs');
webapp.use(function (req, res, next) {
// if not localhost
if (req.url.includes("audio.webm") || (req.headers.host.includes("localhost") && req.headers["user-agent"].includes("Cider"))) {
if (
req.url.includes('audio.webm') ||
(req.headers.host.includes('localhost') &&
req.headers['user-agent'].includes('Cider'))
) {
next();
}
});
@ -381,7 +448,7 @@ const CiderBase = {
webapp.use(express.static(webRemotePath));
webapp.get('/', function (req, res) {
//res.sendFile(path.join(webRemotePath, 'index_old.html'));
res.render("main", CiderBase.EnvironmentVariables)
res.render('main', CiderBase.EnvironmentVariables);
});
webapp.get('/audio.webm', function (req, res) {
try {
@ -397,16 +464,17 @@ const CiderBase = {
try {
res.write(data);
} catch (ex) {
console.log(ex)
console.log(ex);
}
})
} catch (ex) { console.log(ex) }
});
} catch (ex) {
console.log(ex);
}
});
webapp.listen(CiderBase.clientPort, function () {
console.log(`Cider client port: ${CiderBase.clientPort}`);
});
},
}
}
};
module.exports = CiderBase;

View file

@ -1,40 +1,48 @@
const {app} = require('electron'),
DiscordRPC = require('discord-rpc')
const { app } = require('electron'),
DiscordRPC = require('discord-rpc');
module.exports = {
/**
* Connects to Discord RPC
* @param {string} clientId
*/
connect: function (clientId) {
app.discord = {isConnected: false};
app.discord = { isConnected: false };
if (app.cfg.get('general.discord_rpc') == 0) return;
DiscordRPC.register(clientId) // Apparently needed for ask to join, join, spectate etc.
const client = new DiscordRPC.Client({transport: "ipc"});
app.discord = Object.assign(client, {error: false, activityCache: null, isConnected: false});
DiscordRPC.register(clientId); // Apparently needed for ask to join, join, spectate etc.
const client = new DiscordRPC.Client({ transport: 'ipc' });
app.discord = Object.assign(client, {
error: false,
activityCache: null,
isConnected: false
});
// Login to Discord
app.discord.login({clientId})
app.discord
.login({ clientId })
.then(() => {
app.discord.isConnected = true;
})
.catch((e) => console.error(`[DiscordRPC][connect] ${e}`));
app.discord.on('ready', () => {
console.log(`[DiscordRPC][connect] Successfully Connected to Discord. Authed for user: ${client.user.username} (${client.user.id})`);
console.log(
`[DiscordRPC][connect] Successfully Connected to Discord. Authed for user: ${client.user.username} (${client.user.id})`
);
if (app.discord.activityCache) {
client.setActivity(app.discord.activityCache).catch((e) => console.error(e));
client
.setActivity(app.discord.activityCache)
.catch((e) => console.error(e));
app.discord.activityCache = null;
}
})
});
// Handles Errors
app.discord.on('error', err => {
app.discord.on('error', (err) => {
console.error(`[DiscordRPC] ${err}`);
this.disconnect()
this.disconnect();
app.discord.isConnected = false;
});
},
@ -43,15 +51,21 @@ module.exports = {
* Disconnects from Discord RPC
*/
disconnect: function () {
if (app.cfg.get('general.discord_rpc') == 0 || !app.discord.isConnected) return;
if (app.cfg.get('general.discord_rpc') == 0 || !app.discord.isConnected)
return;
try {
app.discord.destroy().then(() => {
app.discord.isConnected = false;
console.log('[DiscordRPC][disconnect] Disconnected from discord.')
}).catch((e) => console.error(`[DiscordRPC][disconnect] ${e}`));
app.discord
.destroy()
.then(() => {
app.discord.isConnected = false;
console.log(
'[DiscordRPC][disconnect] Disconnected from discord.'
);
})
.catch((e) => console.error(`[DiscordRPC][disconnect] ${e}`));
} catch (err) {
console.error(err)
console.error(err);
}
},
@ -63,76 +77,87 @@ module.exports = {
if (app.cfg.get('general.discord_rpc') == 0) return;
if (!app.discord.isConnected) {
this.connect()
this.connect();
}
if (!app.discord.isConnected) return;
// console.log('[DiscordRPC][updateActivity] Updating Discord Activity.')
const listenURL = `https://applemusicelectron.com/p?id=${attributes.playParams.id}`
const listenURL = `https://applemusicelectron.com/p?id=${attributes.playParams.id}`;
//console.log(attributes)
let ActivityObject = {
details: attributes.name,
state: `by ${attributes.artistName}`,
startTimestamp: attributes.startTime,
endTimestamp: attributes.endTime,
largeImageKey: (attributes.artwork.url.replace('{w}', '1024').replace('{h}', '1024')) ?? 'cider',
largeImageKey:
attributes.artwork.url
.replace('{w}', '1024')
.replace('{h}', '1024') ?? 'cider',
largeImageText: attributes.albumName,
smallImageKey: (attributes.status ? 'play' : 'pause'),
smallImageText: (attributes.status ? 'Playing' : 'Paused'),
smallImageKey: attributes.status ? 'play' : 'pause',
smallImageText: attributes.status ? 'Playing' : 'Paused',
instance: true,
buttons: [
{label: "Listen on Cider", url: listenURL},
]
buttons: [{ label: 'Listen on Cider', url: listenURL }]
};
if (ActivityObject.largeImageKey == "" || ActivityObject.largeImageKey == null) {
ActivityObject.largeImageKey = (app.cfg.get("general.discord_rpc") == 1) ? "cider" : "logo"
if (
ActivityObject.largeImageKey == '' ||
ActivityObject.largeImageKey == null
) {
ActivityObject.largeImageKey =
app.cfg.get('general.discord_rpc') == 1 ? 'cider' : 'logo';
}
// console.log(`[LinkHandler] Listening URL has been set to: ${listenURL}`);
if (app.cfg.get('general.discordClearActivityOnPause') == 1) {
delete ActivityObject.smallImageKey
delete ActivityObject.smallImageText
if (app.cfg.get('general.discordClearActivityOnPause') == 1) {
delete ActivityObject.smallImageKey;
delete ActivityObject.smallImageText;
}
// Check all the values work
if (!((new Date(attributes.endTime)).getTime() > 0)) {
delete ActivityObject.startTimestamp
delete ActivityObject.endTimestamp
if (!(new Date(attributes.endTime).getTime() > 0)) {
delete ActivityObject.startTimestamp;
delete ActivityObject.endTimestamp;
}
if (!attributes.artistName) {
delete ActivityObject.state
delete ActivityObject.state;
}
if (!ActivityObject.largeImageText || ActivityObject.largeImageText.length < 2) {
delete ActivityObject.largeImageText
if (
!ActivityObject.largeImageText ||
ActivityObject.largeImageText.length < 2
) {
delete ActivityObject.largeImageText;
}
if (ActivityObject.details.length > 128) {
AcitivityObject.details = ActivityObject.details.substring(0, 125) + '...'
AcitivityObject.details =
ActivityObject.details.substring(0, 125) + '...';
}
// Clear if if needed
if (!attributes.status) {
if (app.cfg.get('general.discordClearActivityOnPause') == 1) {
app.discord.clearActivity().catch((e) => console.error(`[DiscordRPC][clearActivity] ${e}`));
ActivityObject = null
} else
{
delete ActivityObject.startTimestamp
delete ActivityObject.endTimestamp
ActivityObject.smallImageKey = 'pause'
ActivityObject.smallImageText = 'Paused'
app.discord
.clearActivity()
.catch((e) =>
console.error(`[DiscordRPC][clearActivity] ${e}`)
);
ActivityObject = null;
} else {
delete ActivityObject.startTimestamp;
delete ActivityObject.endTimestamp;
ActivityObject.smallImageKey = 'pause';
ActivityObject.smallImageText = 'Paused';
}
}
if (ActivityObject) {
try {
// console.log(`[DiscordRPC][setActivity] Setting activity to ${JSON.stringify(ActivityObject)}`);
app.discord.setActivity(ActivityObject)
// console.log(`[DiscordRPC][setActivity] Setting activity to ${JSON.stringify(ActivityObject)}`);
app.discord.setActivity(ActivityObject);
} catch (err) {
console.error(`[DiscordRPC][setActivity] ${err}`)
console.error(`[DiscordRPC][setActivity] ${err}`);
}
}
},
}
}
};

View file

@ -1,16 +1,18 @@
const {app, Notification} = require('electron'),
const { app, Notification } = require('electron'),
fs = require('fs'),
{resolve} = require('path'),
{ resolve } = require('path'),
sessionPath = resolve(app.getPath('userData'), 'session.json'),
apiCredentials = require('../../resources/lfmApiCredentials.json'),
LastfmAPI = require('lastfmapi');
const lfm = {
authenticateFromFile: function () {
let sessionData = require(sessionPath)
console.log("[LastFM][authenticateFromFile] Logging in with Session Info.")
app.lastfm.setSessionCredentials(sessionData.name, sessionData.key)
console.log("[LastFM][authenticateFromFile] Logged in.")
let sessionData = require(sessionPath);
console.log(
'[LastFM][authenticateFromFile] Logging in with Session Info.'
);
app.lastfm.setSessionCredentials(sessionData.name, sessionData.key);
console.log('[LastFM][authenticateFromFile] Logged in.');
},
authenticate: function () {
@ -18,84 +20,125 @@ const lfm = {
app.cfg.set('lastfm.enabled', true);
}
if (!app.cfg.get('lastfm.enabled') || !app.cfg.get('lastfm.auth_token')) {
if (
!app.cfg.get('lastfm.enabled') ||
!app.cfg.get('lastfm.auth_token')
) {
app.cfg.set('lastfm.enabled', false);
return
return;
}
const lfmAPI = new LastfmAPI({
'api_key': apiCredentials.key,
'secret': apiCredentials.secret
api_key: apiCredentials.key,
secret: apiCredentials.secret
});
app.lastfm = Object.assign(lfmAPI, {cachedAttributes: false, cachedNowPlayingAttributes: false});
app.lastfm = Object.assign(lfmAPI, {
cachedAttributes: false,
cachedNowPlayingAttributes: false
});
fs.stat(sessionPath, function (err) {
if (err) {
console.error("[LastFM][Session] Session file couldn't be opened or doesn't exist,", err)
console.log("[LastFM][Auth] Beginning authentication from configuration")
app.lastfm.authenticate(app.cfg.get('lastfm.auth_token'), function (err, session) {
if (err) {
throw err;
}
console.log("[LastFM] Successfully obtained LastFM session info,", session); // {"name": "LASTFM_USERNAME", "key": "THE_USER_SESSION_KEY"}
console.log("[LastFM] Saving session info to disk.")
let tempData = JSON.stringify(session)
fs.writeFile(sessionPath, tempData, (err) => {
if (err)
console.log("[LastFM][fs]", err)
else {
console.log("[LastFM][fs] File was written successfully.")
lfm.authenticateFromFile()
new Notification({
title: app.getName(),
body: "Successfully logged into LastFM using Authentication Key."
}).show()
console.error(
"[LastFM][Session] Session file couldn't be opened or doesn't exist,",
err
);
console.log(
'[LastFM][Auth] Beginning authentication from configuration'
);
app.lastfm.authenticate(
app.cfg.get('lastfm.auth_token'),
function (err, session) {
if (err) {
throw err;
}
})
});
console.log(
'[LastFM] Successfully obtained LastFM session info,',
session
); // {"name": "LASTFM_USERNAME", "key": "THE_USER_SESSION_KEY"}
console.log('[LastFM] Saving session info to disk.');
let tempData = JSON.stringify(session);
fs.writeFile(sessionPath, tempData, (err) => {
if (err) console.log('[LastFM][fs]', err);
else {
console.log(
'[LastFM][fs] File was written successfully.'
);
lfm.authenticateFromFile();
new Notification({
title: app.getName(),
body: 'Successfully logged into LastFM using Authentication Key.'
}).show();
}
});
}
);
} else {
lfm.authenticateFromFile()
lfm.authenticateFromFile();
}
})
});
},
scrobbleSong: async function (attributes) {
await new Promise(resolve => setTimeout(resolve, app.cfg.get('lastfm.scrobble_after') * 1000));
await new Promise((resolve) =>
setTimeout(resolve, app.cfg.get('lastfm.scrobble_after') * 1000)
);
const currentAttributes = app.media;
if (!app.lastfm || app.lastfm.cachedAttributes === attributes ) {
return
if (!app.lastfm || app.lastfm.cachedAttributes === attributes) {
return;
}
if (app.lastfm.cachedAttributes) {
if (app.lastfm.cachedAttributes.playParams.id === attributes.playParams.id) return;
if (
app.lastfm.cachedAttributes.playParams.id ===
attributes.playParams.id
)
return;
}
if (currentAttributes.status && currentAttributes === attributes) {
if (fs.existsSync(sessionPath)) {
// Scrobble playing song.
if (attributes.status === true) {
app.lastfm.track.scrobble({
'artist': lfm.filterArtistName(attributes.artistName),
'track': attributes.name,
'album': attributes.albumName,
'albumArtist': this.filterArtistName(attributes.artistName),
'timestamp': new Date().getTime() / 1000
}, function (err, scrobbled) {
if (err) {
return console.error('[LastFM] An error occurred while scrobbling', err);
}
app.lastfm.track.scrobble(
{
artist: lfm.filterArtistName(attributes.artistName),
track: attributes.name,
album: attributes.albumName,
albumArtist: this.filterArtistName(
attributes.artistName
),
timestamp: new Date().getTime() / 1000
},
function (err, scrobbled) {
if (err) {
return console.error(
'[LastFM] An error occurred while scrobbling',
err
);
}
console.log('[LastFM] Successfully scrobbled: ', scrobbled);
});
app.lastfm.cachedAttributes = attributes
console.log(
'[LastFM] Successfully scrobbled: ',
scrobbled
);
}
);
app.lastfm.cachedAttributes = attributes;
}
} else {
this.authenticate();
}
} else {
return console.log('[LastFM] Did not add ', attributes.name , '-' , lfm.filterArtistName(attributes.artistName), 'because now playing a other song.');
return console.log(
'[LastFM] Did not add ',
attributes.name,
'-',
lfm.filterArtistName(attributes.artistName),
'because now playing a other song.'
);
}
},
@ -111,43 +154,61 @@ const lfm = {
}
artist = artist.join(' ');
if (artist.includes(',')) {
artist = artist.split(',')
artist = artist[0]
artist = artist.split(',');
artist = artist[0];
}
return artist.charAt(0).toUpperCase() + artist.slice(1);
},
updateNowPlayingSong: function (attributes) {
if (!app.lastfm ||app.lastfm.cachedNowPlayingAttributes === attributes | !app.cfg.get('lastfm.NowPlaying')) {
return
if (
!app.lastfm ||
app.lastfm.cachedNowPlayingAttributes === attributes ||
!app.cfg.get('lastfm.NowPlaying')
) {
return;
}
if (app.lastfm.cachedNowPlayingAttributes) {
if (app.lastfm.cachedNowPlayingAttributes.playParams.id === attributes.playParams.id) return;
if (
app.lastfm.cachedNowPlayingAttributes.playParams.id ===
attributes.playParams.id
)
return;
}
if (fs.existsSync(sessionPath)) {
// update Now Playing
if (attributes.status === true) {
app.lastfm.track.updateNowPlaying({
'artist': lfm.filterArtistName(attributes.artistName),
'track': attributes.name,
'album': attributes.albumName,
'albumArtist': this.filterArtistName(attributes.artistName)
}, function (err, nowPlaying) {
if (err) {
return console.error('[LastFM] An error occurred while updating nowPlayingSong', err);
}
app.lastfm.track.updateNowPlaying(
{
artist: lfm.filterArtistName(attributes.artistName),
track: attributes.name,
album: attributes.albumName,
albumArtist: this.filterArtistName(
attributes.artistName
)
},
function (err, nowPlaying) {
if (err) {
return console.error(
'[LastFM] An error occurred while updating nowPlayingSong',
err
);
}
console.log('[LastFM] Successfully updated nowPlayingSong', nowPlaying);
});
app.lastfm.cachedNowPlayingAttributes = attributes
console.log(
'[LastFM] Successfully updated nowPlayingSong',
nowPlaying
);
}
);
app.lastfm.cachedNowPlayingAttributes = attributes;
}
} else {
this.authenticate()
this.authenticate();
}
}
}
};
module.exports = lfm;
module.exports = lfm;

View file

@ -1,13 +1,12 @@
let mediaPlayer = null;
module.exports = {
/**
* Connects to the MPRIS interface.
* @param {Object} win - The BrowserWindow.
*/
connect: (win) => {
if (process.platform !== "linux") return;
if (process.platform !== 'linux') return;
const Player = require('mpris-service');
@ -18,36 +17,51 @@ module.exports = {
supportedMimeTypes: [],
supportedInterfaces: ['player']
});
mediaPlayer = Object.assign(mediaPlayer, { canQuit: true, canControl: true, canPause: true, canPlay: true, canGoNext: true })
mediaPlayer = Object.assign(mediaPlayer, {
canQuit: true,
canControl: true,
canPause: true,
canPlay: true,
canGoNext: true
});
let pos_atr = {durationInMillis: 0};
let pos_atr = { durationInMillis: 0 };
mediaPlayer.getPosition = function () {
const durationInMicro = pos_atr.durationInMillis * 1000;
const percentage = parseFloat("0") || 0;
const percentage = parseFloat('0') || 0;
return durationInMicro * percentage;
}
};
mediaPlayer.active = true
mediaPlayer.active = true;
mediaPlayer.on('playpause', async () => {
win.webContents.executeJavaScript('MusicKitInterop.pausePlay()').catch(err => console.error(err))
win.webContents
.executeJavaScript('MusicKitInterop.pausePlay()')
.catch((err) => console.error(err));
});
mediaPlayer.on('play', async () => {
win.webContents.executeJavaScript('MusicKitInterop.pausePlay()').catch(err => console.error(err))
win.webContents
.executeJavaScript('MusicKitInterop.pausePlay()')
.catch((err) => console.error(err));
});
mediaPlayer.on('pause', async () => {
win.webContents.executeJavaScript('MusicKitInterop.pausePlay()').catch(err => console.error(err))
win.webContents
.executeJavaScript('MusicKitInterop.pausePlay()')
.catch((err) => console.error(err));
});
mediaPlayer.on('next', async () => {
win.webContents.executeJavaScript('MusicKitInterop.nextTrack()').catch(err => console.error(err))
win.webContents
.executeJavaScript('MusicKitInterop.nextTrack()')
.catch((err) => console.error(err));
});
mediaPlayer.on('previous', async () => {
win.webContents.executeJavaScript('MusicKitInterop.previousTrack()').catch(err => console.error(err))
win.webContents
.executeJavaScript('MusicKitInterop.previousTrack()')
.catch((err) => console.error(err));
});
},
@ -56,23 +70,29 @@ module.exports = {
* @param {Object} attributes - The attributes of the track.
*/
updateAttributes: (attributes) => {
if (process.platform !== "linux") return;
if (process.platform !== 'linux') return;
const MetaData = {
'mpris:trackid': mediaPlayer.objectPath(`track/${attributes.playParams.id.replace(/[.]+/g, "")}`),
'mpris:trackid': mediaPlayer.objectPath(
`track/${attributes.playParams.id.replace(/[.]+/g, '')}`
),
'mpris:length': attributes.durationInMillis * 1000, // In microseconds
'mpris:artUrl': (attributes.artwork.url.replace('/{w}x{h}bb', '/512x512bb')).replace('/2000x2000bb', '/35x35bb'),
'mpris:artUrl': attributes.artwork.url
.replace('/{w}x{h}bb', '/512x512bb')
.replace('/2000x2000bb', '/35x35bb'),
'xesam:title': `${attributes.name}`,
'xesam:album': `${attributes.albumName}`,
'xesam:artist': [`${attributes.artistName}`,],
'xesam:artist': [`${attributes.artistName}`],
'xesam:genre': attributes.genreNames
};
if (
mediaPlayer.metadata['mpris:trackid'] === MetaData['mpris:trackid']
) {
return;
}
if (mediaPlayer.metadata["mpris:trackid"] === MetaData["mpris:trackid"]) {
return
}
mediaPlayer.metadata = MetaData
mediaPlayer.metadata = MetaData;
},
/**
@ -80,11 +100,11 @@ module.exports = {
* @param {Object} attributes - The attributes of the track.
*/
updateState: (attributes) => {
if (process.platform !== "linux") return;
if (process.platform !== 'linux') return;
function setPlaybackIfNeeded(status) {
if (mediaPlayer.playbackStatus === status) {
return
return;
}
mediaPlayer.playbackStatus = status;
}
@ -96,7 +116,8 @@ module.exports = {
case false: // Paused
setPlaybackIfNeeded('Paused');
break;
default: // Stopped
default:
// Stopped
setPlaybackIfNeeded('Stopped');
break;
}
@ -106,8 +127,10 @@ module.exports = {
* Closes the MPRIS interface.
*/
clearActivity: () => {
if (process.platform !== "linux") return;
mediaPlayer.metadata = {'mpris:trackid': '/org/mpris/MediaPlayer2/TrackList/NoTrack'}
if (process.platform !== 'linux') return;
mediaPlayer.metadata = {
'mpris:trackid': '/org/mpris/MediaPlayer2/TrackList/NoTrack'
};
mediaPlayer.playbackStatus = 'Stopped';
},
}
}
};

View file

@ -1,72 +1,135 @@
const electron = require('electron')
const electron = require('electron');
console.log('Loaded Preload')
console.log('Loaded Preload');
let cache = {playParams: {id: 0}, status: null, remainingTime: 0},
playbackCache = {status: null, time: Date.now()};
let cache = { playParams: { id: 0 }, status: null, remainingTime: 0 },
playbackCache = { status: null, time: Date.now() };
const MusicKitInterop = {
init: function () {
MusicKit.getInstance().addEventListener(MusicKit.Events.playbackStateDidChange, () => {
if (MusicKitInterop.filterTrack(MusicKitInterop.getAttributes(), true, false)) {
console.log("ayy");
global.ipcRenderer.send('playbackStateDidChange', MusicKitInterop.getAttributes())
// if (typeof _plugins != "undefined") {
// _plugins.execute("OnPlaybackStateChanged", {Attributes: MusicKitInterop.getAttributes()})
// }
MusicKit.getInstance().addEventListener(
MusicKit.Events.playbackStateDidChange,
() => {
if (
MusicKitInterop.filterTrack(
MusicKitInterop.getAttributes(),
true,
false
)
) {
console.log('ayy');
global.ipcRenderer.send(
'playbackStateDidChange',
MusicKitInterop.getAttributes()
);
// if (typeof _plugins != "undefined") {
// _plugins.execute("OnPlaybackStateChanged", {Attributes: MusicKitInterop.getAttributes()})
// }
}
}
});
);
MusicKit.getInstance().addEventListener(MusicKit.Events.nowPlayingItemDidChange, () => {
if (MusicKitInterop.filterTrack(MusicKitInterop.getAttributes(), false, true)) {
global.ipcRenderer.send('nowPlayingItemDidChange', MusicKitInterop.getAttributes());
MusicKit.getInstance().addEventListener(
MusicKit.Events.nowPlayingItemDidChange,
() => {
if (
MusicKitInterop.filterTrack(
MusicKitInterop.getAttributes(),
false,
true
)
) {
global.ipcRenderer.send(
'nowPlayingItemDidChange',
MusicKitInterop.getAttributes()
);
}
}
});
);
MusicKit.getInstance().addEventListener(MusicKit.Events.authorizationStatusDidChange, () => {
global.ipcRenderer.send('authorizationStatusDidChange', MusicKit.getInstance().authorizationStatus)
})
MusicKit.getInstance().addEventListener(
MusicKit.Events.authorizationStatusDidChange,
() => {
global.ipcRenderer.send(
'authorizationStatusDidChange',
MusicKit.getInstance().authorizationStatus
);
}
);
MusicKit.getInstance().addEventListener(MusicKit.Events.mediaPlaybackError, (e) => {
console.warn(`[mediaPlaybackError] ${e}`);
})
MusicKit.getInstance().addEventListener(
MusicKit.Events.mediaPlaybackError,
(e) => {
console.warn(`[mediaPlaybackError] ${e}`);
}
);
},
getAttributes: function () {
const nowPlayingItem = MusicKit.getInstance().nowPlayingItem;
const isPlayingExport = MusicKit.getInstance().isPlaying;
const remainingTimeExport = MusicKit.getInstance().currentPlaybackTimeRemaining;
const attributes = (nowPlayingItem != null ? nowPlayingItem.attributes : {});
const remainingTimeExport =
MusicKit.getInstance().currentPlaybackTimeRemaining;
const attributes =
nowPlayingItem != null ? nowPlayingItem.attributes : {};
attributes.status = isPlayingExport ? isPlayingExport : false;
attributes.name = attributes.name ? attributes.name : 'No Title Found';
attributes.artwork = attributes.artwork ? attributes.artwork : {url: ''};
attributes.artwork.url = attributes.artwork.url ? attributes.artwork.url : '';
attributes.playParams = attributes.playParams ? attributes.playParams : {id: 'no-id-found'};
attributes.playParams.id = attributes.playParams.id ? attributes.playParams.id : 'no-id-found';
attributes.artwork = attributes.artwork
? attributes.artwork
: { url: '' };
attributes.artwork.url = attributes.artwork.url
? attributes.artwork.url
: '';
attributes.playParams = attributes.playParams
? attributes.playParams
: { id: 'no-id-found' };
attributes.playParams.id = attributes.playParams.id
? attributes.playParams.id
: 'no-id-found';
attributes.albumName = attributes.albumName ? attributes.albumName : '';
attributes.artistName = attributes.artistName ? attributes.artistName : '';
attributes.genreNames = attributes.genreNames ? attributes.genreNames : [];
attributes.remainingTime = remainingTimeExport ? (remainingTimeExport * 1000) : 0;
attributes.durationInMillis = attributes.durationInMillis ? attributes.durationInMillis : 0;
attributes.artistName = attributes.artistName
? attributes.artistName
: '';
attributes.genreNames = attributes.genreNames
? attributes.genreNames
: [];
attributes.remainingTime = remainingTimeExport
? remainingTimeExport * 1000
: 0;
attributes.durationInMillis = attributes.durationInMillis
? attributes.durationInMillis
: 0;
attributes.startTime = Date.now();
attributes.endTime = Math.round((attributes.playParams.id === cache.playParams.id ? (Date.now() + attributes.remainingTime) : (attributes.startTime + attributes.durationInMillis)));
attributes.endTime = attributes.endTime ? attributes.endTime : Date.now();
return attributes
attributes.endTime = Math.round(
attributes.playParams.id === cache.playParams.id
? Date.now() + attributes.remainingTime
: attributes.startTime + attributes.durationInMillis
);
attributes.endTime = attributes.endTime
? attributes.endTime
: Date.now();
return attributes;
},
filterTrack: function (a, playbackCheck, mediaCheck) {
if (a.title === "No Title Found" || a.playParams.id === "no-id-found") {
if (a.title === 'No Title Found' || a.playParams.id === 'no-id-found') {
return;
} else if (mediaCheck && a.playParams.id === cache.playParams.id) {
return;
} else if (playbackCheck && a.status === playbackCache.status) {
return;
} else if (playbackCheck && !a.status && a.remainingTime === playbackCache.time) { /* Pretty much have to do this to prevent multiple runs when a song starts playing */
} else if (
playbackCheck &&
!a.status &&
a.remainingTime === playbackCache.time
) {
/* Pretty much have to do this to prevent multiple runs when a song starts playing */
return;
}
cache = a;
if (playbackCheck) playbackCache = {status: a.status, time: a.remainingTime};
if (playbackCheck)
playbackCache = { status: a.status, time: a.remainingTime };
return true;
},
@ -74,23 +137,31 @@ const MusicKitInterop = {
if (MusicKit.getInstance().isPlaying) {
MusicKit.getInstance().pause();
} else if (MusicKit.getInstance().nowPlayingItem != null) {
MusicKit.getInstance().play().then(r => console.log(`[MusicKitInterop] Playing ${r}`));
MusicKit.getInstance()
.play()
.then((r) => console.log(`[MusicKitInterop] Playing ${r}`));
}
},
nextTrack: function () {
MusicKit.getInstance().skipToNextItem().then(r => console.log(`[MusicKitInterop] Skipping to Next ${r}`));
MusicKit.getInstance()
.skipToNextItem()
.then((r) =>
console.log(`[MusicKitInterop] Skipping to Next ${r}`)
);
},
previousTrack: function () {
MusicKit.getInstance().skipToPreviousItem().then(r => console.log(`[MusicKitInterop] Skipping to Previous ${r}`));
MusicKit.getInstance()
.skipToPreviousItem()
.then((r) =>
console.log(`[MusicKitInterop] Skipping to Previous ${r}`)
);
}
}
};
process.once('loaded', () => {
console.log("Setting ipcRenderer")
console.log('Setting ipcRenderer');
global.ipcRenderer = electron.ipcRenderer;
global.MusicKitInterop = MusicKitInterop;
});
});

File diff suppressed because it is too large Load diff

View file

@ -2,15 +2,15 @@
<html lang="en">
<head>
<link rel="preconnect" href="https://amp-api.music.apple.com/" crossorigin/>
<link rel="preconnect" href="https://api.music.apple.com/" crossorigin/>
<link rel="preconnect" href="https://is1-ssl.mzstatic.com/" crossorigin/>
<link rel="preconnect" href="https://is2-ssl.mzstatic.com/" crossorigin/>
<link rel="preconnect" href="https://is3-ssl.mzstatic.com/" crossorigin/>
<link rel="preconnect" href="https://is4-ssl.mzstatic.com/" crossorigin/>
<link rel="preconnect" href="https://is5-ssl.mzstatic.com/" crossorigin/>
<link rel="preconnect" href="https://play.itunes.apple.com/" crossorigin/>
<link rel="preconnect" href="https://aod-ssl.itunes.apple.com/" crossorigin/>
<link rel="preconnect" href="https://amp-api.music.apple.com/" crossorigin />
<link rel="preconnect" href="https://api.music.apple.com/" crossorigin />
<link rel="preconnect" href="https://is1-ssl.mzstatic.com/" crossorigin />
<link rel="preconnect" href="https://is2-ssl.mzstatic.com/" crossorigin />
<link rel="preconnect" href="https://is3-ssl.mzstatic.com/" crossorigin />
<link rel="preconnect" href="https://is4-ssl.mzstatic.com/" crossorigin />
<link rel="preconnect" href="https://is5-ssl.mzstatic.com/" crossorigin />
<link rel="preconnect" href="https://play.itunes.apple.com/" crossorigin />
<link rel="preconnect" href="https://aod-ssl.itunes.apple.com/" crossorigin />
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover">
@ -23,332 +23,321 @@
</head>
<body oncontextmenu="return false;" loading="1">
<div id="app">
<div id="app-main">
<div class="app-chrome">
<div class="app-chrome--left">
<div class="app-chrome-item full-height">
<div class="app-title"></div>
</div>
<div class="app-chrome-item">
<button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0"
<div id="app">
<div id="app-main">
<div class="app-chrome">
<div class="app-chrome--left">
<div class="app-chrome-item full-height">
<div class="app-title"></div>
</div>
<div class="app-chrome-item">
<button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0"
@click="mk.shuffleMode = 1"></button>
<button class="playback-button--small shuffle active" v-else @click="mk.shuffleMode = 0"></button>
</div>
<div class="app-chrome-item">
<button class="playback-button previous" @click="mk.skipToPreviousItem()"></button>
</div>
<div class="app-chrome-item">
<button class="playback-button pause" @click="mk.pause()" v-if="mk.isPlaying"></button>
<button class="playback-button play" @click="mk.play()" v-else></button>
</div>
<div class="app-chrome-item">
<button class="playback-button next" @click="mk.skipToNextItem()"></button>
</div>
<div class="app-chrome-item">
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0"
<button class="playback-button--small shuffle active" v-else
@click="mk.shuffleMode = 0"></button>
</div>
<div class="app-chrome-item">
<button class="playback-button previous" @click="mk.skipToPreviousItem()"></button>
</div>
<div class="app-chrome-item">
<button class="playback-button pause" @click="mk.pause()" v-if="mk.isPlaying"></button>
<button class="playback-button play" @click="mk.play()" v-else></button>
</div>
<div class="app-chrome-item">
<button class="playback-button next" @click="mk.skipToNextItem()"></button>
</div>
<div class="app-chrome-item">
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0"
@click="mk.repeatMode = 1"></button>
<button class="playback-button--small repeat active" @click="mk.repeatMode = 2"
<button class="playback-button--small repeat active" @click="mk.repeatMode = 2"
v-else-if="mk.repeatMode == 1"></button>
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 0"
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 0"
v-else-if="mk.repeatMode == 2"></button>
</div>
</div>
</div>
<div class="app-chrome--center">
<div class="app-chrome-item playback-controls">
<template v-if="mkReady()">
<div class="app-playback-controls">
<div class="artwork" :style="{'--artwork': getNowPlayingArtwork(42)}"></div>
<div class="playback-info">
<div class="song-name">
{{ mk.nowPlayingItem["attributes"]["name"] }}
</div>
<div class="song-artist">
{{ mk.nowPlayingItem["attributes"]["artistName"] }} - {{
mk.nowPlayingItem["attributes"]["albumName"] }}
</div>
<div class="song-progress">
<input type="range" step="0.01" min="0"
@change="mk.seekToTime($event.target.value)"
:max="mk.currentPlaybackDuration"
:value="playerLCD.playbackDuration">
<div class="app-chrome--center">
<div class="app-chrome-item playback-controls">
<template v-if="mkReady()">
<div class="app-playback-controls">
<div class="artwork" :style="{'--artwork': getNowPlayingArtwork(42)}"></div>
<div class="playback-info">
<div class="song-name">
{{ mk.nowPlayingItem["attributes"]["name"] }}
</div>
<div class="song-artist">
{{ mk.nowPlayingItem["attributes"]["artistName"] }} - {{
mk.nowPlayingItem["attributes"]["albumName"] }}
</div>
<div class="song-progress">
<input type="range" step="0.01" min="0"
@change="mk.seekToTime($event.target.value)"
:max="mk.currentPlaybackDuration" :value="playerLCD.playbackDuration">
</div>
</div>
<div class="actions">❤️</div>
</div>
<div class="actions">❤️</div>
</div>
</template>
</div>
</div>
<div class="app-chrome--right">
<div class="app-chrome-item volume display--large">
<input type="range" class="" step="0.01" min="0" max="1" v-model="mk.volume"
v-if="typeof mk.volume != 'undefined'">
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 22" version="1.1" fill="#fff"
style="width: 100%; height: 100%; fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421">
<path
d="M16.811,12.75c0.245,-0.355 0.389,-0.786 0.389,-1.25c0,-1.215 -0.985,-2.2 -2.2,-2.2c-1.215,0 -2.2,0.985 -2.2,2.2c0,0.466 0.145,0.898 0.392,1.254l-0.83,1.047c-0.537,-0.616 -0.862,-1.42 -0.862,-2.301c0,-1.933 1.567,-3.5 3.5,-3.5c1.933,0 3.5,1.567 3.5,3.5c0,0.879 -0.324,1.683 -0.859,2.297l-0.83,-1.047Zm1.271,1.604c0.694,-0.749 1.118,-1.752 1.118,-2.854c0,-2.32 -1.88,-4.2 -4.2,-4.2c-2.32,0 -4.2,1.88 -4.2,4.2c0,1.103 0.425,2.107 1.121,2.857l-0.814,1.028c-0.993,-0.995 -1.607,-2.368 -1.607,-3.885c0,-3.038 2.462,-5.5 5.5,-5.5c3.038,0 5.5,2.462 5.5,5.5c0,1.515 -0.613,2.887 -1.604,3.882l-0.814,-1.028Zm1.252,1.58c1.151,-1.126 1.866,-2.697 1.866,-4.434c0,-3.424 -2.776,-6.2 -6.2,-6.2c-3.424,0 -6.2,2.776 -6.2,6.2c0,1.739 0.716,3.311 1.869,4.437l-0.811,1.023c-1.452,-1.368 -2.358,-3.308 -2.358,-5.46c0,-4.142 3.358,-7.5 7.5,-7.5c4.142,0 7.5,3.358 7.5,7.5c0,2.15 -0.905,4.089 -2.355,5.457l-0.811,-1.023Zm-0.227,2.066l-8.219,0c-0.355,0 -0.515,-0.434 -0.27,-0.717l4.058,-5.12c0.178,-0.217 0.474,-0.217 0.652,0l4.058,5.12c0.237,0.283 0.085,0.717 -0.279,0.717Z"
style="fill-rule:nonzero"></path>
</svg>
</button>
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small queue"></button>
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small lyrics"
@click="drawertest = !drawertest; lyricon =!lyricon; if(drawertest == true){loadLyrics();}"
></button>
</div>
<div class="app-chrome-item full-height">
<div class="window-controls">
<div class="minimize" @click="ipcRenderer.send('minimize')"></div>
<div class="minmax restore" v-if="chrome.maximized" @click="ipcRenderer.send('maximize')"></div>
<div class="minmax" v-else @click="ipcRenderer.send('maximize')"></div>
<div class="close" @click="ipcRenderer.send('close')"></div>
</div>
</div>
</div>
</div>
<div class="app-navigation">
<div id="app-sidebar">
<div class="app-sidebar-header">
<div class="search-input-container">
<div class="search-input--icon"></div>
<input type="search"
spellcheck="false"
@click="showSearch()"
@change="showSearch();searchQuery()"
placeholder="Search..."
v-model="search.term"
class="search-input">
</div>
</div>
<div class="app-sidebar-content">
<div class="app-sidebar-header-text">
Apple Music
</div>
<sidebar-library-item name="Listen Now" page="listen_now"></sidebar-library-item>
<sidebar-library-item name="Browse" page="browse"></sidebar-library-item>
<sidebar-library-item name="Radio" page="radio"></sidebar-library-item>
<div class="app-sidebar-header-text">
Library
</div>
<sidebar-library-item name="Songs" page="library-songs"></sidebar-library-item>
<sidebar-library-item name="Albums" page="library-albums"></sidebar-library-item>
<sidebar-library-item name="Artists" page="library-artists"></sidebar-library-item>
<sidebar-library-item name="Made For You" page="library-madeforyou"></sidebar-library-item>
<div class="app-sidebar-header-text">
Playlists
</div>
<button class="app-sidebar-item" v-for="item in playlists.listing" :key="item.id" :href="item.href"
@click='app.page=`playlist_` + item.id ; showingPlaylist = [];getPlaylistFromID(app.page.substring(9))'>
{{ item.attributes.name }}
</button>
</div>
<transition name="wpfade">
<div class="app-sidebar-content" v-if="chrome.menuOpened">
<button class="app-sidebar-item" @click="chrome.hideUserInfo = !chrome.hideUserInfo">
Toggle Personal Info
</button>
<button class="app-sidebar-item">
About
</button>
<button class="app-sidebar-item">
Discord
</button>
<button class="app-sidebar-item">
Settings
</button>
<button class="app-sidebar-item">
Sign Out
</button>
</div>
</transition>
<div class="app-sidebar-footer">
<input type="range" class="display--small">
<button class="app-sidebar-button" style="width:100%"
@click="chrome.menuOpened = !chrome.menuOpened">
<template v-if="chrome.userinfo.attributes">
<img class="sidebar-user-icon" loading="lazy"
:src="getMediaItemArtwork(chrome.userinfo.attributes['artwork'] ? chrome.userinfo.attributes['artwork']['url'] : '', 26)"
/>
</template>
<div class="sidebar-user-text" v-if="!chrome.hideUserInfo">
<template v-if="chrome.userinfo.attributes">
<div class="fullname text-overflow-elipsis">{{ chrome.userinfo.attributes.name }}</div>
<div class="handle-text text-overflow-elipsis">@{{ chrome.userinfo.attributes.handle
}}
</div>
</template>
<template v-else>
Sign in
</template>
</div>
<div class="sidebar-user-text" v-else>
Cider
</div>
</button>
</div>
</div>
<div class="app-sidebar-notification" v-if="library.songs.downloadState == 1">
<div>Updating your library...</div>
<div>{{ library.songs.meta.progress }} / {{ library.songs.meta.total }}</div>
<div style="width: 100%">
<progress style="width: 80%;" :value="library.songs.meta.progress"
:max="library.songs.meta.total"></progress>
<div class="app-chrome--right">
<div class="app-chrome-item volume display--large">
<input type="range" class="" step="0.01" min="0" max="1" v-model="mk.volume"
v-if="typeof mk.volume != 'undefined'">
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 22" version="1.1" fill="#fff"
style="width: 100%; height: 100%; fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421">
<path
d="M16.811,12.75c0.245,-0.355 0.389,-0.786 0.389,-1.25c0,-1.215 -0.985,-2.2 -2.2,-2.2c-1.215,0 -2.2,0.985 -2.2,2.2c0,0.466 0.145,0.898 0.392,1.254l-0.83,1.047c-0.537,-0.616 -0.862,-1.42 -0.862,-2.301c0,-1.933 1.567,-3.5 3.5,-3.5c1.933,0 3.5,1.567 3.5,3.5c0,0.879 -0.324,1.683 -0.859,2.297l-0.83,-1.047Zm1.271,1.604c0.694,-0.749 1.118,-1.752 1.118,-2.854c0,-2.32 -1.88,-4.2 -4.2,-4.2c-2.32,0 -4.2,1.88 -4.2,4.2c0,1.103 0.425,2.107 1.121,2.857l-0.814,1.028c-0.993,-0.995 -1.607,-2.368 -1.607,-3.885c0,-3.038 2.462,-5.5 5.5,-5.5c3.038,0 5.5,2.462 5.5,5.5c0,1.515 -0.613,2.887 -1.604,3.882l-0.814,-1.028Zm1.252,1.58c1.151,-1.126 1.866,-2.697 1.866,-4.434c0,-3.424 -2.776,-6.2 -6.2,-6.2c-3.424,0 -6.2,2.776 -6.2,6.2c0,1.739 0.716,3.311 1.869,4.437l-0.811,1.023c-1.452,-1.368 -2.358,-3.308 -2.358,-5.46c0,-4.142 3.358,-7.5 7.5,-7.5c4.142,0 7.5,3.358 7.5,7.5c0,2.15 -0.905,4.089 -2.355,5.457l-0.811,-1.023Zm-0.227,2.066l-8.219,0c-0.355,0 -0.515,-0.434 -0.27,-0.717l4.058,-5.12c0.178,-0.217 0.474,-0.217 0.652,0l4.058,5.12c0.237,0.283 0.085,0.717 -0.279,0.717Z"
style="fill-rule:nonzero"></path>
</svg>
</button>
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small queue"></button>
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small lyrics"
@click="drawertest = !drawertest; lyricon =!lyricon; if(drawertest == true){loadLyrics();}"></button>
</div>
<div class="app-chrome-item full-height">
<div class="window-controls">
<div class="minimize" @click="ipcRenderer.send('minimize')"></div>
<div class="minmax restore" v-if="chrome.maximized" @click="ipcRenderer.send('maximize')">
</div>
<div class="minmax" v-else @click="ipcRenderer.send('maximize')"></div>
<div class="close" @click="ipcRenderer.send('close')"></div>
</div>
</div>
</div>
</div>
<div id="app-content">
<!-- Playlist / Album page-->
<transition name="wpfade">
<template v-if="page.includes('playlist_')">
<cider-playlist :data="showingPlaylist"></cider-playlist>
</template>
</transition>
<transition name="wpfade">
<template v-if="page.includes('album_')">
<cider-playlist :data="showingPlaylist"></cider-playlist>
</template>
</transition>
</transition>
<!-- Browse -->
<transition name="wpfade">
<template v-if="page == 'browse'">
<div class="content-inner">
<button id="apple-music-authorize" class="md-btn md-btn-primary" @click="init()">Start
MusicKit
<div class="app-navigation">
<div id="app-sidebar">
<div class="app-sidebar-header">
<div class="search-input-container">
<div class="search-input--icon"></div>
<input type="search" spellcheck="false" @click="showSearch()"
@change="showSearch();searchQuery()" placeholder="Search..." v-model="search.term"
class="search-input">
</div>
</div>
<div class="app-sidebar-content">
<div class="app-sidebar-header-text">
Apple Music
</div>
<sidebar-library-item name="Listen Now" page="listen_now"></sidebar-library-item>
<sidebar-library-item name="Browse" page="browse"></sidebar-library-item>
<sidebar-library-item name="Radio" page="radio"></sidebar-library-item>
<div class="app-sidebar-header-text">
Library
</div>
<sidebar-library-item name="Songs" page="library-songs"></sidebar-library-item>
<sidebar-library-item name="Albums" page="library-albums"></sidebar-library-item>
<sidebar-library-item name="Artists" page="library-artists"></sidebar-library-item>
<sidebar-library-item name="Made For You" page="library-madeforyou"></sidebar-library-item>
<div class="app-sidebar-header-text">
Playlists
</div>
<button class="app-sidebar-item" v-for="item in playlists.listing" :key="item.id"
:href="item.href"
@click='app.page=`playlist_` + item.id ; showingPlaylist = [];getPlaylistFromID(app.page.substring(9))'>
{{ item.attributes.name }}
</button>
</div>
<transition name="wpfade">
<div class="app-sidebar-content" v-if="chrome.menuOpened">
<button class="app-sidebar-item" @click="chrome.hideUserInfo = !chrome.hideUserInfo">
Toggle Personal Info
</button>
<button id="apple-music-unauthorize" class="md-btn md-btn-primary" @click="unauthorize()">
Stop
MusicKit
<button class="app-sidebar-item">
About
</button>
<br>
<template v-if="mk.nowPlayingItem">
currentPlaybackProgress: {{ app.mk.currentPlaybackProgress }}
<br>
currentPlaybackDuration: {{ app.mk.currentPlaybackDuration }}
<button class="app-sidebar-item">
Discord
</button>
<button class="app-sidebar-item">
Settings
</button>
<button class="app-sidebar-item">
Sign Out
</button>
</div>
</transition>
<div class="app-sidebar-footer">
<input type="range" class="display--small">
<button class="app-sidebar-button" style="width:100%"
@click="chrome.menuOpened = !chrome.menuOpened">
<template v-if="chrome.userinfo.attributes">
<img class="sidebar-user-icon" loading="lazy"
:src="getMediaItemArtwork(chrome.userinfo.attributes['artwork'] ? chrome.userinfo.attributes['artwork']['url'] : '', 26)" />
</template>
<div><input type="text" v-model="quickPlayQuery">
<button @click="quickPlay(quickPlayQuery)">Play</button>
</div>
<h1 class="header-text">Browse</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, urna eu tincidunt
consectetur, nisl nunc euismod nisi, eu porttitor nisl nisi euismod nisi.
</p>
<div class="media-item--small">
<div class="artwork">
</div>
<div class="text">
Text
</div>
<div class="subtext">
Subtext
</div>
<div class="sidebar-user-text" v-if="!chrome.hideUserInfo">
<template v-if="chrome.userinfo.attributes">
<div class="fullname text-overflow-elipsis">{{ chrome.userinfo.attributes.name }}
</div>
<div class="handle-text text-overflow-elipsis">@{{ chrome.userinfo.attributes.handle
}}
</div>
</template>
<template v-else>
Sign in
</template>
</div>
<br>
<br>
<h1 class="header-text">Listen Now</h1>
<div class="winbox">
<div class="fancy">990kbps</div>
<div class="">
<button class="md-btn md-btn-primary">Audio Quality Settings</button>
<div class="sidebar-user-text" v-else>
Cider
</div>
</button>
</div>
<div class="app-sidebar-notification" v-if="library.songs.downloadState == 1">
<div>Updating your library...</div>
<div>{{ library.songs.meta.progress }} / {{ library.songs.meta.total }}</div>
<div style="width: 100%">
<progress style="width: 80%;" :value="library.songs.meta.progress"
:max="library.songs.meta.total"></progress>
</div>
</div>
</div>
<div id="app-content">
<!-- Playlist / Album page-->
<transition name="wpfade">
<template v-if="page.includes('playlist_')">
<cider-playlist :data="showingPlaylist"></cider-playlist>
</template>
</transition>
<transition name="wpfade">
<template v-if="page.includes('album_')">
<cider-playlist :data="showingPlaylist"></cider-playlist>
</template>
</transition>
</transition>
<!-- Browse -->
<transition name="wpfade">
<template v-if="page == 'browse'">
<div class="content-inner">
<button id="apple-music-authorize" class="md-btn md-btn-primary" @click="init()">Start
MusicKit
</button>
<button id="apple-music-unauthorize" class="md-btn md-btn-primary"
@click="unauthorize()">
Stop
MusicKit
</button>
<br>
<template v-if="mk.nowPlayingItem">
currentPlaybackProgress: {{ app.mk.currentPlaybackProgress }}
<br>
currentPlaybackDuration: {{ app.mk.currentPlaybackDuration }}
</template>
<div><input type="text" v-model="quickPlayQuery">
<button @click="quickPlay(quickPlayQuery)">Play</button>
</div>
<h1 class="header-text">Browse</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, urna eu
tincidunt
consectetur, nisl nunc euismod nisi, eu porttitor nisl nisi euismod nisi.
</p>
<div class="media-item--small">
<div class="artwork">
</div>
<div class="text">
Text
</div>
<div class="subtext">
Subtext
</div>
</div>
<br>
<br>
<h1 class="header-text">Listen Now</h1>
<div class="winbox">
<div class="fancy">990kbps</div>
<div class="">
<button class="md-btn md-btn-primary">Audio Quality Settings</button>
</div>
</div>
<button class="md-btn" @click="drawertest = !drawertest">Toggle Drawer</button>
<button class="md-btn">Button</button>
<button class="md-btn md-btn-primary">Button</button>
</div>
<button class="md-btn" @click="drawertest = !drawertest">Toggle Drawer</button>
<button class="md-btn">Button</button>
<button class="md-btn md-btn-primary">Button</button>
</div>
</template>
</transition>
<!-- Listen Now -->
<transition v-on:enter="getListenNow()" name="wpfade">
<template v-if="page == 'listen_now'" @created="console.log('listennow')">
<cider-listen-now :data="listennow"></cider-listen-now>
</template>
</transition>
<!-- Radio -->
<transition v-on:enter="getRadioStations()" name="wpfade">
<template v-if="page == 'radio'" @created="console.log('radio')">
<div class="content-inner">
<h1 class="header-text">Radio</h1>
<h3>Recent Stations</h3>
<mediaitem-square :item="item" v-for="item in radio.personal"></mediaitem-square>
</div>
</template>
</transition>
<!-- Search -->
<transition name="wpfade">
<template v-if="page == 'search'">
<cider-search :search="search"></cider-search>
</template>
</transition>
<!-- Library - Songs -->
<transition name="wpfade" v-on:enter="getLibrarySongsFull()">
<template v-if="page == 'library-songs'">
<div class="content-inner">
<h1 class="header-text">Songs</h1>
<div class="search-input-container" style="width:100%;margin: 16px 0px;">
<div class="search-input--icon"></div>
<input type="search"
style="width:100%;"
spellcheck="false"
placeholder="Search..."
@input="searchLibrarySongs"
v-model="library.songs.search"
class="search-input">
</template>
</transition>
<!-- Listen Now -->
<transition v-on:enter="getListenNow()" name="wpfade">
<template v-if="page == 'listen_now'" @created="console.log('listennow')">
<cider-listen-now :data="listennow"></cider-listen-now>
</template>
</transition>
<!-- Radio -->
<transition v-on:enter="getRadioStations()" name="wpfade">
<template v-if="page == 'radio'" @created="console.log('radio')">
<div class="content-inner">
<h1 class="header-text">Radio</h1>
<h3>Recent Stations</h3>
<mediaitem-square :item="item" v-for="item in radio.personal"></mediaitem-square>
</div>
<mediaitem-list-item :item="item"
v-for="item in library.songs.displayListing"></mediaitem-list-item>
</div>
</template>
</transition>
<!-- Library - Albums -->
<transition name="wpfade" v-on:enter="getLibraryAlbums()">
<template v-if="page == 'library-albums'">
<div class="content-inner">
<h1 class="header-text">Albums</h1>
<div class="search-input-container" style="width:100%;margin: 16px 0px;">
<div class="search-input--icon"></div>
<input type="search"
style="width:100%;"
spellcheck="false"
placeholder="Search..."
class="search-input">
</template>
</transition>
<!-- Search -->
<transition name="wpfade">
<template v-if="page == 'search'">
<cider-search :search="search"></cider-search>
</template>
</transition>
<!-- Library - Songs -->
<transition name="wpfade" v-on:enter="getLibrarySongsFull()">
<template v-if="page == 'library-songs'">
<div class="content-inner">
<h1 class="header-text">Songs</h1>
<div class="search-input-container" style="width:100%;margin: 16px 0px;">
<div class="search-input--icon"></div>
<input type="search" style="width:100%;" spellcheck="false" placeholder="Search..."
@input="searchLibrarySongs" v-model="library.songs.search" class="search-input">
</div>
<mediaitem-list-item :item="item" v-for="item in library.songs.displayListing">
</mediaitem-list-item>
</div>
<mediaitem-square-large :item="item"
v-for="item in library.albums.listing"></mediaitem-square-large>
</div>
</template>
</template>
</transition>
<!-- Library - Albums -->
<transition name="wpfade" v-on:enter="getLibraryAlbums()">
<template v-if="page == 'library-albums'">
<div class="content-inner">
<h1 class="header-text">Albums</h1>
<div class="search-input-container" style="width:100%;margin: 16px 0px;">
<div class="search-input--icon"></div>
<input type="search" style="width:100%;" spellcheck="false" placeholder="Search..."
class="search-input">
</div>
<mediaitem-square-large :item="item" v-for="item in library.albums.listing">
</mediaitem-square-large>
</div>
</template>
</transition>
</div>
<transition name="drawertransition">
<div class="app-drawer" v-if="drawertest">
<lyrics-view v-if="drawertest && lyricon" :time="lyriccurrenttime" :lyrics="lyrics">
</lyrics-view>
</div>
</transition>
</div>
<transition name="drawertransition">
<div class="app-drawer" v-if="drawertest">
<lyrics-view v-if="drawertest && lyricon" :time="lyriccurrenttime" :lyrics="lyrics"></lyrics-view>
</div>
</transition>
</div>
<transition name="wpfade">
<img v-show="chrome.artworkReady" @load="chrome.artworkReady = true" class="bg-artwork"
:src="getNowPlayingArtworkBG(32)">
</transition>
<transition name="wpfade">
<div class="bg-artwork--placeholder" v-else></div>
</transition>
</div>
<transition name="wpfade">
<img v-show="chrome.artworkReady"
@load="chrome.artworkReady = true"
class="bg-artwork"
:src="getNowPlayingArtworkBG(32)">
</transition>
<transition name="wpfade">
<div class="bg-artwork--placeholder" v-else></div>
</transition>
</div>
<script type="text/x-template" id="mediaitem-artwork">
<script type="text/x-template" id="mediaitem-artwork">
<template v-if="type == 'artists'">
<div class="mediaitem-artwork rounded"
>
@ -365,8 +354,8 @@
</template>
</script>
<!-- Generic Collection of MediaItems -->
<script type="text/x-template" id="collection-view-generic">
<!-- Generic Collection of MediaItems -->
<script type="text/x-template" id="collection-view-generic">
<template>
<div class="content-inner">
@ -374,8 +363,8 @@
</template>
</script>
<!-- Listen Now -->
<script type="text/x-template" id="cider-listen-now">
<!-- Listen Now -->
<script type="text/x-template" id="cider-listen-now">
<div class="content-inner">
<h1 class="header-text">Listen Now</h1>
<template v-for="recom in data.data">
@ -401,8 +390,8 @@
</div>
</script>
<!-- Album / Playlist View -->
<script type="text/x-template" id="cider-playlist">
<!-- Album / Playlist View -->
<script type="text/x-template" id="cider-playlist">
<div class="content-inner">
<template v-if="data != [] && data.attributes != []">
<div class="playlist-display row">
@ -429,8 +418,8 @@
</div>
</script>
<!-- Search -->
<script type="text/x-template" id="cider-search">
<!-- Search -->
<script type="text/x-template" id="cider-search">
<div class="content-inner">
<div class="row">
<div class="col-sm" style="width: auto;" v-if="getTopResult()">
@ -496,20 +485,20 @@
</div>
</script>
<script type="text/x-template" id="am-musiccovershelf">
<script type="text/x-template" id="am-musiccovershelf">
<h1>{{ component.attributes.title.stringForDisplay }}</h1>
</script>
<!-- Sidebar Item -->
<script type="text/x-template" id="sidebar-library-item">
<!-- Sidebar Item -->
<script type="text/x-template" id="sidebar-library-item">
<button class="app-sidebar-item"
:class="$parent.getSidebarItemClass(page)"
@click="$parent.page = page">{{ name }}
</button>
</script>
<!-- Horizontal MediaItem Scroller -->
<script type="text/x-template" id="mediaitem-scroller-horizontal">
<!-- Horizontal MediaItem Scroller -->
<script type="text/x-template" id="mediaitem-scroller-horizontal">
<template>
<div class="cd-hmedia-scroller">
<mediaitem-square :item="item"
@ -518,8 +507,8 @@
</template>
</script>
<!-- Horizontal MediaItem Scroller (Large) -->
<script type="text/x-template" id="mediaitem-scroller-horizontal-large">
<!-- Horizontal MediaItem Scroller (Large) -->
<script type="text/x-template" id="mediaitem-scroller-horizontal-large">
<template>
<div class="cd-hmedia-scroller">
<mediaitem-square-large :item="item"
@ -528,9 +517,9 @@
</template>
</script>
<!-- Horizontal MediaItem Scroller (SP : Special) -->
<!-- Horizontal MediaItem Scroller (SP : Special) -->
<script type="text/x-template" id="mediaitem-scroller-horizontal-sp">
<script type="text/x-template" id="mediaitem-scroller-horizontal-sp">
<template>
<div class="cd-hmedia-scroller">
<mediaitem-square-sp :item="item"
@ -539,9 +528,9 @@
</template>
</script>
<!-- MediaItem List Item -->
<!-- MediaItem List Item -->
<script type="text/x-template" id="mediaitem-list-item">
<script type="text/x-template" id="mediaitem-list-item">
<template>
<div @click="app.playMediaItemById(item.attributes.playParams.id ?? item.id, item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.url)"
class="cd-mediaitem-list-item">
@ -572,8 +561,8 @@
</template>
</script>
<!-- MediaItem Horizontal Rectangle -->
<script type="text/x-template" id="mediaitem-hrect">
<!-- MediaItem Horizontal Rectangle -->
<script type="text/x-template" id="mediaitem-hrect">
<template>
<div @click="app.playMediaItemById(item.attributes.playParams.id ?? item.id, item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.url)"
class="cd-mediaitem-hrect">
@ -599,9 +588,9 @@
</template>
</script>
<!-- MediaItem Square -->
<!-- MediaItem Square -->
<script type="text/x-template" id="mediaitem-square">
<script type="text/x-template" id="mediaitem-square">
<template>
<div @click="app.playMediaItemById(item.attributes.playParams.id ?? item.id, item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.url)"
class="cd-mediaitem-square">
@ -622,9 +611,9 @@
</template>
</script>
<!-- MediaItem Square (Large) -->
<!-- MediaItem Square (Large) -->
<script type="text/x-template" id="mediaitem-square-large">
<script type="text/x-template" id="mediaitem-square-large">
<template>
<div style="position: relative; display: inline-flex;">
<div @click.self='app.routeView(item)'
@ -680,8 +669,8 @@
</template>
</script>
<!-- MediaItem Square SP -->
<script type="text/x-template" id="mediaitem-square-sp">
<!-- MediaItem Square SP -->
<script type="text/x-template" id="mediaitem-square-sp">
<template>
<div style="position: relative; display: inline-flex;">
<div @click.self='app.routeView(item)'
@ -742,7 +731,7 @@
</div>
</template>
</script>
<script type="text/x-template" id="lyrics-view">
<script type="text/x-template" id="lyrics-view">
<div class="md-body lyric-body">
<template v-if="lyrics">
<template v-for="lyric in lyrics" v-if="lyric.line != 'lrcInstrumental'">
@ -771,8 +760,8 @@
</template>
</div>
</script>
<script src="https://js-cdn.music.apple.com/musickit/v2/amp/musickit.js"></script>
<script src="index.js?v=1"></script>
<script src="https://js-cdn.music.apple.com/musickit/v2/amp/musickit.js"></script>
<script src="index.js?v=1"></script>
</body>
</html>

View file

@ -1,156 +1,191 @@
// Apple Music Listen Now Page
// URL : https://amp-api.music.apple.com/v1/me/recommendations?timezone=+00:00
// &with=friendsMix,library,social&art[social-profiles:url]=c
// &name=listen-now&art[url]=c,f&omit[resource]=autos
// &relate[editorial-items]=contents
// &extend=editorialCard,editorialVideo
// &extend[albums]=artistUrl
// &extend[library-albums]=artistUrl
// &extend[playlists]=artistNames,editorialArtwork
// &extend[library-playlists]=artistNames,editorialArtwork
// &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-profiles,social-upsells
// &l=en-gb&platform=web
// &with=friendsMix,library,social&art[social-profiles:url]=c
// &name=listen-now&art[url]=c,f&omit[resource]=autos
// &relate[editorial-items]=contents
// &extend=editorialCard,editorialVideo
// &extend[albums]=artistUrl
// &extend[library-albums]=artistUrl
// &extend[playlists]=artistNames,editorialArtwork
// &extend[library-playlists]=artistNames,editorialArtwork
// &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-profiles,social-upsells
// &l=en-gb&platform=web
await app.mk.api.personalRecommendations("",
{
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"],
"extend[playlists]": ["artistNames", "editorialArtwork"],
"extend[library-playlists]": ["artistNames", "editorialArtwork"],
"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-profiles,social-upsells",
l:"en-gb",
platform:"web"
},
{
includeResponseMeta: !0,
reload: !0
});
await app.mk.api.personalRecommendations(
'',
{
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'],
'extend[playlists]': ['artistNames', 'editorialArtwork'],
'extend[library-playlists]': ['artistNames', 'editorialArtwork'],
'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-profiles,social-upsells',
l: 'en-gb',
platform: 'web'
},
{
includeResponseMeta: !0,
reload: !0
}
);
// Browse page
await app.mk.api.groupings("",
{
platform: "web",
name: "music",
l: "en-gb",
"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"
});
await app.mk.api.groupings('', {
platform: 'web',
name: 'music',
l: 'en-gb',
'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'
});
// Radio page
await app.mk.api.recentRadioStations("",
{l: "en-gb",
"platform": "web",
"art[url]": "f"});
await app.mk.api.recentRadioStations('', {
l: 'en-gb',
platform: 'web',
'art[url]': 'f'
});
// Recently Added
await app.mk.api.library.recentlyAdded({
"platform": "web",
include: {
"library-albums": ["artists"],
"library-artists": ["catalog"]
await app.mk.api.library.recentlyAdded(
{
platform: 'web',
include: {
'library-albums': ['artists'],
'library-artists': ['catalog']
},
fields: {
artists: ['url'],
albums: 'artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url'
},
includeOnly: ['catalog', 'artists'],
limit: 25
},
fields: {
artists: ["url"],
albums: "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url"
},
includeOnly: ["catalog", "artists"],
limit: 25
}, {
reload: !0,
includePagination: !0
})
{
reload: !0,
includePagination: !0
}
);
// Songs
await app.mk.api.library.songs({limit: 100}).then((data)=>{
console.log(data)
})
// Songs
await app.mk.api.library.songs({ limit: 100 }).then((data) => {
console.log(data);
});
// Artists
await app.mk.api.library.artists({limit: 100}).then((data)=>{
console.log(data)
})
// Artists
await app.mk.api.library.artists({ limit: 100 }).then((data) => {
console.log(data);
});
// Artists
await app.mk.api.library.albums({limit: 100}).then((data)=>{
console.log(data)
})
// Artists
await app.mk.api.library.albums({ limit: 100 }).then((data) => {
console.log(data);
});
// Albums
// does not like limit = 100 for some reason
await app.mk.api.library.albums({limit: 50}).then((data)=>{
console.log(data)
})
await app.mk.api.library.albums({ limit: 50 }).then((data) => {
console.log(data);
});
// Made For You
app.mk.api.recommendations("",{extend: "editorialArtwork,artistUrl"})
app.mk.api.recommendations('', { extend: 'editorialArtwork,artistUrl' });
// Library with library length
await app.mk.api.library.songs("", {limit: 100}, {includeResponseMeta: !0}).then((data)=>{
console.log(data)
})
await app.mk.api.library
.songs('', { limit: 100 }, { includeResponseMeta: !0 })
.then((data) => {
console.log(data);
});
// Artist View Top Songs
app.mk.api.artistView("325096253", "top-songs", {}, {view: "top-songs", includeResponseMeta: !0})
app.mk.api.artistView(
'325096253',
'top-songs',
{},
{ view: 'top-songs', includeResponseMeta: !0 }
);
// Artist Page Data
app.mkapi("artists", false, "412778295", {
"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",
"omit[resource:songs]": "relationships",
"fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url,trackCount",
"limit[artists:top-songs]": 20,
"art[url]": "f"
}, {includeResponseMeta: !0}).then((data)=>{
console.log(data)
})
app.mkapi(
'artists',
false,
'412778295',
{
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',
'omit[resource:songs]': 'relationships',
'fields[albums]':
'artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url,trackCount',
'limit[artists:top-songs]': 20,
'art[url]': 'f'
},
{ includeResponseMeta: !0 }
).then((data) => {
console.log(data);
});
// download entire library
var library = []
var library = [];
var downloaded = null;
function downloadChunk () {
if(downloaded == null) {
app.mk.api.library.songs("", {limit: 100}, {includeResponseMeta: !0}).then((response)=>{
processChunk(response)
})
}else{
downloaded.next("", {limit: 100}, {includeResponseMeta: !0}).then((response)=>{
processChunk(response)
})
function downloadChunk() {
if (downloaded == null) {
app.mk.api.library
.songs('', { limit: 100 }, { includeResponseMeta: !0 })
.then((response) => {
processChunk(response);
});
} else {
downloaded
.next('', { limit: 100 }, { includeResponseMeta: !0 })
.then((response) => {
processChunk(response);
});
}
}
function processChunk (response) {
downloaded = response
library = library.concat(downloaded.data)
function processChunk(response) {
downloaded = response;
library = library.concat(downloaded.data);
if (downloaded.meta.total > library.length) {
console.log(`downloading next chunk - ${library.length} songs so far`)
downloadChunk()
console.log(`downloading next chunk - ${library.length} songs so far`);
downloadChunk();
} else {
console.log(library)
console.log(library);
}
}
@ -158,23 +193,26 @@ function processChunk (response) {
// recentPlayed() -> recently played songs ?
// create Artist / Song/ Album stations:
app.mk.setStationQueue({artist:"1258279972"})
app.mk.setStationQueue({song:"1437308307"}) // yes the song id here can be the albumId, but just keep using the song:
app.mk.setStationQueue({ artist: '1258279972' });
app.mk.setStationQueue({ song: '1437308307' }); // yes the song id here can be the albumId, but just keep using the song:
// Sorting Playlists, send an array of tracks in the format below
// playlist must be fully recursively downloaded first before sorting
app.mk.api.library.putPlaylistTracklisting(app.showingPlaylist.attributes.playParams.id, [
{
"id": relationships.tracks.data[X].id,
"type": relationships.tracks.data[X].type
},
{
"id": relationships.tracks.data[X].id,
"type": relationships.tracks.data[X].type
},
{
"id": relationships.tracks.data[X].id,
"type": relationships.tracks.data[X].type
},
])
app.mk.api.library.putPlaylistTracklisting(
app.showingPlaylist.attributes.playParams.id,
[
{
id: relationships.tracks.data[X].id,
type: relationships.tracks.data[X].type
},
{
id: relationships.tracks.data[X].id,
type: relationships.tracks.data[X].type
},
{
id: relationships.tracks.data[X].id,
type: relationships.tracks.data[X].type
}
]
);

View file

@ -54,8 +54,8 @@
persistentState: "required"
}
let p = {
platformInfo: {requiresCDMAttachOnStart: !0, maxSecurityLevel: d, keySystemConfig: h},
appData: {serviceName: "Apple Music"}
platformInfo: { requiresCDMAttachOnStart: !0, maxSecurityLevel: d, keySystemConfig: h },
appData: { serviceName: "Apple Music" }
}
this.hls.attachMedia(this.$refs.video);
this.hls.loadSource(this.video);

View file

@ -41,14 +41,14 @@
}
},
props: {
'item': {type: Object, required: true},
'parent': {type: String, required: false},
'index': {type: Number, required: false, default: -1},
'show-artwork': {type: Boolean, default: true},
'show-library-status': {type: Boolean, default: true},
'show-meta-data': {type: Boolean, default: false},
'show-duration': {type: Boolean, default: true},
'contextExt': {type: Object, required: false},
'item': { type: Object, required: true },
'parent': { type: String, required: false },
'index': { type: Number, required: false, default: -1 },
'show-artwork': { type: Boolean, default: true },
'show-library-status': { type: Boolean, default: true },
'show-meta-data': { type: Boolean, default: false },
'show-duration': { type: Boolean, default: true },
'contextExt': { type: Object, required: false },
},
methods: {
uuidv4() {
@ -68,22 +68,25 @@
return this.item.attributes.playParams.kind
}
},
async select(e) {
let u = this.item
let u1 = await app.mk.api.library.artistRelationship(u.id,"albums",
{platform: "web",
"include[library-albums]": "artists,tracks",
"include[library-artists]": "catalog",
"fields[artists]": "url",
"includeOnly": "catalog,artists"}
)
app.showCollection({data : Object.assign({},u1)}, u.attributes.name?? '', '');
async select(e) {
let u = this.item
let u1 = await app.mk.api.library.artistRelationship(u.id, "albums",
{
platform: "web",
"include[library-albums]": "artists,tracks",
"include[library-artists]": "catalog",
"fields[artists]": "url",
"includeOnly": "catalog,artists"
}
)
app.showCollection({ data: Object.assign({}, u1) }, u.attributes.name ?? '', '');
},
getArtwork(){
getArtwork() {
let u = ""
try{
u = this.item.relationships.catalog.data[0].attributes.artwork.url}
catch (e){};
try {
u = this.item.relationships.catalog.data[0].attributes.artwork.url
}
catch (e) { };
return u;
},
contextMenu(event) {
@ -122,7 +125,7 @@
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playNext({[kind + "s"]: itemsToPlay[kind]})
app.mk.playNext({ [kind + "s"]: itemsToPlay[kind] })
}
}
console.log(itemsToPlay)
@ -143,7 +146,7 @@
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playLater({[kind + "s"]: itemsToPlay[kind]})
app.mk.playLater({ [kind + "s"]: itemsToPlay[kind] })
}
}
app.selectedMediaItems = []
@ -162,7 +165,7 @@
{
"name": "Start Radio",
"action": function () {
app.mk.setStationQueue({song: self.item.attributes.playParams.id ?? self.item.id}).then(() => {
app.mk.setStationQueue({ song: self.item.attributes.playParams.id ?? self.item.id }).then(() => {
app.mk.play()
app.selectedMediaItems = []
})
@ -171,7 +174,7 @@
{
"name": "Play Next",
"action": function () {
app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.playNext({ [self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id })
app.mk.queue._reindex()
app.selectedMediaItems = []
}
@ -179,7 +182,7 @@
{
"name": "Play Later",
"action": function () {
app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.playLater({ [self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id })
app.mk.queue._reindex()
app.selectedMediaItems = []
}
@ -227,7 +230,7 @@
},
async removeFromLibrary() {
let item = this.item
let params = {"fields[songs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library"}
let params = { "fields[songs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library" }
let id = item.id ?? item.attributes.playParams.id
let res = await app.mkapi(item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.playParams.id ?? item.id, params);
if (res && res.relationships && res.relationships.library && res.relationships.library.data && res.relationships.library.data.length > 0) {

View file

@ -45,7 +45,7 @@
},
data: function () {
return {
app:this.$root,
app: this.$root,
isVisible: false,
style: {
"box-shadow": ""
@ -58,9 +58,9 @@
},
methods: {
getVideoPriority() {
if(app.cfg.visual.animated_artwork == "always") {
if (app.cfg.visual.animated_artwork == "always") {
return true;
}else if (this.videoPriority && app.cfg.visual.animated_artwork == "limited") {
} else if (this.videoPriority && app.cfg.visual.animated_artwork == "limited") {
return true
} else if (app.cfg.visual.animated_artwork == "disabled") {
return false

View file

@ -76,14 +76,14 @@
}
},
props: {
'item': {type: Object, required: true},
'parent': {type: String, required: false},
'index': {type: Number, required: false, default: -1},
'show-artwork': {type: Boolean, default: true},
'show-library-status': {type: Boolean, default: true},
'show-meta-data': {type: Boolean, default: false},
'show-duration': {type: Boolean, default: true},
'contextExt': {type: Object, required: false},
'item': { type: Object, required: true },
'parent': { type: String, required: false },
'index': { type: Number, required: false, default: -1 },
'show-artwork': { type: Boolean, default: true },
'show-library-status': { type: Boolean, default: true },
'show-meta-data': { type: Boolean, default: false },
'show-duration': { type: Boolean, default: true },
'contextExt': { type: Object, required: false },
},
mounted() {
let duration = this.item.attributes.durationInMillis ?? 0
@ -215,7 +215,7 @@
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playNext({[kind + "s"]: itemsToPlay[kind]})
app.mk.playNext({ [kind + "s"]: itemsToPlay[kind] })
}
}
console.log(itemsToPlay)
@ -236,7 +236,7 @@
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playLater({[kind + "s"]: itemsToPlay[kind]})
app.mk.playLater({ [kind + "s"]: itemsToPlay[kind] })
}
}
app.selectedMediaItems = []
@ -256,7 +256,7 @@
{
"name": "Play Next",
"action": function () {
app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.playNext({ [self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id })
app.mk.queue._reindex()
app.selectedMediaItems = []
}
@ -264,7 +264,7 @@
{
"name": "Play Later",
"action": function () {
app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.playLater({ [self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id })
app.mk.queue._reindex()
app.selectedMediaItems = []
}
@ -273,7 +273,7 @@
"icon": "./assets/feather/radio.svg",
"name": "Start Radio",
"action": function () {
app.mk.setStationQueue({song: self.item.attributes.playParams.id ?? self.item.id}).then(() => {
app.mk.setStationQueue({ song: self.item.attributes.playParams.id ?? self.item.id }).then(() => {
app.mk.play()
app.selectedMediaItems = []
})
@ -348,19 +348,19 @@
menus.multiple.items = menus.multiple.items.concat(this.contextExt.multiple)
}
}
try{
let rating = await app.getRating(self.item).catch();
if (rating) {
if(rating == 0) {
menus.normal.items.find(x => x.id == 'love').disabled = false
menus.normal.items.find(x => x.id == 'dislike').disabled = false
}else if(rating == 1) {
menus.normal.items.find(x => x.id == 'unlove').disabled = false
}else if(rating == -1) {
menus.normal.items.find(x => x.id == 'undo_dislike').disabled = false
try {
let rating = await app.getRating(self.item).catch();
if (rating) {
if (rating == 0) {
menus.normal.items.find(x => x.id == 'love').disabled = false
menus.normal.items.find(x => x.id == 'dislike').disabled = false
} else if (rating == 1) {
menus.normal.items.find(x => x.id == 'unlove').disabled = false
} else if (rating == -1) {
menus.normal.items.find(x => x.id == 'undo_dislike').disabled = false
}
}
}
} catch(_){}
} catch (_) { }
CiderContextMenu.Create(event, menus[useMenu])
},
visibilityChanged: function (isVisible, entry) {
@ -380,7 +380,7 @@
},
async removeFromLibrary() {
let item = this.item
let params = {"fields[songs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library"}
let params = { "fields[songs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library" }
let id = item.id ?? item.attributes.playParams.id
let res = await app.mkapi(item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.playParams.id ?? item.id, params);
if (res && res.relationships && res.relationships.library && res.relationships.library.data && res.relationships.library.data.length > 0) {

View file

@ -18,4 +18,4 @@
},
methods: {}
});
</script>
</script>

View file

@ -122,7 +122,7 @@
}
let kind = this.item.attributes.playParams.kind ?? this.item.type ?? '';
var truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
app.mk.api.library.remove({[truekind]: id})
app.mk.api.library.remove({ [truekind]: id })
this.addedToLibrary = true
},
async contextMenu(event) {
@ -187,7 +187,7 @@
{
"name": "Play Next",
"action": function () {
app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.playNext({ [self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id })
app.mk.queue._reindex()
app.selectedMediaItems = []
}
@ -195,7 +195,7 @@
{
"name": "Play Later",
"action": function () {
app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.playLater({ [self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id })
app.mk.queue._reindex()
app.selectedMediaItems = []
}

View file

@ -85,7 +85,7 @@
return {
app: this.$root,
isVisible: true,
addedToLibrary : false,
addedToLibrary: false,
}
},
methods: {
@ -94,7 +94,7 @@
},
async isInLibrary() {
if (this.item.type && !this.item.type.includes("library")) {
var params = {"fields[playlists]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library", "extend": this.revisedRandId()}
var params = { "fields[playlists]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library", "extend": this.revisedRandId() }
var res = await app.mkapi(this.item.attributes.playParams.kind ?? this.item.type, this.item.attributes.playParams.isLibrary ?? false, this.item.attributes.playParams.id ?? this.item.id, params);
this.addedToLibrary = (res && res.attributes && res.attributes.inLibrary) ? res.attributes.inLibrary : false
} else {
@ -102,7 +102,7 @@
}
},
async removeFromLibrary(id) {
var params = {"fields[playlists]": "inLibrary","fields[songs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library", "extend": this.revisedRandId()}
var params = { "fields[playlists]": "inLibrary", "fields[songs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library", "extend": this.revisedRandId() }
var id = this.item.id ?? this.item.attributes.playParams.id
var res = await app.mkapi(this.item.attributes.playParams.kind ?? this.item.type, this.item.attributes.playParams.isLibrary ?? false, this.item.attributes.playParams.id ?? this.item.id, params);
if (res && res.relationships && res.relationships.library && res.relationships.library.data && res.relationships.library.data.length > 0) {
@ -110,11 +110,11 @@
}
let kind = this.item.attributes.playParams.kind ?? this.item.type ?? '';
var truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
app.mk.api.library.remove({[truekind]: id})
app.mk.api.library.remove({ [truekind]: id })
this.addedToLibrary = true
},
subtitleSearchNavigate(item) {
if((item.attributes.editorialNotes == null) && item.attributes.artistName)app.searchAndNavigate(item,'artist')
if ((item.attributes.editorialNotes == null) && item.attributes.artistName) app.searchAndNavigate(item, 'artist')
},
clickContext() {
var evt = document.createEvent('MouseEvent');
@ -133,7 +133,7 @@
visibilityChanged: function (isVisible, entry) {
this.isVisible = isVisible
},
async contextMenu(event) {
async contextMenu(event) {
if (!event) {
event = this.$refs.main
} else {
@ -165,7 +165,7 @@
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playNext({[kind + "s"]: itemsToPlay[kind]})
app.mk.playNext({ [kind + "s"]: itemsToPlay[kind] })
}
}
console.log(itemsToPlay)
@ -186,7 +186,7 @@
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playLater({[kind + "s"]: itemsToPlay[kind]})
app.mk.playLater({ [kind + "s"]: itemsToPlay[kind] })
}
}
app.selectedMediaItems = []
@ -200,7 +200,7 @@
{
"name": "Play Next",
"action": function () {
app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.playNext({ [self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id })
app.mk.queue._reindex()
app.selectedMediaItems = []
}
@ -208,7 +208,7 @@
{
"name": "Play Later",
"action": function () {
app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.playLater({ [self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id })
app.mk.queue._reindex()
app.selectedMediaItems = []
}
@ -253,12 +253,12 @@
}
},
{
"name": (this.addedToLibrary) ? "Remove from Library..." : "Add to Library...",
"name": (this.addedToLibrary) ? "Remove from Library..." : "Add to Library...",
"action": async function () {
let item_id = self.item.attributes.playParams.id ?? self.item.id;
let data_type = self.item.attributes.playParams.kind ?? self.item.type;
if (self.addedToLibrary != true) { console.log("add"); app.addToLibrary(item_id); self.addedToLibrary = true}
else { console.log("remove"); await self.removeFromLibrary(item_id); self.addedToLibrary = false};
if (self.addedToLibrary != true) { console.log("add"); app.addToLibrary(item_id); self.addedToLibrary = true }
else { console.log("remove"); await self.removeFromLibrary(item_id); self.addedToLibrary = false };
}
},
@ -266,12 +266,12 @@
}
}
let rating = await app.getRating(self.item)
if(rating == 0) {
if (rating == 0) {
menus.normal.items.find(x => x.id == 'love').disabled = false
menus.normal.items.find(x => x.id == 'dislike').disabled = false
}else if(rating == 1) {
} else if (rating == 1) {
menus.normal.items.find(x => x.id == 'unlove').disabled = false
}else if(rating == -1) {
} else if (rating == -1) {
menus.normal.items.find(x => x.id == 'undo_dislike').disabled = false
}
if ((self.item.attributes.playParams.kind ?? self.item.type).includes("playlist")) {

View file

@ -57,7 +57,7 @@
type: String,
default: '190'
},
'contextExt': {type: Object, required: false},
'contextExt': { type: Object, required: false },
},
data: function () {
return {
@ -80,8 +80,8 @@
let c = color.substring(1); // strip #
var rgb = parseInt(c, 16); // convert rrggbb to decimal
var r = (rgb >> 16) & 0xff; // extract red
var g = (rgb >> 8) & 0xff; // extract green
var b = (rgb >> 0) & 0xff; // extract blue
var g = (rgb >> 8) & 0xff; // extract green
var b = (rgb >> 0) & 0xff; // extract blue
var luma = 0.2126 * r + 0.7152 * g + 0.0722 * b; // per ITU-R BT.709
@ -89,14 +89,14 @@
console.log(luma)
if (luma > 140) {
return "#aaaaaa"
}else{
} else {
return color
}
},
getSubtitle() {
if(this.kind == 'card') {
if (this.kind == 'card') {
try {
if (typeof this.item.attributes.artistNames != "undefined") {
return this.item.attributes.artistNames
@ -107,10 +107,10 @@
} else {
return ''
}
}catch(e) {
} catch (e) {
return ''
}
}else {
} else {
if (typeof this.item.attributes.artistName != "undefined") {
return this.item.attributes.artistName
} else {
@ -119,23 +119,23 @@
}
},
getSubtitleNavigation() {
if(this.kind == 'card') {
if (this.kind == 'card') {
try {
if (typeof this.item.attributes.artistNames != "undefined") {
return app.routeView(this.item)
} else if (typeof this.item.attributes.editorialNotes != "undefined") {
return app.routeView(this.item)
} else if (typeof this.item.attributes.artistName != "undefined") {
return app.searchAndNavigate(this.item,'artist')
return app.searchAndNavigate(this.item, 'artist')
} else {
return app.routeView(this.item)
}
}catch(e) {
} catch (e) {
return app.routeView(this.item)
}
}else {
} else {
if (typeof this.item.attributes.artistName != "undefined") {
return app.searchAndNavigate(this.item,'artist')
return app.searchAndNavigate(this.item, 'artist')
} else {
return app.routeView(this.item)
}
@ -186,7 +186,7 @@
}
let kind = this.item.attributes.playParams.kind ?? this.item.type ?? '';
var truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
app.mk.api.library.remove({[truekind]: id})
app.mk.api.library.remove({ [truekind]: id })
this.addedToLibrary = true
},
uuidv4() {
@ -196,7 +196,7 @@
},
getArtworkUrl(size = -1, includeUrl = false) {
let artwork = this.item.attributes.artwork ? this.item.attributes.artwork.url : ''
if(size != -1) {
if (size != -1) {
artwork = artwork.replace('{w}', size).replace('{h}', size).replace('{f}', "webp").replace('{c}', ((size === 900) ? "sr" : "cc"))
}
switch (this.kind) {
@ -204,9 +204,9 @@
artwork = this.item.attributes.editorialArtwork.subscriptionHero.url
break;
}
if(!includeUrl) {
if (!includeUrl) {
return artwork
}else{
} else {
return `url("${artwork}")`
}
},
@ -273,7 +273,7 @@
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playNext({[kind + "s"]: itemsToPlay[kind]})
app.mk.playNext({ [kind + "s"]: itemsToPlay[kind] })
}
}
console.log(itemsToPlay)
@ -294,7 +294,7 @@
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playLater({[kind + "s"]: itemsToPlay[kind]})
app.mk.playLater({ [kind + "s"]: itemsToPlay[kind] })
}
}
app.selectedMediaItems = []
@ -315,7 +315,7 @@
{
"name": "Play Next",
"action": function () {
app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.playNext({ [self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id })
app.mk.queue._reindex()
app.selectedMediaItems = []
}
@ -323,7 +323,7 @@
{
"name": "Play Later",
"action": function () {
app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.playLater({ [self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id })
app.mk.queue._reindex()
app.selectedMediaItems = []
}

View file

@ -50,7 +50,7 @@
items: [{
"name": "Remove from queue",
"action": function () {
}
}]
});

View file

@ -57,8 +57,8 @@
},
methods: {
select(e, position) {
if(e.ctrlKey || e.shiftKey) {
if(this.selectedItems.indexOf(position) == -1) {
if (e.ctrlKey || e.shiftKey) {
if (this.selectedItems.indexOf(position) == -1) {
this.selectedItems.push(position)
} else {
this.selectedItems.splice(this.selectedItems.indexOf(position), 1)
@ -70,7 +70,7 @@
queueContext(event, item, position) {
let self = this
let useMenu = "single"
if(this.selectedItems.length > 1) {
if (this.selectedItems.length > 1) {
useMenu = "multiple"
}
let menus = {
@ -83,16 +83,16 @@
app.mk.queue._reindex()
}
},
{
"name": "Start Radio",
"action": function () {
app.mk.setStationQueue({
song: item.attributes.playParams.id ?? item.id
}).then(() => {
app.mk.play()
})
}
},
{
"name": "Start Radio",
"action": function () {
app.mk.setStationQueue({
song: item.attributes.playParams.id ?? item.id
}).then(() => {
app.mk.play()
})
}
},
]
},
multiple: {

View file

@ -64,9 +64,9 @@
rename() {
this.renaming = false
if(this.item.type === "library-playlist-folders") {
if (this.item.type === "library-playlist-folders") {
this.$root.editPlaylistFolder(this.item.id, this.item.attributes.name)
}else{
} else {
this.$root.editPlaylist(this.item.id, this.item.attributes.name)
}
},
@ -74,7 +74,7 @@
let self = this
this.children = []
this.children = this.$root.playlists.listing.filter(child => {
if(child.parent == self.item.id) {
if (child.parent == self.item.id) {
return child
}
})
@ -98,13 +98,13 @@
// find the item in this.$root.playlists.listing and store it in a variable
this.$root.playlists.listing.filter(playlist => {
if(playlist.id == item.id) {
if (playlist.id == item.id) {
console.log(playlist)
playlist.parent = sendTo.id
}
})
if(typeof this.$parent.getChildren == "function") {
if (typeof this.$parent.getChildren == "function") {
this.$parent.getChildren()
console.log(this.$parent.children)
}
@ -123,14 +123,14 @@
id: this.playlistRoot,
type: "library-playlist-folders"
})
setTimeout(()=>{self.getChildren()}, 2000)
setTimeout(() => { self.getChildren() }, 2000)
}
},
"rename": {
name: "Rename",
action: () => {
this.renaming = true
setTimeout(()=>{
setTimeout(() => {
document.querySelector(".pl-rename-field").focus()
document.querySelector(".pl-rename-field").select()
}, 100)
@ -151,7 +151,7 @@
}
}
}
if(this.item.type === "library-playlist-folders") {
if (this.item.type === "library-playlist-folders") {
menu.items.addToFavorites.disabled = true
}
CiderContextMenu.Create(event, menu)
@ -160,23 +160,23 @@
evt.preventDefault();
evt.dataTransfer.dropEffect = "move";
},
onDrop (evt) {
onDrop(evt) {
let data = JSON.parse(evt.dataTransfer.getData("text/plain"))
evt.preventDefault();
if(data.id == this.item.id) {
if (data.id == this.item.id) {
return;
}
console.log(data)
if(data) {
if(this.item.type == "library-playlists" || this.item.type == "library-playlist-folders") {
if(data.type == "library-playlists" && this.item.type == "library-playlists") {
if (data) {
if (this.item.type == "library-playlists" || this.item.type == "library-playlist-folders") {
if (data.type == "library-playlists" && this.item.type == "library-playlists") {
return
}
this.move(data, this.item)
}
}
},
startDrag (evt) {
startDrag(evt) {
evt.dataTransfer.dropEffect = 'move'
evt.dataTransfer.effectAllowed = 'move'
evt.dataTransfer.setData('text/plain', JSON.stringify(this.item))
@ -188,7 +188,7 @@
this.toggleFolder()
this.$root.mk.api.library.playlistFolderChildren(item.id).then(children => {
children.forEach(child => {
if(!self.$root.playlists.listing.find(listing => listing.id == child.id)) {
if (!self.$root.playlists.listing.find(listing => listing.id == child.id)) {
child.parent = self.item.id
self.$root.playlists.listing.push(child)
}
@ -207,9 +207,9 @@
})
},
isPlaylistSelected(item) {
if(this.$root.showingPlaylist.id == item.id) {
if (this.$root.showingPlaylist.id == item.id) {
return ["active"]
} else {
} else {
return []
}
},

View file

@ -294,7 +294,7 @@
this.room_materials = JSON.parse(JSON.stringify(this.$root.cfg.audio.spatial_properties.room_materials))
this.audio_position = JSON.parse(JSON.stringify(this.$root.cfg.audio.spatial_properties.audio_position))
this.listener_position = JSON.parse(JSON.stringify(this.$root.cfg.audio.spatial_properties.listener_position))
if(typeof this.app.mk.nowPlayingItem != "undefined") {
if (typeof this.app.mk.nowPlayingItem != "undefined") {
this.setRoom()
}
this.ready = true
@ -320,7 +320,7 @@
},
objectContainerStyle() {
let scale = 1
if(this.room_dimensions.width * this.visualMultiplier > 300) {
if (this.room_dimensions.width * this.visualMultiplier > 300) {
scale = 300 / (this.room_dimensions.width * this.visualMultiplier)
}
let style = {
@ -346,7 +346,7 @@
window.CiderAudio.audioNodes.spatialNode.setRoomProperties(this.room_dimensions, this.room_materials);
CiderAudio.audioNodes.spatialInput.setPosition(...this.audio_position)
CiderAudio.audioNodes.spatialNode.setListenerPosition(...this.listener_position)
if(!this.app.cfg.audio.normalization) {
if (!this.app.cfg.audio.normalization) {
window.CiderAudio.audioNodes.gainNode.gain.value = app.cfg.audio.spatial_properties.gain
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,7 @@
Major thanks to the Cider Development Team and all of our contributors.
<p>"Apple Music" - Copyright © 2021 <a href="https://www.apple.com/" class="dt-footer__link"
target="_blank"
rel="noopener" data-dt-link-to-exclude="">Apple Inc.</a>
<p>"Apple Music" - Copyright © 2021 <a href="https://www.apple.com/" class="dt-footer__link" target="_blank"
rel="noopener" data-dt-link-to-exclude="">Apple Inc.</a>
All Rights
Reserved.</p>
@ -14,6 +13,5 @@ vapormusic - Developer - https://github.com/vapormusic
Void - Social Communications Team - https://twitter.com/MoonyVoid
NoseySG - Social Communications Team - https://twitter.com/noah_grose
<img class="md-contributors"
onclick="window.open('https://github.com/ciderapp/Cider/graphs/contributors')"
src="https://contrib.rocks/image?repo=ciderapp/Cider"/>
<img class="md-contributors" onclick="window.open('https://github.com/ciderapp/Cider/graphs/contributors')"
src="https://contrib.rocks/image?repo=ciderapp/Cider" />

View file

@ -33,7 +33,7 @@
Vue.component('cider-applecurator', {
template: "#cider-applecurator",
props: ["data"],
data: function() {
data: function () {
return {
app: this.$root
}

View file

@ -48,7 +48,7 @@
"fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url,trackCount",
"limit[artists:top-songs]": 20,
"art[url]": "f"
}, {includeResponseMeta: !0}).then(artistData => {
}, { includeResponseMeta: !0 }).then(artistData => {
artistData.data.forEach(item => {
if (item.views["latest-release"].data.length != 0) {
self.artistFeed.push(item.views["latest-release"].data[0])

View file

@ -133,19 +133,19 @@
}
},
methods: {
artistMenu (event) {
artistMenu(event) {
let self = this
let followAction = "follow"
let followActions = {
follow: {
name: "Follow Artist",
action: ()=>{
action: () => {
self.app.cfg.home.followedArtists.push(self.data.id)
}
},
unfollow: {
name: "Unfollow Artist",
action: ()=>{
action: () => {
let index = self.app.cfg.home.followedArtists.indexOf(self.data.id)
if (index > -1) {
self.app.cfg.home.followedArtists.splice(index, 1)
@ -153,15 +153,15 @@
}
}
}
if(this.app.cfg.home.followedArtists.includes(self.data.id)) {
if (this.app.cfg.home.followedArtists.includes(self.data.id)) {
followAction = "unfollow"
}
CiderContextMenu.Create(event, {
items: [
{
name: "Play Artist Radio",
action: ()=>{
app.mk.setStationQueue({artist:self.data.id}).then(()=>{
action: () => {
app.mk.setStationQueue({ artist: self.data.id }).then(() => {
app.mk.play()
})
}
@ -169,7 +169,7 @@
followActions[followAction],
{
name: "Share",
action: ()=>{
action: () => {
self.app.copyToClipboard(self.data.attributes.url)
}
}

View file

@ -138,7 +138,7 @@
methods: {
getBadges() {
return
if(this.badgesRequested) {
if (this.badgesRequested) {
return
}
this.badgesRequested = true
@ -152,7 +152,7 @@
}
this.$root.getSocialBadges((badges) => {
let friends = badges[id]
if(friends) {
if (friends) {
friends.forEach(function (friend) {
self.app.mk.api.socialProfile(friend).then(data => {
self.itemBadges.push(data)
@ -164,7 +164,7 @@
async isInLibrary() {
if (this.data.type && !this.data.type.includes("library")) {
// please keep using vars here
var params = {"fields[playlists]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library"}
var params = { "fields[playlists]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library" }
var res = await app.mkapi(this.data.attributes.playParams.kind ?? this.data.type, this.data.attributes.playParams.isLibrary ?? false, this.data.attributes.playParams.id ?? this.data.id, params);
this.inLibrary = (res && res.attributes && res.attributes.inLibrary) ? res.attributes.inLibrary : false
console.log(res)
@ -186,7 +186,7 @@
this.inLibrary = true
},
async removeFromLibrary(id) {
var params = {"fields[somgs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library"}
var params = { "fields[somgs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library" }
var id = this.data.id ?? this.data.attributes.playParams.id
var res = await app.mkapi(this.data.attributes.playParams.kind ?? this.data.type, this.data.attributes.playParams.isLibrary ?? false, this.data.attributes.playParams.id ?? this.data.id, params);
if (res && res.relationships && res.relationships.library && res.relationships.library.data && res.relationships.library.data.length > 0) {
@ -194,7 +194,7 @@
}
let kind = this.data.attributes.playParams.kind ?? this.data.type ?? '';
var truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
app.mk.api.library.remove({[truekind]: id})
app.mk.api.library.remove({ [truekind]: id })
this.inLibrary = false
},
editPlaylistName() {
@ -276,7 +276,7 @@
if (date == null || date === "") return "";
try {
var releaseDate = new Date(date);
month = new Intl.DateTimeFormat('en-US', {month: 'long'}).format(releaseDate);
month = new Intl.DateTimeFormat('en-US', { month: 'long' }).format(releaseDate);
date = releaseDate.getDate();
year = releaseDate.getFullYear();

View file

@ -52,11 +52,11 @@
},
methods: {
getKind(item) {
if(typeof item.kind != "undefined") {
if (typeof item.kind != "undefined") {
this.commonKind = item.kind;
return item.kind
}
if(typeof item.attributes.playParams != "undefined") {
if (typeof item.attributes.playParams != "undefined") {
this.commonKind = item.attributes.playParams.kind
return item.attributes.playParams.kind
}
@ -72,7 +72,7 @@
},
getNext() {
// if this.data.next is not null, then we can run this.data.next() and concat to this.data.data to get the next page
switch(this.type) {
switch (this.type) {
default:
case "artists":
if (this.data.next && this.triggerEnabled) {
@ -84,12 +84,12 @@
this.data.data = this.data.data.concat(data.data);
this.triggerEnabled = true;
});
if(typeof this.data.next == "function") {
if (typeof this.data.next == "function") {
this.data.next().then(data => nextFn(data));
}else{
} else {
this.api.v3.music(this.data.next).then(data => nextFn(data));
}
}else{
} else {
console.log("No next page");
this.triggerEnabled = false;
}
@ -103,14 +103,14 @@
this.data.data = this.data.data.concat(data[this.data.groups].data.data);
this.triggerEnabled = true;
});
}else{
} else {
console.log("No next page");
this.triggerEnabled = false;
}
break;
case "listen_now":
case "listen_now":
case "curator":
if (this.data.next && this.triggerEnabled) {
if (this.data.next && this.triggerEnabled) {
this.triggerEnabled = false;
app.mk.api.v3.music(this.data.next).then(data => {
console.log(data);
@ -118,26 +118,26 @@
this.data.data = this.data.data.concat(data.data.data);
this.triggerEnabled = true;
});
}else{
} else {
console.log("No next page");
this.triggerEnabled = false;
}
break;
break;
}
},
headerVisibility: function (isVisible, entry) {
if(isVisible) {
if (isVisible) {
this.showFab = false;
}else{
} else {
this.showFab = true;
}
},
visibilityChanged: function (isVisible, entry) {
if(isVisible) {
if (isVisible) {
this.canSeeTrigger = true;
this.getNext();
}else{
} else {
this.canSeeTrigger = false;
}
}

View file

@ -73,7 +73,7 @@
<script>
Vue.component('cider-home', {
template: '#cider-home',
data: function() {
data: function () {
return {
app: this.$root,
followedArtists: this.$root.cfg.home.followedArtists,
@ -105,7 +105,7 @@
let self = this
return {
name: "Remove from Favorites",
action: function(item) {
action: function (item) {
let index = self.favoriteItems.findIndex(x => x.id == item.id)
if (index > -1) {
self.favoriteItems.splice(index, 1)
@ -151,11 +151,11 @@
includeResponseMeta: !0
}).then(artistData => {
artistData.data.forEach(item => {
if (item.views["latest-release"].data.length != 0) {
self.artistFeed.push(item.views["latest-release"].data[0])
}
})
// sort artistFeed by attributes.releaseDate descending, date is formatted as "YYYY-MM-DD"
if (item.views["latest-release"].data.length != 0) {
self.artistFeed.push(item.views["latest-release"].data[0])
}
})
// sort artistFeed by attributes.releaseDate descending, date is formatted as "YYYY-MM-DD"
this.artistFeed.sort((a, b) => {
let dateA = new Date(a.attributes.releaseDate)
let dateB = new Date(b.attributes.releaseDate)
@ -203,7 +203,7 @@
return section
};
})[0].relationships.contents.data
} catch (err) {}
} catch (err) { }
self.sectionsReady.push("madeForYou")
try {
@ -213,7 +213,7 @@
return section
};
})[0].relationships.contents.data
} catch (err) {}
} catch (err) { }
self.sectionsReady.push("recentlyPlayed")
self.sectionsReady.push("friendsListeningTo")
});

View file

@ -5,19 +5,18 @@
<h1 class="header-text">Albums</h1>
</div>
<div class="col-auto">
<button v-if="library.albums.downloadState == 2" @click="getLibraryAlbumsFull(true, 1)" class="reload-btn"><%- include('../svg/redo.svg') %></button>
<button v-if="library.albums.downloadState == 2" @click="getLibraryAlbumsFull(true, 1)"
class="reload-btn">
<%- include('../svg/redo.svg') %>
</button>
</div>
</div>
<div class="row">
<div class="col" style="padding:0px;">
<div class="search-input-container" style="width:100%;margin: 16px 0px;">
<div class="search-input--icon"></div>
<input type="search"
style="width:100%;"
spellcheck="false"
placeholder="Search..."
@input="searchLibraryAlbums"
v-model="library.albums.search" class="search-input">
<input type="search" style="width:100%;" spellcheck="false" placeholder="Search..."
@input="searchLibraryAlbums" v-model="library.albums.search" class="search-input">
</div>
</div>
<div class="col-auto flex-center">
@ -25,12 +24,14 @@
<div class="col">
<select class="md-select" v-model="library.albums.sorting[1]" @change="searchLibraryAlbums(1)">
<optgroup label="Sort By">
<option v-for="(sort, index) in library.albums.sortingOptions" :value="index">{{ sort }}</option>
<option v-for="(sort, index) in library.albums.sortingOptions" :value="index">{{ sort }}
</option>
</optgroup>
</select>
</div>
<div class="col">
<select class="md-select" v-model="library.albums.sortOrder[1]" @change="searchLibraryAlbums(1)">
<select class="md-select" v-model="library.albums.sortOrder[1]"
@change="searchLibraryAlbums(1)">
<optgroup label="Sort Order">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
@ -49,13 +50,15 @@
</div>
</div>
<div class="well">
<div class="albums-square-container">
<div>
<mediaitem-square v-if="library.albums.viewAs == 'covers'" :size="'150'" :item="item" v-for="item in library.albums.displayListing">
<div class="albums-square-container">
<div>
<mediaitem-square v-if="library.albums.viewAs == 'covers'" :size="'150'" :item="item"
v-for="item in library.albums.displayListing">
</mediaitem-square>
</div>
</div>
</div>
<mediaitem-list-item v-if="library.albums.viewAs == 'list'" :show-duration="false" :show-meta-data="true" :show-library-status="false" :item="item" v-for="item in library.albums.displayListing">
<mediaitem-list-item v-if="library.albums.viewAs == 'list'" :show-duration="false" :show-meta-data="true"
:show-library-status="false" :item="item" v-for="item in library.albums.displayListing">
</mediaitem-list-item>
</div>
</div>

View file

@ -1,40 +1,36 @@
<div class="content-inner">
<div class="row">
<div class="col" style="padding:0px;">
<h1 class="header-text">Artists</h1>
</div>
<div class="content-inner">
<div class="row">
<div class="col" style="padding:0px;">
<h1 class="header-text">Artists</h1>
</div>
<div class="row">
<div class="col" style="padding:0px;">
<div class="search-input-container" style="width:100%;margin: 16px 0px;">
<div class="search-input--icon"></div>
<input type="search"
style="width:100%;"
spellcheck="false"
placeholder="Search..."
@input="searchLibraryArtists"
v-model="library.artists.search" class="search-input">
</div>
</div>
<div class="row">
<div class="col" style="padding:0px;">
<div class="search-input-container" style="width:100%;margin: 16px 0px;">
<div class="search-input--icon"></div>
<input type="search" style="width:100%;" spellcheck="false" placeholder="Search..."
@input="searchLibraryArtists" v-model="library.artists.search" class="search-input">
</div>
<div class="col-auto flex-center">
<div class="row">
<!-- <div class="col">
</div>
<div class="col-auto flex-center">
<div class="row">
<!-- <div class="col">
<select class="md-select" v-model="library.artists.sorting[1]" @change="searchLibraryArtists(1)">
<optgroup label="Sort By">
<option v-for="(sort, index) in library.artists.sortingOptions" :value="index">{{ sort }}</option>
</optgroup>
</select>
</div> -->
<div class="col">
<select class="md-select" v-model="library.artists.sortOrder[1]" @change="searchLibraryArtists(1)">
<optgroup label="Sort Order">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</optgroup>
</select>
</div>
<!-- <div class="col">
<div class="col">
<select class="md-select" v-model="library.artists.sortOrder[1]" @change="searchLibraryArtists(1)">
<optgroup label="Sort Order">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</optgroup>
</select>
</div>
<!-- <div class="col">
<select class="md-select" v-model="library.artists.viewAs">
<optgroup label="View As">
<option value="covers">Cover Art</option>
@ -42,13 +38,14 @@
</optgroup>
</select>
</div> -->
</div>
</div>
</div>
<div class="well">
<!-- <mediaitem-square v-if="library.artists.viewAs == 'covers'" :item="item" v-for="item in library.artists.displayListing">
</div>
<div class="well">
<!-- <mediaitem-square v-if="library.artists.viewAs == 'covers'" :item="item" v-for="item in library.artists.displayListing">
</mediaitem-square> -->
<libraryartist-item :show-duration="false" :show-meta-data="true" :show-library-status="false" :item="item" v-for="item in library.artists.displayListing">
</libraryartist-item>
</div>
</div>
<libraryartist-item :show-duration="false" :show-meta-data="true" :show-library-status="false" :item="item"
v-for="item in library.artists.displayListing">
</libraryartist-item>
</div>
</div>

View file

@ -6,26 +6,24 @@
</div>
<div class="col-auto">
<button v-if="library.albums.downloadState == 2" @click="getLibraryAlbumsFull(true, 0)"
class="reload-btn"><%- include('../svg/redo.svg') %></button>
class="reload-btn">
<%- include('../svg/redo.svg') %>
</button>
</div>
</div>
<div class="row">
<div class="col" style="padding:0px;">
<div class="search-input-container" style="width:100%;margin: 16px 0px;">
<div class="search-input--icon"></div>
<input type="search"
style="width:100%;"
spellcheck="false"
placeholder="Search..."
@input="searchLibraryAlbums"
v-model="library.albums.search" class="search-input">
<input type="search" style="width:100%;" spellcheck="false" placeholder="Search..."
@input="searchLibraryAlbums" v-model="library.albums.search" class="search-input">
</div>
</div>
<div class="col-auto flex-center">
<div class="row">
<div class="col">
<select class="md-select" v-model="library.albums.sortOrder[0]"
@change="searchLibraryAlbums(0)">
@change="searchLibraryAlbums(0)">
<optgroup label="Sort Order">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
@ -45,11 +43,10 @@
</div>
<div class="well">
<mediaitem-square v-if="library.albums.viewAs == 'covers'" :item="item"
v-for="item in library.albums.displayListing">
v-for="item in library.albums.displayListing">
</mediaitem-square>
<mediaitem-list-item v-if="library.albums.viewAs == 'list'" :show-duration="false" :show-meta-data="true"
:show-library-status="false" :item="item"
v-for="item in library.albums.displayListing">
:show-library-status="false" :item="item" v-for="item in library.albums.displayListing">
</mediaitem-list-item>
</div>
</div>

View file

@ -5,19 +5,17 @@
<h1 class="header-text">Songs</h1>
</div>
<div class="col-auto">
<button v-if="library.songs.downloadState == 2" @click="getLibrarySongsFull(true)" class="reload-btn"><%- include('../svg/redo.svg') %></button>
<button v-if="library.songs.downloadState == 2" @click="getLibrarySongsFull(true)" class="reload-btn">
<%- include('../svg/redo.svg') %>
</button>
</div>
</div>
<div class="row">
<div class="col" style="padding:0px;">
<div class="search-input-container" style="width:100%;margin: 16px 0px;">
<div class="search-input--icon"></div>
<input type="search"
style="width:100%;"
spellcheck="false"
placeholder="Search..."
@input="searchLibrarySongs"
v-model="library.songs.search" class="search-input">
<input type="search" style="width:100%;" spellcheck="false" placeholder="Search..."
@input="searchLibrarySongs" v-model="library.songs.search" class="search-input">
</div>
</div>
<div class="col-auto flex-center">
@ -25,7 +23,8 @@
<div class="col">
<select class="md-select" v-model="library.songs.sorting" @change="searchLibrarySongs()">
<optgroup label="Sort By">
<option v-for="(sort, index) in library.songs.sortingOptions" :value="index">{{ sort }}</option>
<option v-for="(sort, index) in library.songs.sortingOptions" :value="index">{{ sort }}
</option>
</optgroup>
</select>
</div>
@ -41,6 +40,7 @@
</div>
</div>
<div v-if="library.songs.downloadState == 3">Library contains no songs.</div>
<mediaitem-list-item :item="item" :parent="'librarysongs'" :index="index" :show-meta-data="true" :show-library-status="false" v-for="(item, index) in library.songs.displayListing"></mediaitem-list-item>
<mediaitem-list-item :item="item" :parent="'librarysongs'" :index="index" :show-meta-data="true"
:show-library-status="false" v-for="(item, index) in library.songs.displayListing"></mediaitem-list-item>
</div>
</template>

View file

@ -8,8 +8,8 @@
</script>
<script>
Vue.component('cider-listen-now', {
template: "#cider-listen-now",
props: ["data"]
})
</script>
Vue.component('cider-listen-now', {
template: "#cider-listen-now",
props: ["data"]
})
</script>

View file

@ -1,11 +1,11 @@
<div class="content-inner">
<div class="row">
<div class="col" style="padding:0px">
<h1 class="header-text">Made For You</h1>
</div>
</div>
<div class="madeforyou-body">
<mediaitem-square :item="item" v-for="item in madeforyou.data">
</mediaitem-square>
</div>
</div>
<div class="content-inner">
<div class="row">
<div class="col" style="padding:0px">
<h1 class="header-text">Made For You</h1>
</div>
</div>
<div class="madeforyou-body">
<mediaitem-square :item="item" v-for="item in madeforyou.data">
</mediaitem-square>
</div>
</div>

View file

@ -564,37 +564,39 @@
}
},
mounted: function () {
if (app.cfg.lastfm.enabled){
if (app.cfg.lastfm.enabled) {
const element = document.getElementById('lfmConnect');
if (element){
element.innerHTML = `Disconnect\n<p style="font-size: 8px"><i>(Authed: ${app.cfg.lastfm.auth_token})</i></p>`;
element.onclick = app.LastFMDeauthorize;
if (element) {
element.innerHTML = `Disconnect\n<p style="font-size: 8px"><i>(Authed: ${app.cfg.lastfm.auth_token})</i></p>`;
element.onclick = app.LastFMDeauthorize;
}
}
},
methods: {
toggleAudioContext: function(){
if (app.cfg.advanced.AudioContext){
toggleAudioContext: function () {
if (app.cfg.advanced.AudioContext) {
CiderAudio.init();
if (app.cfg.audio.normalization){
CiderAudio.normalizerOn()}
if (app.cfg.audio.spatial){
CiderAudio.spatialOn()}
if (app.cfg.audio.normalization) {
CiderAudio.normalizerOn()
}
if (app.cfg.audio.spatial) {
CiderAudio.spatialOn()
}
} else {
CiderAudio.off();
}
},
toggleNormalization : function(){
if (app.cfg.audio.normalization){
toggleNormalization: function () {
if (app.cfg.audio.normalization) {
CiderAudio.normalizerOn()
} else {CiderAudio.normalizerOff()}
} else { CiderAudio.normalizerOff() }
},
toggleSpatial : function(){
if (app.cfg.audio.spatial){
toggleSpatial: function () {
if (app.cfg.audio.spatial) {
CiderAudio.spatialOn()
} else {CiderAudio.spatialOff()}
} else { CiderAudio.spatialOff() }
},
changeAudioQuality : function(){
changeAudioQuality: function () {
app.mk.bitrate = app.cfg.audio.quality
}
}

View file

@ -2,4 +2,4 @@
<div style="display:flex;width:100%;height:100%">
<webview id="foo" :src="webview.url" style="display:inline-flex; width:100%;"></webview>
</div>
</template>
</template>

View file

@ -6,4 +6,4 @@
<div class="spinner"></div>
<button class="md-btn">Cider Button</button>
</div>
</template>
</template>

View file

@ -2,39 +2,40 @@ module.exports = {
globDirectory: 'src/renderer/',
swDest: 'src/renderer/sw.js',
// Define runtime caching rules.
runtimeCaching: [{
// Match any request that ends with .png, .jpg, .jpeg or .svg.
urlPattern: /\.(?:png|jpg|jpeg|svg|webp)$/,
runtimeCaching: [
{
// Match any request that ends with .png, .jpg, .jpeg or .svg.
urlPattern: /\.(?:png|jpg|jpeg|svg|webp)$/,
// Apply a cache-first strategy.
handler: 'CacheFirst',
// Apply a cache-first strategy.
handler: 'CacheFirst',
options: {
// Use a custom cache name.
cacheName: 'imageinternet',
// Only cache 10 images.
options: {
// Use a custom cache name.
cacheName: 'imageinternet'
// Only cache 10 images.
}
},
},
{
urlPattern: /https:\/\/amp-api.music.apple.com\/v1\//,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'amp-api',
cacheableResponse: {
statuses: [0, 200],
},
},
statuses: [0, 200]
}
}
},
{
urlPattern: /https:\/\/is[0-9]-ssl\.mzstatic\.com\/image+/,
handler: "CacheFirst",
handler: 'CacheFirst'
},
{
urlPattern: /^https:\/\/store-\d{3}\.blobstore\.apple\.com\/.{65}\/image+/,
handler: "CacheFirst",
},
urlPattern:
/^https:\/\/store-\d{3}\.blobstore\.apple\.com\/.{65}\/image+/,
handler: 'CacheFirst'
}
],
ignoreURLParametersMatching: [
/^utm_/,
@ -44,6 +45,6 @@ module.exports = {
/^X-Amz-SignedHeaders/,
/^X-Amz-Expires/,
/^X-Amz-Credential/,
/^X-Amz-Signature/,
/^X-Amz-Signature/
]
};