Merge branch 'develop'
This commit is contained in:
commit
ea3d67b8b4
10 changed files with 197 additions and 1032 deletions
|
@ -7,4 +7,13 @@ Some notes about Cider's i18n support.
|
|||
- The default language is used for messages that are not translated.
|
||||
- Try when possible to keep the messages the similar in length to the English ones.
|
||||
- Most of the strings in the content area are provided and translated by Apple themselves, and do not need to be translated.
|
||||
- The language Apple Music uses are dependent on the storefront region.
|
||||
- The language Apple Music uses are dependent on the storefront region.
|
||||
|
||||
|
||||
## Localization Notices
|
||||
|
||||
Several changes have been made to configuration options and will be listed below with the relevant locales that have
|
||||
been modified, the ones not mentioned in the list need modifying.
|
||||
|
||||
* `settings.option.experimental.closeButtonBehaviour`: Changed to `close_button_hide` - Should be "Close Button Should Hide the Application". `.quit`, `.minimizeTaskbar` and `.minimizeTray` have been removed. Translations done for en_US.
|
||||
* `term.loadingPlaylist`: Added for `en_US` and `en_PISS`.
|
|
@ -274,10 +274,7 @@
|
|||
"settings.header.experimental": "Experimental",
|
||||
"settings.header.experimental.description": "Adjust the experimental settings for Cider.",
|
||||
"settings.option.experimental.compactUI": "Compact UI", // Toggle
|
||||
"settings.option.experimental.closeButtonBehaviour": "Close Button Behavior",
|
||||
"settings.option.experimental.closeButtonBehaviour.quit": "Quit Cider",
|
||||
"settings.option.experimental.closeButtonBehaviour.minimizeTaskbar": "Minimize to Taskbar",
|
||||
"settings.option.experimental.closeButtonBehaviour.minimizeTray": "Minimize to Tray",
|
||||
"settings.option.experimental.close_button_hide": "Close Button Should Hide the Application",
|
||||
// Refer to term.disabled & term.enabled
|
||||
|
||||
// Spatialization Menu
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as electron from 'electron';
|
|||
import * as path from 'path';
|
||||
|
||||
export class AppEvents {
|
||||
private static protocols: any = [
|
||||
private protocols: string[] = [
|
||||
"ame",
|
||||
"cider",
|
||||
"itms",
|
||||
|
@ -10,20 +10,22 @@ export class AppEvents {
|
|||
"musics",
|
||||
"music"
|
||||
]
|
||||
private static plugin: any = null;
|
||||
private static store: any = null;
|
||||
private static win: any = null;
|
||||
private plugin: any = undefined;
|
||||
private store: any = undefined;
|
||||
private win: any = undefined;
|
||||
private tray: any = undefined;
|
||||
private i18n: any = undefined;
|
||||
|
||||
constructor(store: any) {
|
||||
AppEvents.store = store
|
||||
AppEvents.start(store);
|
||||
this.store = store
|
||||
this.start(store);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles all actions that occur for the app on start (Mainly commandline arguments)
|
||||
* @returns {void}
|
||||
*/
|
||||
private static start(store: any): void {
|
||||
private start(store: any): void {
|
||||
console.info('[AppEvents] App started');
|
||||
|
||||
/**********************************************************************************************************************
|
||||
|
@ -46,12 +48,12 @@ export class AppEvents {
|
|||
}
|
||||
|
||||
// Expose GC
|
||||
electron.app.commandLine.appendSwitch('js-flags','--expose_gc')
|
||||
electron.app.commandLine.appendSwitch('js-flags', '--expose_gc')
|
||||
|
||||
if (process.platform === "win32") {
|
||||
electron.app.setAppUserModelId("Cider") // For notification name
|
||||
if (process.platform === "win32") {
|
||||
electron.app.setAppUserModelId(electron.app.getName()) // For notification name
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************************************************************
|
||||
* Commandline arguments
|
||||
**********************************************************************************************************************/
|
||||
|
@ -103,33 +105,39 @@ export class AppEvents {
|
|||
}
|
||||
|
||||
public quit() {
|
||||
console.log('App stopped');
|
||||
console.log('[AppEvents] App quit');
|
||||
}
|
||||
|
||||
public ready(plug: any) {
|
||||
AppEvents.plugin = plug
|
||||
this.plugin = plug
|
||||
console.log('[AppEvents] App ready');
|
||||
}
|
||||
|
||||
public bwCreated(win: Electron.BrowserWindow) {
|
||||
AppEvents.win = win
|
||||
public bwCreated(win: Electron.BrowserWindow, i18n: any) {
|
||||
this.win = win
|
||||
this.i18n = i18n
|
||||
|
||||
electron.app.on('open-url', (event, url) => {
|
||||
event.preventDefault()
|
||||
if (AppEvents.protocols.some((protocol: string) => url.includes(protocol))) {
|
||||
AppEvents.LinkHandler(url)
|
||||
if (this.protocols.some((protocol: string) => url.includes(protocol))) {
|
||||
this.LinkHandler(url)
|
||||
console.log(url)
|
||||
}
|
||||
})
|
||||
|
||||
AppEvents.InstanceHandler()
|
||||
this.InstanceHandler()
|
||||
this.InitTray()
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************
|
||||
* Private methods
|
||||
**********************************************************************************************************************/
|
||||
|
||||
private static LinkHandler(arg: string) {
|
||||
/**
|
||||
* Handles links (URI) and protocols for the application
|
||||
* @param arg
|
||||
*/
|
||||
private LinkHandler(arg: string) {
|
||||
if (!arg) return;
|
||||
|
||||
// LastFM Auth URL
|
||||
|
@ -137,10 +145,10 @@ export class AppEvents {
|
|||
let authURI = arg.split('/auth/')[1]
|
||||
if (authURI.startsWith('lastfm')) { // If we wanted more auth options
|
||||
const authKey = authURI.split('lastfm?token=')[1];
|
||||
AppEvents.store.set('lastfm.enabled', true);
|
||||
AppEvents.store.set('lastfm.auth_token', authKey);
|
||||
AppEvents.win.webContents.send('LastfmAuthenticated', authKey);
|
||||
AppEvents.plugin.callPlugin('lastfm', 'authenticate', authKey);
|
||||
this.store.set('lastfm.enabled', true);
|
||||
this.store.set('lastfm.auth_token', authKey);
|
||||
this.win.webContents.send('LastfmAuthenticated', authKey);
|
||||
this.plugin.callPlugin('lastfm', 'authenticate', authKey);
|
||||
}
|
||||
}
|
||||
// Play
|
||||
|
@ -156,7 +164,7 @@ export class AppEvents {
|
|||
for (const [key, value] of Object.entries(mediaType)) {
|
||||
if (playParam.includes(key)) {
|
||||
const id = playParam.split(key)[1]
|
||||
AppEvents.win.webContents.send('play', value, id)
|
||||
this.win.webContents.send('play', value, id)
|
||||
console.debug(`[LinkHandler] Attempting to load ${value} by id: ${id}`)
|
||||
}
|
||||
}
|
||||
|
@ -165,11 +173,14 @@ export class AppEvents {
|
|||
console.log(arg)
|
||||
let url = arg.split('//')[1]
|
||||
console.warn(`[LinkHandler] Attempting to load url: ${url}`);
|
||||
AppEvents.win.webContents.send('play', 'url', url)
|
||||
this.win.webContents.send('play', 'url', url)
|
||||
}
|
||||
}
|
||||
|
||||
private static InstanceHandler() {
|
||||
/**
|
||||
* Handles the creation of a new instance of the app
|
||||
*/
|
||||
private InstanceHandler() {
|
||||
|
||||
// Detects of an existing instance is running (So if the lock has been achieved, no existing instance has been found)
|
||||
const gotTheLock = electron.app.requestSingleInstanceLock()
|
||||
|
@ -185,17 +196,97 @@ export class AppEvents {
|
|||
console.log(arg)
|
||||
if (arg.includes("cider://")) {
|
||||
console.debug('[InstanceHandler] (second-instance) Link detected with ' + arg)
|
||||
AppEvents.LinkHandler(arg)
|
||||
this.LinkHandler(arg)
|
||||
} else if (arg.includes("--force-quit")) {
|
||||
console.warn('[InstanceHandler] (second-instance) Force Quit found. Quitting App.');
|
||||
electron.app.quit()
|
||||
} else if (AppEvents.win) {
|
||||
if (AppEvents.win.isMinimized()) AppEvents.win.restore()
|
||||
AppEvents.win.focus()
|
||||
} else if (this.win) {
|
||||
if (this.win.isMinimized()) this.win.restore()
|
||||
this.win.focus()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the applications tray
|
||||
*/
|
||||
private InitTray() {
|
||||
const icons = {
|
||||
"win32": electron.nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.ico`)).resize({
|
||||
width: 32,
|
||||
height: 32
|
||||
}),
|
||||
"linux": electron.nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.png`)).resize({
|
||||
width: 32,
|
||||
height: 32
|
||||
}),
|
||||
"darwin": electron.nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.png`)).resize({
|
||||
width: 20,
|
||||
height: 20
|
||||
}),
|
||||
}
|
||||
console.log(this.i18n)
|
||||
|
||||
this.tray = new electron.Tray(process.platform === 'win32' ? icons.win32 : (process.platform === 'darwin' ? icons.darwin : icons.linux))
|
||||
this.tray.setToolTip(electron.app.getName())
|
||||
this.setTray(false)
|
||||
|
||||
this.tray.on('double-click', () => {
|
||||
if (this.win) {
|
||||
if (this.win.isVisible()) {
|
||||
this.win.focus()
|
||||
} else {
|
||||
this.win.show()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this.win.on('show', () => {
|
||||
this.setTray(true)
|
||||
})
|
||||
|
||||
this.win.on('restore', () => {
|
||||
this.setTray(true)
|
||||
})
|
||||
|
||||
this.win.on('hide', () => {
|
||||
this.setTray(false)
|
||||
})
|
||||
|
||||
this.win.on('minimize', () => {
|
||||
this.setTray(false)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tray context menu to a given state
|
||||
* @param visible - BrowserWindow Visibility
|
||||
*/
|
||||
private setTray(visible: boolean = this.win.isVisible()) {
|
||||
|
||||
const menu = electron.Menu.buildFromTemplate([
|
||||
{
|
||||
label: (visible ? this.i18n['action.tray.minimize'] : `${this.i18n['action.tray.show']} ${electron.app.getName()}`),
|
||||
click: () => {
|
||||
if (this.win) {
|
||||
if (visible) {
|
||||
this.win.hide()
|
||||
} else {
|
||||
this.win.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.i18n['action.tray.quit'],
|
||||
click: () => {
|
||||
electron.app.quit()
|
||||
}
|
||||
}
|
||||
])
|
||||
this.tray.setContextMenu(menu)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ export class ConfigStore {
|
|||
|
||||
private defaults: any = {
|
||||
"general": {
|
||||
"close_behavior": 0, // 0 = close, 1 = minimize, 2 = minimize to tray
|
||||
"close_button_hide": true,
|
||||
"open_on_startup": false,
|
||||
"discord_rpc": 1, // 0 = disabled, 1 = enabled as Cider, 2 = enabled as Apple Music
|
||||
"discord_rpc_clear_on_pause": true,
|
||||
|
|
|
@ -6,7 +6,7 @@ import * as express from "express";
|
|||
import * as getPort from "get-port";
|
||||
import * as yt from "youtube-search-without-api-key";
|
||||
import * as fs from "fs";
|
||||
import { Stream } from "stream";
|
||||
import {Stream} from "stream";
|
||||
import * as qrcode from "qrcode-terminal";
|
||||
import * as os from "os";
|
||||
import * as mm from 'music-metadata';
|
||||
|
@ -19,6 +19,7 @@ export class Win {
|
|||
private app: any | undefined = null;
|
||||
private store: any | undefined = null;
|
||||
private devMode: boolean = !electron.app.isPackaged;
|
||||
public i18n: any = {};
|
||||
|
||||
constructor(app: electron.App, store: any) {
|
||||
this.app = app;
|
||||
|
@ -76,7 +77,7 @@ export class Win {
|
|||
* Creates the browser window
|
||||
*/
|
||||
async createWindow(): Promise<Electron.BrowserWindow> {
|
||||
this.clientPort = await getPort({ port: 9000 });
|
||||
this.clientPort = await getPort({port: 9000});
|
||||
this.verifyFiles();
|
||||
|
||||
// Load the previous state with fallback to defaults
|
||||
|
@ -140,7 +141,7 @@ export class Win {
|
|||
*/
|
||||
private startWebServer(): void {
|
||||
const app = express();
|
||||
|
||||
|
||||
app.use(express.static(path.join(this.paths.srcPath, "./renderer/")));
|
||||
app.set("views", path.join(this.paths.srcPath, "./renderer/views"));
|
||||
app.set("view engine", "ejs");
|
||||
|
@ -157,7 +158,7 @@ export class Win {
|
|||
res.redirect("https://discord.gg/applemusic");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
res.render("main", this.EnvironmentVariables);
|
||||
});
|
||||
|
@ -199,7 +200,7 @@ export class Win {
|
|||
remote.set("views", path.join(this.paths.srcPath, "./web-remote/views"));
|
||||
remote.set("view engine", "ejs");
|
||||
getPort({port: 6942}).then((port) => {
|
||||
this.remotePort = port;
|
||||
this.remotePort = port;
|
||||
// Start Remote Discovery
|
||||
this.broadcastRemote()
|
||||
remote.listen(this.remotePort, () => {
|
||||
|
@ -262,7 +263,7 @@ export class Win {
|
|||
if (itspod != null)
|
||||
details.requestHeaders["Cookie"] = `itspod=${itspod}`;
|
||||
}
|
||||
callback({ requestHeaders: details.requestHeaders });
|
||||
callback({requestHeaders: details.requestHeaders});
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -288,18 +289,29 @@ export class Win {
|
|||
event.returnValue = process.platform;
|
||||
});
|
||||
|
||||
let i18nBase = fs.readFileSync(path.join(__dirname, "../../src/i18n/en_US.jsonc"), "utf8");
|
||||
i18nBase = jsonc.parse(i18nBase)
|
||||
try {
|
||||
let i18n = fs.readFileSync(path.join(__dirname, `../../src/i18n/${this.store.general.language}.jsonc`), "utf8");
|
||||
i18n = jsonc.parse(i18n)
|
||||
this.i18n = Object.assign(i18nBase, i18n)
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
electron.ipcMain.on("get-i18n", (event, key) => {
|
||||
let i18nBase = fs.readFileSync(path.join(__dirname, "../../src/i18n/en_US.jsonc"), "utf8");
|
||||
i18nBase = jsonc.parse(i18nBase)
|
||||
try {
|
||||
let i18n = fs.readFileSync(path.join(__dirname, `../../src/i18n/${key}.jsonc`), "utf8");
|
||||
i18n = jsonc.parse(i18n)
|
||||
Object.assign(i18nBase, i18n)
|
||||
}catch(e) {
|
||||
this.i18n = Object.assign(i18nBase, i18n)
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
event.returnValue = e;
|
||||
}
|
||||
|
||||
|
||||
this.i18n = i18nBase;
|
||||
event.returnValue = i18nBase;
|
||||
|
||||
});
|
||||
|
@ -330,11 +342,6 @@ export class Win {
|
|||
event.returnValue = this.devMode;
|
||||
});
|
||||
|
||||
electron.ipcMain.on("close", () => {
|
||||
// listen for close event
|
||||
this.win.close();
|
||||
});
|
||||
|
||||
electron.ipcMain.on("put-library-songs", (event, arg) => {
|
||||
fs.writeFileSync(
|
||||
path.join(this.paths.ciderCache, "library-songs.json"),
|
||||
|
@ -415,8 +422,8 @@ export class Win {
|
|||
return await yt.search(u);
|
||||
});
|
||||
|
||||
electron.ipcMain.handle("setVibrancy", (event, key, value) => {
|
||||
this.win.setVibrancy(value);
|
||||
electron.ipcMain.on("close", () => {
|
||||
this.win.close();
|
||||
});
|
||||
|
||||
electron.ipcMain.on("maximize", () => {
|
||||
|
@ -429,7 +436,7 @@ export class Win {
|
|||
});
|
||||
electron.ipcMain.on("unmaximize", () => {
|
||||
// listen for maximize event
|
||||
this.win.unmaximize();
|
||||
this.win.unmaximize();
|
||||
});
|
||||
|
||||
electron.ipcMain.on("minimize", () => {
|
||||
|
@ -443,7 +450,7 @@ export class Win {
|
|||
});
|
||||
|
||||
electron.ipcMain.on("windowmin", (event, width, height) => {
|
||||
this.win.setMinimumSize(width,height);
|
||||
this.win.setMinimumSize(width, height);
|
||||
})
|
||||
|
||||
electron.ipcMain.on("windowontop", (event, ontop) => {
|
||||
|
@ -451,7 +458,7 @@ export class Win {
|
|||
});
|
||||
|
||||
// Set scale
|
||||
electron.ipcMain.on("windowresize", (event, width, height, lock = false) => {
|
||||
electron.ipcMain.on("windowresize", (event, width, height, lock = false) => {
|
||||
this.win.setContentSize(width, height);
|
||||
this.win.setResizable(!lock);
|
||||
});
|
||||
|
@ -462,9 +469,9 @@ export class Win {
|
|||
})
|
||||
//Fullscreen
|
||||
electron.ipcMain.on('detachDT', (event, _) => {
|
||||
this.win.webContents.openDevTools({ mode: 'detach' });
|
||||
this.win.webContents.openDevTools({mode: 'detach'});
|
||||
})
|
||||
|
||||
|
||||
|
||||
electron.ipcMain.on('play', (event, type, id) => {
|
||||
this.win.webContents.executeJavaScript(`
|
||||
|
@ -497,7 +504,7 @@ export class Win {
|
|||
}
|
||||
|
||||
//QR Code
|
||||
electron.ipcMain.handle('showQR', async (event , _) => {
|
||||
electron.ipcMain.handle('showQR', async (event, _) => {
|
||||
let url = `http://${getIp()}:${this.remotePort}`;
|
||||
electron.shell.openExternal(`https://cider.sh/pair-remote?url=${btoa(encodeURI(url))}`);
|
||||
/*
|
||||
|
@ -510,11 +517,11 @@ export class Win {
|
|||
'get url'
|
||||
fetch(url)
|
||||
.then(res => res.buffer())
|
||||
.then(async(buffer) => {
|
||||
.then(async (buffer) => {
|
||||
try {
|
||||
const metadata = await mm.parseBuffer(buffer, 'audio/x-m4a');
|
||||
let SoundCheckTag = metadata.native.iTunes[1].value
|
||||
console.log('sc',SoundCheckTag)
|
||||
console.log('sc', SoundCheckTag)
|
||||
this.win.webContents.send('SoundCheckTag', SoundCheckTag)
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
|
@ -564,6 +571,25 @@ export class Win {
|
|||
});
|
||||
}
|
||||
|
||||
let isQuiting = false
|
||||
|
||||
this.win.on("close", (event: Event) => {
|
||||
if ((this.store.general.close_button_hide || process.platform === "darwin" )&& !isQuiting) {
|
||||
event.preventDefault();
|
||||
this.win.hide();
|
||||
} else {
|
||||
this.win.destroy();
|
||||
}
|
||||
})
|
||||
|
||||
electron.app.on('before-quit', () => {
|
||||
isQuiting = true
|
||||
});
|
||||
|
||||
electron.app.on('window-all-closed', () => {
|
||||
electron.app.quit()
|
||||
})
|
||||
|
||||
this.win.on("closed", () => {
|
||||
this.win = null;
|
||||
});
|
||||
|
@ -571,19 +597,20 @@ export class Win {
|
|||
// Set window Handler
|
||||
this.win.webContents.setWindowOpenHandler((x: any) => {
|
||||
if (x.url.includes("apple") || x.url.includes("localhost")) {
|
||||
return { action: "allow" };
|
||||
return {action: "allow"};
|
||||
}
|
||||
electron.shell.openExternal(x.url).catch(console.error);
|
||||
return { action: "deny" };
|
||||
return {action: "deny"};
|
||||
});
|
||||
}
|
||||
|
||||
private async broadcastRemote() {
|
||||
function getIp() {
|
||||
let ip :any = false;
|
||||
let ip: any = false;
|
||||
let alias = 0;
|
||||
const ifaces: any = os.networkInterfaces() ;
|
||||
const ifaces: any = os.networkInterfaces();
|
||||
for (var dev in ifaces) {
|
||||
ifaces[dev].forEach( (details: any) => {
|
||||
ifaces[dev].forEach((details: any) => {
|
||||
if (details.family === 'IPv4') {
|
||||
if (!/(loopback|vmware|internal|hamachi|vboxnet|virtualbox)/gi.test(dev + (alias ? ':' + alias : ''))) {
|
||||
if (details.address.substring(0, 8) === '192.168.' ||
|
||||
|
@ -595,14 +622,15 @@ export class Win {
|
|||
}
|
||||
}
|
||||
}
|
||||
}) ;
|
||||
});
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
|
||||
const myString = `http://${getIp()}:${this.remotePort}`;
|
||||
let mdns = require('mdns-js');
|
||||
const encoded = new Buffer(myString).toString('base64');
|
||||
var x = mdns.tcp('cider-remote');
|
||||
var x = mdns.tcp('cider-remote');
|
||||
var txt_record = {
|
||||
"Ver": "131077",
|
||||
'DvSv': '3689',
|
||||
|
@ -613,7 +641,7 @@ export class Win {
|
|||
"CtlN": "Cider",
|
||||
"iV": "196623"
|
||||
}
|
||||
let server2 = mdns.createAdvertisement(x, `${await getPort({port: 3839})}`, { name: encoded, txt: txt_record });
|
||||
let server2 = mdns.createAdvertisement(x, `${await getPort({port: 3839})}`, {name: encoded, txt: txt_record});
|
||||
server2.start();
|
||||
console.log('remote broadcasted')
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ electron.app.on('ready', () => {
|
|||
|
||||
electron.components.whenReady().then(async () => {
|
||||
win = await Cider.createWindow()
|
||||
App.bwCreated(win);
|
||||
App.bwCreated(win, Cider.i18n);
|
||||
/// please dont change this for plugins to get proper and fully initialized Win objects
|
||||
plug.callPlugins('onReady', win);
|
||||
win.on("ready-to-show", () => {
|
||||
|
|
|
@ -1,171 +0,0 @@
|
|||
import * as electron from 'electron';
|
||||
import * as path from 'path';
|
||||
|
||||
|
||||
export default class MinimizeToTray {
|
||||
/**
|
||||
* Private variables for interaction in plugins
|
||||
*/
|
||||
private _win: any;
|
||||
private _app: any;
|
||||
private _store: any;
|
||||
private _tray: any;
|
||||
private _forceQuit = false;
|
||||
|
||||
/**
|
||||
* Base Plugin Details (Eventually implemented into a GUI in settings)
|
||||
*/
|
||||
public name: string = 'Minimize to tray';
|
||||
public description: string = 'Allow Cider to minimize to tray';
|
||||
public version: string = '1.0.0';
|
||||
public author: string = 'vapormusic';
|
||||
|
||||
constructor(app: any, store: any) {
|
||||
this._app = app;
|
||||
this._store = store;
|
||||
}
|
||||
|
||||
private SetContextMenu(visibility : any) {
|
||||
let self = this
|
||||
if (visibility) {
|
||||
this._tray.setContextMenu(electron.Menu.buildFromTemplate([
|
||||
// {
|
||||
// label: 'Check for Updates',
|
||||
// click: function () {
|
||||
// app.ame.utils.checkForUpdates(true)
|
||||
// }
|
||||
// },
|
||||
{
|
||||
label: 'Minimize to Tray',
|
||||
click: function () {
|
||||
if (typeof self._win.hide === 'function') {
|
||||
self._win.hide();
|
||||
self.SetContextMenu(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
click: function () {
|
||||
self._forceQuit = true; self._app.quit();
|
||||
}
|
||||
}
|
||||
]));
|
||||
} else {
|
||||
this._tray.setContextMenu(electron.Menu.buildFromTemplate([
|
||||
// {
|
||||
// label: 'Check for Updates',
|
||||
// click: function () {
|
||||
// this._app.ame.utils.checkForUpdates(true)
|
||||
// }
|
||||
// },
|
||||
{
|
||||
label: `Show ${electron.app.getName()}`,
|
||||
click: function () {
|
||||
if (typeof self._win.show === 'function') {
|
||||
self._win.show();
|
||||
self.SetContextMenu(true);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
click: function () {
|
||||
self._forceQuit = true; self._app.quit();
|
||||
}
|
||||
}
|
||||
]));
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs on app ready
|
||||
*/
|
||||
onReady(win: any): void {
|
||||
this._win = win;
|
||||
const winTray = electron.nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.ico`)).resize({
|
||||
width: 32,
|
||||
height: 32
|
||||
})
|
||||
const macTray = electron.nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.png`)).resize({
|
||||
width: 20,
|
||||
height: 20
|
||||
})
|
||||
const linuxTray = electron.nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.png`)).resize({
|
||||
width: 32,
|
||||
height: 32
|
||||
})
|
||||
let trayIcon : any ;
|
||||
if (process.platform === "win32") {
|
||||
trayIcon = winTray
|
||||
} else if (process.platform === "linux") {
|
||||
trayIcon = linuxTray
|
||||
} else if (process.platform === "darwin") {
|
||||
trayIcon = macTray
|
||||
}
|
||||
|
||||
this._tray = new electron.Tray(trayIcon)
|
||||
this._tray.setToolTip(this._app.getName());
|
||||
this.SetContextMenu(true);
|
||||
|
||||
this._tray.on('double-click', () => {
|
||||
if (typeof this._win.show === 'function') {
|
||||
if (this._win.isVisible()) {
|
||||
this._win.focus()
|
||||
} else {
|
||||
this._win.show()
|
||||
}
|
||||
}
|
||||
})
|
||||
electron.ipcMain.handle("update-store-mtt", (event, value) => {
|
||||
this._store.general["close_behavior"] = value;
|
||||
})
|
||||
electron.ipcMain.on("win-close", (event, value) => {
|
||||
console.log("tray", this._store.general["close_behavior"] )
|
||||
if (this._forceQuit || this._store.general["close_behavior"] == '0' ) {
|
||||
this._app.quit();
|
||||
} else if (this._store.general["close_behavior"] == '1') {
|
||||
this._win.minimize();
|
||||
} else {
|
||||
this._win.hide();
|
||||
this.SetContextMenu(false);
|
||||
}
|
||||
});
|
||||
this._win.on("close", (e :any) => {
|
||||
if (this._forceQuit || this._store.general["close_behavior"] == '0' ) {
|
||||
this._app.quit();
|
||||
} else if (this._store.general["close_behavior"] == '1') {
|
||||
e.preventDefault();
|
||||
this._win.minimize();
|
||||
} else {
|
||||
e.preventDefault();
|
||||
this._win.hide();
|
||||
this.SetContextMenu(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs on app stop
|
||||
*/
|
||||
onBeforeQuit(): void {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs on playback State Change
|
||||
* @param attributes Music Attributes (attributes.state = current state)
|
||||
*/
|
||||
onPlaybackStateDidChange(attributes: object): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs on song change
|
||||
* @param attributes Music Attributes
|
||||
*/
|
||||
onNowPlayingItemDidChange(attributes: object): void {
|
||||
}
|
||||
|
||||
}
|
|
@ -3533,8 +3533,7 @@ const app = new Vue({
|
|||
}
|
||||
},
|
||||
closeWindow(){
|
||||
// window.close doesnt call the win "close" event for some reason
|
||||
ipcRenderer.send('win-close');
|
||||
ipcRenderer.send('close');
|
||||
},
|
||||
checkForUpdate(){
|
||||
ipcRenderer.send('check-for-update')
|
||||
|
|
|
@ -1,778 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<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/>
|
||||
<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">
|
||||
<title>Cider</title>
|
||||
<link rel="stylesheet" href="style-old.css?v=2">
|
||||
<script src="vue.js"></script>
|
||||
<script src="sortable.min.js"></script>
|
||||
<script src="vuedraggable.umd.min.js"></script>
|
||||
<link rel="manifest" href="./manifest.json?v=2">
|
||||
</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"
|
||||
@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"
|
||||
@click="mk.repeatMode = 1"></button>
|
||||
<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"
|
||||
v-else-if="mk.repeatMode == 2"></button>
|
||||
</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>
|
||||
</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 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>
|
||||
</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">
|
||||
</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">
|
||||
</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>
|
||||
</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">
|
||||
<template v-if="type == 'artists'">
|
||||
<div class="mediaitem-artwork rounded"
|
||||
>
|
||||
<img :src="app.getMediaItemArtwork(url, size)"
|
||||
class="mediaitem-artwork--img">
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="mediaitem-artwork"
|
||||
>
|
||||
<img :src="app.getMediaItemArtwork(url, size)"
|
||||
class="mediaitem-artwork--img">
|
||||
</div>
|
||||
</template>
|
||||
</script>
|
||||
|
||||
<!-- Generic Collection of MediaItems -->
|
||||
<script type="text/x-template" id="collection-view-generic">
|
||||
<template>
|
||||
<div class="content-inner">
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</script>
|
||||
|
||||
<!-- 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">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>{{ recom.attributes.title ? recom.attributes.title.stringForDisplay : ""}}</h3>
|
||||
</div>
|
||||
<div class="col-auto flex-center" v-if="recom.relationships.contents.data.length >= 10">
|
||||
<button class="cd-btn-seeall">See All</button>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="recom.attributes.display.kind == 'MusicCoverShelf'">
|
||||
<mediaitem-scroller-horizontal-large
|
||||
:items="recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-large>
|
||||
</template>
|
||||
<template v-else-if="recom.attributes.display.kind == 'MusicSuperHeroShelf'">
|
||||
</template>
|
||||
<template v-else>
|
||||
<mediaitem-scroller-horizontal-sp
|
||||
:items="recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-sp>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<!-- 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">
|
||||
<div class="col-auto">
|
||||
<mediaitem-artwork
|
||||
:url="(data.attributes != null && data.attributes.artwork != null) ? data.attributes.artwork.url : (data.relationships.tracks.data.length > 0 ? data.relationships.tracks.data[0].attributes.artwork.url ?? '':'')"
|
||||
size="200"
|
||||
></mediaitem-artwork>
|
||||
</div>
|
||||
<div class="col playlist-info">
|
||||
<div class="playlist-name">{{data.attributes.name ?? (data.attributes.title ?? '') ?? ''}}</div>
|
||||
<div class="playlist-artist" v-if="data.attributes.artistName">{{data.attributes.artistName ??
|
||||
''}}
|
||||
</div>
|
||||
<div class="playlist-desc"
|
||||
v-html="((data.attributes.editorialNotes) ? (data.attributes.editorialNotes.short ?? (data.attributes.editorialNotes.standard ?? '') ) : (data.attributes.description ? (data.attributes.description.short ?? (data.attributes.description.standard ?? '')) : ''))"></div>
|
||||
</div>
|
||||
</div>
|
||||
<mediaitem-list-item :item="item"
|
||||
v-for="item in data.relationships.tracks.data"></mediaitem-list-item>
|
||||
<div class="playlist-time">{{app.getTotalTime()}}</div>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<!-- 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()">
|
||||
<template>
|
||||
<h3>Top Result</h3>
|
||||
<mediaitem-square-large :item="getTopResult()"></mediaitem-square>
|
||||
</template>
|
||||
</div>
|
||||
<div class="col" v-if="search.results.songs">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>Songs</h3>
|
||||
</div>
|
||||
<div class="col-auto flex-center" v-if="search.results.songs.data.length >= 6">
|
||||
<button class="cd-btn-seeall">See All</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<mediaitem-list-item :item="item"
|
||||
v-for="item in search.results.songs.data.limit(6)"></mediaitem-list-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="search.results['meta']">
|
||||
<template v-if="search.results.albums">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>Albums</h3>
|
||||
</div>
|
||||
<div class="col-auto flex-center" v-if="search.results.albums.data.length >= 10">
|
||||
<button class="cd-btn-seeall">See All</button>
|
||||
</div>
|
||||
</div>
|
||||
<mediaitem-scroller-horizontal-large
|
||||
:items="search.results.albums.data.limit(10)"></mediaitem-scroller-horizontal-large>
|
||||
</template>
|
||||
<template v-if="search.results.artists">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>Artists</h3>
|
||||
</div>
|
||||
<div class="col-auto flex-center" v-if="search.results.artists.data.length >= 5">
|
||||
<button class="cd-btn-seeall">See All</button>
|
||||
</div>
|
||||
</div>
|
||||
<mediaitem-square-large :item="item"
|
||||
v-for="item in search.results.artists.data.limit(5)"></mediaitem-square-large>
|
||||
</template>
|
||||
<template v-if="search.results.playlists">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>Playlists</h3>
|
||||
</div>
|
||||
<div class="col-auto flex-center" v-if="search.results.playlists.data.length >= 10">
|
||||
<button class="cd-btn-seeall">See All</button>
|
||||
</div>
|
||||
</div>
|
||||
<mediaitem-square-large :item="item"
|
||||
v-for="item in search.results.playlists.data.limit(10)"></mediaitem-square-large>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<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">
|
||||
<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">
|
||||
<template>
|
||||
<div class="cd-hmedia-scroller">
|
||||
<mediaitem-square :item="item"
|
||||
v-for="item in items"></mediaitem-square>
|
||||
</div>
|
||||
</template>
|
||||
</script>
|
||||
|
||||
<!-- 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"
|
||||
v-for="item in items"></mediaitem-square-large>
|
||||
</div>
|
||||
</template>
|
||||
</script>
|
||||
|
||||
<!-- Horizontal MediaItem Scroller (SP : Special) -->
|
||||
|
||||
<script type="text/x-template" id="mediaitem-scroller-horizontal-sp">
|
||||
<template>
|
||||
<div class="cd-hmedia-scroller">
|
||||
<mediaitem-square-sp :item="item"
|
||||
v-for="item in items"></mediaitem-square-sp>
|
||||
</div>
|
||||
</template>
|
||||
</script>
|
||||
|
||||
<!-- 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">
|
||||
<div class="artwork">
|
||||
<mediaitem-artwork
|
||||
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
|
||||
size="34"
|
||||
:type="item.type"
|
||||
></mediaitem-artwork>
|
||||
</div>
|
||||
<div class="info-rect">
|
||||
<div class="title text-overflow-elipsis">
|
||||
{{ item.attributes.name }}
|
||||
</div>
|
||||
<div class="subtitle text-overflow-elipsis">
|
||||
<template v-if="item.attributes.artistName">
|
||||
{{ item.attributes.artistName }}
|
||||
<template v-if="item.attributes.albumName">
|
||||
— {{ item.attributes.albumName }}
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-rating" v-if="item.attributes.contentRating">
|
||||
{{ item.attributes.contentRating }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</script>
|
||||
|
||||
<!-- 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">
|
||||
<div class="artwork">
|
||||
<mediaitem-artwork
|
||||
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
|
||||
size="70"
|
||||
:type="item.type"
|
||||
></mediaitem-artwork>
|
||||
</div>
|
||||
<div class="info-rect">
|
||||
<div class="title text-overflow-elipsis">
|
||||
{{ item.attributes.name }}
|
||||
</div>
|
||||
<div class="subtitle text-overflow-elipsis">
|
||||
{{ item.type }}
|
||||
<template v-if="item.attributes.artistName">
|
||||
∙ {{ item.attributes.artistName }}
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</script>
|
||||
|
||||
<!-- 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">
|
||||
<div class="artwork">
|
||||
<mediaitem-artwork
|
||||
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
|
||||
size="300"
|
||||
:type="item.type"
|
||||
></mediaitem-artwork>
|
||||
</div>
|
||||
<div class="title text-overflow-elipsis">
|
||||
{{ item.attributes.name }}
|
||||
</div>
|
||||
<div class="subtitle text-overflow-elipsis" v-if="item.attributes.artistName">
|
||||
{{ item.attributes.artistName }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</script>
|
||||
|
||||
<!-- 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)'
|
||||
class="cd-mediaitem-square-large">
|
||||
<div class="artwork">
|
||||
<mediaitem-artwork
|
||||
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
|
||||
size="300"
|
||||
:type="item.type"
|
||||
></mediaitem-artwork>
|
||||
</div>
|
||||
<div class="cd-mediaitem-square-large-overlay" @click.self='app.routeView(item)'>
|
||||
<div class="button" style="
|
||||
border-radius: 50%;
|
||||
background: rgba(50,50,50,0.7);"
|
||||
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '140px',
|
||||
width: '40px',
|
||||
height: '40px',} :
|
||||
{margin: '35px',
|
||||
width: '120px',
|
||||
height: '120px',}]"
|
||||
@click="app.playMediaItem(item)">
|
||||
<svg fill="white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27" class="glyph">
|
||||
<path d="M11.3545232,18.4180929 L18.4676039,14.242665 C19.0452323,13.9290954 19.0122249,13.1204156 18.4676039,12.806846 L11.3545232,8.63141809 C10.7603912,8.26833741 9.98471883,8.54889976 9.98471883,9.19254279 L9.98471883,17.8404645 C9.98471883,18.5006112 10.7108802,18.7976773 11.3545232,18.4180929 Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="title text-overflow-elipsis" @click='app.routeView(item)'>
|
||||
{{ item.attributes.name ?? '' }}
|
||||
</div>
|
||||
<div class="subtitle text-overflow-elipsis" v-if="item.attributes.artistName">
|
||||
{{ item.attributes.artistName ?? '' }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="cd-mediaitem-square-large-overlay" @click.self='app.routeView(item)'>
|
||||
<div class="button" style="
|
||||
border-radius: 50%;
|
||||
background: rgba(50,50,50,0.7);"
|
||||
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '140px',
|
||||
width: '40px',
|
||||
height: '40px',} :
|
||||
{margin: '35px',
|
||||
width: '120px',
|
||||
height: '120px',}]"
|
||||
@click="app.playMediaItem(item)">
|
||||
<svg fill="white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27" class="glyph">
|
||||
<path d="M11.3545232,18.4180929 L18.4676039,14.242665 C19.0452323,13.9290954 19.0122249,13.1204156 18.4676039,12.806846 L11.3545232,8.63141809 C10.7603912,8.26833741 9.98471883,8.54889976 9.98471883,9.19254279 L9.98471883,17.8404645 C9.98471883,18.5006112 10.7108802,18.7976773 11.3545232,18.4180929 Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</script>
|
||||
|
||||
<!-- 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)'
|
||||
class="cd-mediaitem-square-sp"
|
||||
:style="{'--spcolor' : (item.attributes.artwork.bgColor != null) ? ('#'+item.attributes.artwork.bgColor) : `black`}">
|
||||
<div class="artwork">
|
||||
<mediaitem-artwork
|
||||
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
|
||||
size="300"
|
||||
:type="item.type"
|
||||
></mediaitem-artwork>
|
||||
</div>
|
||||
<div class="cd-mediaitem-square-large-overlay" @click.self='app.routeView(item)'>
|
||||
<div class="button" style="
|
||||
border-radius: 50%;
|
||||
background: rgba(50,50,50,0.7);"
|
||||
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '140px',
|
||||
width: '40px',
|
||||
height: '40px',} :
|
||||
{margin: '35px',
|
||||
width: '120px',
|
||||
height: '120px',}]"
|
||||
@click="app.playMediaItem(item)">
|
||||
<svg fill="white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27" class="glyph">
|
||||
<path d="M11.3545232,18.4180929 L18.4676039,14.242665 C19.0452323,13.9290954 19.0122249,13.1204156 18.4676039,12.806846 L11.3545232,8.63141809 C10.7603912,8.26833741 9.98471883,8.54889976 9.98471883,9.19254279 L9.98471883,17.8404645 C9.98471883,18.5006112 10.7108802,18.7976773 11.3545232,18.4180929 Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="title text-overflow-elipsis"
|
||||
:style="{'color' : (item.attributes.artwork.textColor1 != null) ? ('#'+item.attributes.artwork.textColor1) : `#eee`}"
|
||||
style="font-weight: 600">
|
||||
{{ item.attributes.name }}
|
||||
</div>
|
||||
<div class="subtitle text-overflow-elipsis"
|
||||
:style="{'color' : (item.attributes.artwork.textColor1 != null) ? ('#'+item.attributes.artwork.textColor1) : `#eee`}"
|
||||
style="padding-left: 4px;padding-right: 4px; display: -webkit-box;-webkit-box-orient: vertical; -webkit-line-clamp: 2;white-space: normal;">
|
||||
{{ (item.attributes.editorialNotes != null) ? item.attributes.editorialNotes.short
|
||||
:(item.attributes.artistName ?? '') }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="cd-mediaitem-square-large-overlay" @click.self='app.routeView(item)'>
|
||||
<div class="button" style="
|
||||
border-radius: 50%;
|
||||
background: rgba(50,50,50,0.7);"
|
||||
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '140px',
|
||||
width: '40px',
|
||||
height: '40px',} :
|
||||
{margin: '35px',
|
||||
width: '120px',
|
||||
height: '120px',}]"
|
||||
@click="app.playMediaItem(item)">
|
||||
<svg fill="white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27" class="glyph">
|
||||
<path d="M11.3545232,18.4180929 L18.4676039,14.242665 C19.0452323,13.9290954 19.0122249,13.1204156 18.4676039,12.806846 L11.3545232,8.63141809 C10.7603912,8.26833741 9.98471883,8.54889976 9.98471883,9.19254279 L9.98471883,17.8404645 C9.98471883,18.5006112 10.7108802,18.7976773 11.3545232,18.4180929 Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</script>
|
||||
<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'">
|
||||
<h3 class="lyric-line" @click="app.seekTo(lyric.startTime, false)"
|
||||
v-bind:class="{ active: app.getLyricClass(lyric.startTime, lyric.endTime)}">
|
||||
{{ lyric.line }}
|
||||
<div class="lyrics-translation" v-if="lyric.translation && lyric.translation != ''">
|
||||
{{ lyric.translation }}
|
||||
<div>
|
||||
</h3>
|
||||
</template>
|
||||
<template v-else>
|
||||
<h3 class="lyric-line" @click="app.seekTo(lyric.startTime, false)" :start="lyric.startTime"
|
||||
:end="lyric.endTime"
|
||||
v-bind:class="{ active: app.getLyricClass(lyric.startTime, lyric.endTime)}">
|
||||
<div class="lyricWaiting">
|
||||
<div class='WaitingDot1'></div>
|
||||
<div class='WaitingDot2'></div>
|
||||
<div class='WaitingDot3'></div>
|
||||
</div>
|
||||
</h3>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
No Lyrics Available
|
||||
</template>
|
||||
</div>
|
||||
</script>
|
||||
<script src="https://js-cdn.music.apple.com/musickit/v2/amp/musickit.js"></script>
|
||||
<script src="index.js?v=1"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -598,20 +598,10 @@
|
|||
</div>
|
||||
<div class="md-option-line">
|
||||
<div class="md-option-segment">
|
||||
{{$root.getLz("settings.option.experimental.closeButtonBehaviour")}}
|
||||
{{$root.getLz("settings.option.experimental.close_button_hide")}}
|
||||
</div>
|
||||
<div class="md-option-segment md-option-segment_auto">
|
||||
<select class="md-select" v-model="app.cfg.general.close_behavior" @change="sendDataToMTT()">
|
||||
<option value="0">
|
||||
{{$root.getLz("settings.option.experimental.closeButtonBehaviour.quit")}}
|
||||
</option>
|
||||
<option value="1">
|
||||
{{$root.getLz("settings.option.experimental.closeButtonBehaviour.minimizeTaskbar")}}
|
||||
</option>
|
||||
<option value="2">
|
||||
{{$root.getLz("settings.option.experimental.closeButtonBehaviour.minimizeTray")}}
|
||||
</option>
|
||||
</select>
|
||||
<input type="checkbox" v-model="app.cfg.general.close_button_hide" switch/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md-option-line">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue