
* Update ru_RU.json keeping russian lang actual * ok * Add gradient to lyric-footer * *Commit en español Ñ (#1304) * i hate my life (#1307) * world is now a better place * meltdown avoided * meltdown avoided * stylize new listen now childs * full scale artwork, finally * dynamic width for search categories * hd all album work * Update afterPack.js * force hq quality * oops * attempt to fix * misc cleanup * why what * what was i thinking * fix duplicated text in listen now childs * Paginate/infinite scroll for albums, playlists (#1234) * Infinite scroll, pagination to album, playlists * move pagination below tracks * Make page size configurable * remove renderer * Mitigate songs / album slow app issue. * add ratings, library change to web remote (#1285) * Add compact artist header option (#1308) * Support compact artist header (optional) * Add required term * pain e19 still bugged * improve pagination styling * el16 fix * up 1.5.2 * Disable Fullscreen view when artist/album name is clicked. (#1315) * Disable Fullscreen view when artist/album name is clicked. idk why this change didn't exist * Seperate dash from album name * Replace `$root.showSearch()` with `app.appRoute('search')` `$root.showSearch()` prevents going back to previous page from sidebar. * Fix Anim (#1316) * make tracks tab active (#1318) * welp that wasn't it. * Thnks (#1319) * Thnks * i need sleep * Update cider-playlist.ejs * remove v-ripple * attempt to fix flatpak * Update config.yml * minimize categories names * fix versioning * fix versioning v2 * version fix v3 - final * fix categories name for other storefronts * ident unparsed title * center fullscreen * fix share * Add protocol for playpause and nextitem (#1329) * Moved to minimize hide window instead of close * Moved state saving to before quit * build test * fix ci * sudo * ci * use old branch fn * d * revert * Exposed songid in attributes, reimplemented lastfm primary artist scrobbling, cleaned up mkinterop * Moved it back * im over this * attempt airts * use new airtunes branch * *eye roll* * Moved time conversion to built-in MK function * Moved to Electron Notifications * Swapped to array index * keeping this here until for now * debuggin * run ci * allow noti button on macs * No image for now * revert space * Latest releases -> Latest Release * Artwork caching (sorta) * holy balls optimization * save sentry from this Co-authored-by: h0ckerman <35598335+h0ckerman@users.noreply.github.com> Co-authored-by: vapormusic <vietanhfat@gmail.com> Co-authored-by: Monochromish <chillygamer7@gmail.com> Co-authored-by: Gabriel Davila <56521591+mefsaal@users.noreply.github.com> Co-authored-by: Core <64542347+coredev-uk@users.noreply.github.com> Co-authored-by: Maikiwi <stella@mai.kiwi> Co-authored-by: yazninja <yazlesean@gmail.com> Co-authored-by: booploops <49113086+booploops@users.noreply.github.com> Co-authored-by: Kendall Garner <17521368+kgarner7@users.noreply.github.com> Co-authored-by: Pedro Galhardo <pgalhardo@icloud.com> Co-authored-by: Monochromish <79590499+Monochromish@users.noreply.github.com> Co-authored-by: Core <core@c0r3.uk>
426 lines
15 KiB
TypeScript
426 lines
15 KiB
TypeScript
import {app, Menu, nativeImage, Tray, ipcMain, clipboard, shell} from 'electron';
|
|
import {readFileSync} from "fs";
|
|
import * as path from 'path';
|
|
import * as log from 'electron-log';
|
|
import {utils} from './utils';
|
|
|
|
/**
|
|
* @file Creates App instance
|
|
* @author CiderCollective
|
|
*/
|
|
|
|
/** @namespace */
|
|
export class AppEvents {
|
|
private protocols: string[] = [
|
|
"ame",
|
|
"cider",
|
|
"itms",
|
|
"itmss",
|
|
"musics",
|
|
"music"
|
|
]
|
|
private plugin: any = undefined;
|
|
private tray: any = undefined;
|
|
private i18n: any = undefined;
|
|
|
|
/** @constructor */
|
|
constructor() {
|
|
this.start();
|
|
}
|
|
|
|
/**
|
|
* Handles all actions that occur for the app on start (Mainly commandline arguments)
|
|
* @returns {void}
|
|
*/
|
|
private start(): void {
|
|
AppEvents.initLogging()
|
|
console.info('[AppEvents] App started');
|
|
|
|
/**********************************************************************************************************************
|
|
* Startup arguments handling
|
|
**********************************************************************************************************************/
|
|
if (app.commandLine.hasSwitch('version') || app.commandLine.hasSwitch('v')) {
|
|
console.log(app.getVersion())
|
|
app.exit()
|
|
}
|
|
|
|
// Verbose Check
|
|
if (app.commandLine.hasSwitch('verbose')) {
|
|
console.log("[Cider] User has launched the application with --verbose");
|
|
}
|
|
|
|
// Log File Location
|
|
if (app.commandLine.hasSwitch('log') || app.commandLine.hasSwitch('l')) {
|
|
console.log(path.join(app.getPath('userData'), 'logs'))
|
|
app.exit()
|
|
}
|
|
|
|
// Try limiting JS memory to 350MB.
|
|
app.commandLine.appendSwitch('js-flags', '--max-old-space-size=350');
|
|
|
|
// Expose GC
|
|
app.commandLine.appendSwitch('js-flags', '--expose_gc')
|
|
|
|
if (process.platform === "win32") {
|
|
app.setAppUserModelId(app.getName()) // For notification name
|
|
}
|
|
|
|
/***********************************************************************************************************************
|
|
* Commandline arguments
|
|
**********************************************************************************************************************/
|
|
switch (utils.getStoreValue('visual.hw_acceleration') as string) {
|
|
default:
|
|
case "default":
|
|
app.commandLine.appendSwitch('enable-accelerated-mjpeg-decode')
|
|
app.commandLine.appendSwitch('enable-accelerated-video')
|
|
app.commandLine.appendSwitch('disable-gpu-driver-bug-workarounds')
|
|
app.commandLine.appendSwitch('ignore-gpu-blacklist')
|
|
app.commandLine.appendSwitch('enable-native-gpu-memory-buffers')
|
|
app.commandLine.appendSwitch('enable-accelerated-video-decode');
|
|
app.commandLine.appendSwitch('enable-gpu-rasterization');
|
|
app.commandLine.appendSwitch('enable-native-gpu-memory-buffers');
|
|
app.commandLine.appendSwitch('enable-oop-rasterization');
|
|
break;
|
|
|
|
case "webgpu":
|
|
console.info("WebGPU is enabled.");
|
|
app.commandLine.appendSwitch('enable-unsafe-webgpu')
|
|
break;
|
|
|
|
case "disabled":
|
|
console.info("Hardware acceleration is disabled.");
|
|
app.commandLine.appendSwitch('disable-gpu')
|
|
break;
|
|
}
|
|
|
|
if (process.platform === "linux") {
|
|
app.commandLine.appendSwitch('disable-features', 'MediaSessionService');
|
|
}
|
|
|
|
/***********************************************************************************************************************
|
|
* Protocols
|
|
**********************************************************************************************************************/
|
|
/** */
|
|
if (process.defaultApp) {
|
|
if (process.argv.length >= 2) {
|
|
this.protocols.forEach((protocol: string) => {
|
|
app.setAsDefaultProtocolClient(protocol, process.execPath, [path.resolve(process.argv[1])])
|
|
})
|
|
}
|
|
} else {
|
|
this.protocols.forEach((protocol: string) => {
|
|
app.setAsDefaultProtocolClient(protocol)
|
|
})
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public quit() {
|
|
console.log('[AppEvents] App quit');
|
|
}
|
|
|
|
public ready(plug: any) {
|
|
this.plugin = plug
|
|
console.log('[AppEvents] App ready');
|
|
|
|
AppEvents.setLoginSettings()
|
|
}
|
|
|
|
public bwCreated() {
|
|
app.on('open-url', (event, url) => {
|
|
event.preventDefault()
|
|
if (this.protocols.some((protocol: string) => url.includes(protocol))) {
|
|
this.LinkHandler(url)
|
|
console.log(url)
|
|
}
|
|
})
|
|
|
|
if (process.platform === "darwin") {
|
|
app.setUserActivity('8R23J2835D.com.ciderapp.webremote.play', {
|
|
title: 'Web Remote',
|
|
description: 'Connect to your Web Remote',
|
|
}, "https://webremote.cider.sh")
|
|
}
|
|
|
|
this.InstanceHandler()
|
|
if (process.platform !== "darwin") {
|
|
this.InitTray()
|
|
}
|
|
}
|
|
|
|
/***********************************************************************************************************************
|
|
* Private methods
|
|
**********************************************************************************************************************/
|
|
|
|
/**
|
|
* Handles links (URI) and protocols for the application
|
|
* @param arg
|
|
*/
|
|
private LinkHandler(arg: string) {
|
|
if (!arg) return;
|
|
|
|
// LastFM Auth URL
|
|
if (arg.includes('auth')) {
|
|
const authURI = arg.split('/auth/')[1]
|
|
if (authURI.startsWith('lastfm')) { // If we wanted more auth options
|
|
console.log('token: ', authURI.split('lastfm?token=')[1])
|
|
utils.getWindow().webContents.executeJavaScript(`ipcRenderer.send('lastfm:auth', "${authURI.split('lastfm?token=')[1]}")`).catch(console.error)
|
|
}
|
|
}
|
|
else if (arg.includes('playpause')) {
|
|
//language=JS
|
|
utils.getWindow().webContents.executeJavaScript('MusicKitInterop.playPause()')
|
|
}
|
|
else if (arg.includes('nextitem')) {
|
|
//language=JS
|
|
utils.getWindow().webContents.executeJavaScript('app.mk.skipToNextItem()')
|
|
}
|
|
// Play
|
|
else if (arg.includes('/play/')) { //Steer away from protocol:// specific conditionals
|
|
const playParam = arg.split('/play/')[1]
|
|
|
|
const mediaType = {
|
|
"s/": "song",
|
|
"a/": "album",
|
|
"p/": "playlist"
|
|
}
|
|
|
|
for (const [key, value] of Object.entries(mediaType)) {
|
|
if (playParam.includes(key)) {
|
|
const id = playParam.split(key)[1]
|
|
utils.getWindow().webContents.send('play', value, id)
|
|
console.debug(`[LinkHandler] Attempting to load ${value} by id: ${id}`)
|
|
}
|
|
}
|
|
|
|
} else if (arg.includes('music.apple.com')) { // URL (used with itms/itmss/music/musics uris)
|
|
console.log(arg)
|
|
let url = arg.split('//')[1]
|
|
console.warn(`[LinkHandler] Attempting to load url: ${url}`);
|
|
utils.getWindow().webContents.send('play', 'url', url)
|
|
} else if (arg.includes('/debug/appdata')) {
|
|
shell.openPath(app.getPath('userData'))
|
|
} else if (arg.includes('/debug/logs')) {
|
|
shell.openPath(app.getPath('logs'))
|
|
} else if (arg.includes('/discord')) {
|
|
shell.openExternal('https://discord.gg/applemusic')
|
|
} else if (arg.includes('/github')) {
|
|
shell.openExternal('https://github.com/ciderapp/cider')
|
|
} else if (arg.includes('/donate')) {
|
|
shell.openExternal('https://opencollective.com/ciderapp')
|
|
} else if (arg.includes('/beep')) {
|
|
shell.beep()
|
|
} else {
|
|
utils.getWindow().webContents.executeJavaScript(`app.appRoute('${arg.split('//')[1]}')`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 = app.requestSingleInstanceLock()
|
|
|
|
if (!gotTheLock) { // Runs on the new instance if another instance has been found
|
|
console.log('[Cider] Another instance has been found, quitting.')
|
|
app.quit()
|
|
} else { // Runs on the first instance if no other instance has been found
|
|
app.on('second-instance', (_event, startArgs) => {
|
|
console.log("[InstanceHandler] (second-instance) Instance started with " + startArgs.toString())
|
|
|
|
startArgs.forEach(arg => {
|
|
console.log(arg)
|
|
if (arg.includes("cider://")) {
|
|
console.debug('[InstanceHandler] (second-instance) Link detected with ' + arg)
|
|
this.LinkHandler(arg)
|
|
} else if (arg.includes("--force-quit")) {
|
|
console.warn('[InstanceHandler] (second-instance) Force Quit found. Quitting App.');
|
|
app.quit()
|
|
} else if (utils.getWindow()) {
|
|
if (utils.getWindow().isMinimized()) utils.getWindow().restore()
|
|
utils.getWindow().show()
|
|
utils.getWindow().focus()
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Initializes the applications tray
|
|
*/
|
|
private InitTray() {
|
|
const icons = {
|
|
"win32": nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.ico`)).resize({
|
|
width: 32,
|
|
height: 32
|
|
}),
|
|
"linux": nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.png`)).resize({
|
|
width: 32,
|
|
height: 32
|
|
}),
|
|
"darwin": nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.png`)).resize({
|
|
width: 20,
|
|
height: 20
|
|
}),
|
|
}
|
|
this.tray = new Tray(process.platform === 'win32' ? icons.win32 : (process.platform === 'darwin' ? icons.darwin : icons.linux))
|
|
this.tray.setToolTip(app.getName())
|
|
this.setTray(false)
|
|
|
|
this.tray.on('double-click', () => {
|
|
if (utils.getWindow()) {
|
|
if (utils.getWindow().isVisible()) {
|
|
utils.getWindow().focus()
|
|
} else {
|
|
utils.getWindow().show()
|
|
}
|
|
}
|
|
})
|
|
|
|
utils.getWindow().on('show', () => {
|
|
this.setTray(true)
|
|
})
|
|
|
|
utils.getWindow().on('restore', () => {
|
|
this.setTray(true)
|
|
})
|
|
|
|
utils.getWindow().on('hide', () => {
|
|
this.setTray(false)
|
|
})
|
|
|
|
utils.getWindow().on('minimize', () => {
|
|
this.setTray(false)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Sets the tray context menu to a given state
|
|
* @param visible - BrowserWindow Visibility
|
|
*/
|
|
private setTray(visible: boolean = utils.getWindow().isVisible()) {
|
|
this.i18n = utils.getLocale(utils.getStoreValue('general.language'))
|
|
|
|
const ciderIcon = nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.png`)).resize({
|
|
width: 24,
|
|
height: 24
|
|
})
|
|
|
|
const menu = Menu.buildFromTemplate([
|
|
|
|
{
|
|
label: app.getName(),
|
|
enabled: false,
|
|
icon: ciderIcon,
|
|
|
|
},
|
|
|
|
{type: 'separator'},
|
|
|
|
/* For now only idea i dont know if posible to implement
|
|
|
|
this could be implemented in a plugin if you would like track info, it would be impractical to put listeners in this file. -Core
|
|
{
|
|
label: this.i18n['action.tray.listento'],
|
|
enabled: false,
|
|
},
|
|
|
|
{
|
|
visible: visible,
|
|
label: 'track info',
|
|
enabled: false,
|
|
},
|
|
|
|
{type: 'separator'},
|
|
*/
|
|
|
|
{
|
|
visible: !visible,
|
|
label: this.i18n['term.playpause'],
|
|
click: () => {
|
|
utils.getWindow().webContents.executeJavaScript('MusicKitInterop.playPause()')
|
|
}
|
|
},
|
|
|
|
{
|
|
visible: !visible,
|
|
label: this.i18n['term.next'],
|
|
click: () => {
|
|
utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.next()`)
|
|
}
|
|
},
|
|
|
|
{
|
|
visible: !visible,
|
|
label: this.i18n['term.previous'],
|
|
click: () => {
|
|
utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.previous()`)
|
|
}
|
|
},
|
|
|
|
{type: 'separator', visible: !visible},
|
|
|
|
{
|
|
label: (visible ? this.i18n['action.tray.minimize'] : `${this.i18n['action.tray.show']}`),
|
|
click: () => {
|
|
if (utils.getWindow()) {
|
|
if (visible) {
|
|
utils.getWindow().hide()
|
|
} else {
|
|
utils.getWindow().show()
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
label: this.i18n['term.quit'],
|
|
click: () => {
|
|
app.quit()
|
|
}
|
|
}
|
|
])
|
|
this.tray.setContextMenu(menu)
|
|
}
|
|
|
|
/**
|
|
* Initializes logging in the application
|
|
* @private
|
|
*/
|
|
private static initLogging() {
|
|
log.transports.console.format = '[{h}:{i}:{s}.{ms}] [{level}] {text}';
|
|
Object.assign(console, log.functions);
|
|
console.debug = function(...args: any[]) {
|
|
if (!app.isPackaged) {
|
|
log.debug(...args)
|
|
}
|
|
};
|
|
|
|
ipcMain.on('fetch-log', (_event) => {
|
|
const data = readFileSync(log.transports.file.getFile().path, {encoding: 'utf8', flag: 'r'});
|
|
clipboard.writeText(data)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Set login settings
|
|
* @private
|
|
*/
|
|
private static setLoginSettings() {
|
|
if (utils.getStoreValue('general.onStartup.enabled')) {
|
|
app.setLoginItemSettings({
|
|
openAtLogin: true,
|
|
path: app.getPath('exe'),
|
|
args: [`${utils.getStoreValue('general.onStartup.hidden') ? '--hidden' : ''}`]
|
|
})
|
|
} else {
|
|
app.setLoginItemSettings({
|
|
openAtLogin: false,
|
|
path: app.getPath('exe')
|
|
})
|
|
}
|
|
}
|
|
}
|