Merge branch 'upcoming' of https://github.com/ciderapp/Cider into upcoming

This commit is contained in:
vapormusic 2022-01-26 13:19:24 +07:00
commit 694a75c4a0
10 changed files with 305 additions and 84 deletions

View file

@ -50,7 +50,7 @@ export class AppEvents {
/*********************************************************************************************************************** /***********************************************************************************************************************
* Commandline arguments * Commandline arguments
**********************************************************************************************************************/ **********************************************************************************************************************/
switch (store.get("visual.hw_acceleration")) { switch (store.visual.hw_acceleration) {
default: default:
case "default": case "default":
electron.app.commandLine.appendSwitch('enable-accelerated-mjpeg-decode') electron.app.commandLine.appendSwitch('enable-accelerated-mjpeg-decode')
@ -75,6 +75,10 @@ export class AppEvents {
break; break;
} }
if (process.platform === "linux") {
electron.app.commandLine.appendSwitch('disable-features', 'MediaSessionService');
}
/*********************************************************************************************************************** /***********************************************************************************************************************
* Protocols * Protocols
**********************************************************************************************************************/ **********************************************************************************************************************/

View file

@ -5,10 +5,11 @@ import * as electron from 'electron'
export default class PluginHandler { export default class PluginHandler {
private basePluginsPath = path.join(__dirname, '../plugins'); private basePluginsPath = path.join(__dirname, '../plugins');
private userPluginsPath = path.join(electron.app.getPath('userData'), 'plugins'); private userPluginsPath = path.join(electron.app.getPath('userData'), 'plugins');
private pluginsList: any = {}; private readonly pluginsList: any = {};
private readonly _store: any;
constructor() {
constructor(config: any) {
this._store = config;
this.pluginsList = this.getPlugins(); this.pluginsList = this.getPlugins();
} }
@ -23,7 +24,7 @@ export default class PluginHandler {
if (plugins[file] || plugin.name in plugins) { if (plugins[file] || plugin.name in plugins) {
console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`); console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`);
} else { } else {
plugins[file] = new plugin(electron.app); plugins[file] = new plugin(electron.app, this._store);
} }
} }
}); });
@ -38,7 +39,7 @@ export default class PluginHandler {
if (plugins[file] || plugin in plugins) { if (plugins[file] || plugin in plugins) {
console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`); console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`);
} else { } else {
plugins[file] = new plugin(electron.app); plugins[file] = new plugin(electron.app, this._store);
} }
} }
}); });

View file

@ -2,7 +2,7 @@ import * as Store from 'electron-store';
import * as electron from "electron"; import * as electron from "electron";
export class ConfigStore { export class ConfigStore {
public store: Store | undefined; private _store: Store;
private defaults: any = { private defaults: any = {
"general": { "general": {
@ -89,14 +89,26 @@ export class ConfigStore {
private migrations: any = {} private migrations: any = {}
constructor() { constructor() {
this.store = new Store({ this._store = new Store({
name: 'cider-config', name: 'cider-config',
defaults: this.defaults, defaults: this.defaults,
migrations: this.migrations, migrations: this.migrations,
}); });
this.store.set(this.mergeStore(this.defaults, this.store.store)) this._store.set(this.mergeStore(this.defaults, this._store.store))
this.ipcHandler(this.store); this.ipcHandler(this._store);
}
get store() {
return this._store.store;
}
get(key: string) {
return this._store.get(key);
}
set(key: string, value: any) {
this._store.set(key, value);
} }
/** /**

View file

@ -15,10 +15,10 @@ import {wsapi} from "./wsapi";
import * as jsonc from "jsonc"; import * as jsonc from "jsonc";
export class Win { export class Win {
win: any | undefined = null; private win: any | undefined = null;
app: any | undefined = null; private app: any | undefined = null;
store: any | undefined = null; private store: any | undefined = null;
devMode: boolean = !electron.app.isPackaged; private devMode: boolean = !electron.app.isPackaged;
constructor(app: electron.App, store: any) { constructor(app: electron.App, store: any) {
this.app = app; this.app = app;

View file

@ -13,7 +13,7 @@ import PluginHandler from "./base/plugins";
const config = new ConfigStore(); const config = new ConfigStore();
const App = new AppEvents(config.store); const App = new AppEvents(config.store);
const Cider = new Win(electron.app, config.store) const Cider = new Win(electron.app, config.store)
const plug = new PluginHandler(); const plug = new PluginHandler(config.store);
let win: Electron.BrowserWindow; let win: Electron.BrowserWindow;
@ -34,7 +34,7 @@ electron.app.on('ready', () => {
win = await Cider.createWindow() win = await Cider.createWindow()
App.bwCreated(win); App.bwCreated(win);
/// please dont change this for plugins to get proper and fully initialized Win objects /// please dont change this for plugins to get proper and fully initialized Win objects
plug.callPlugins('onReady', Cider); plug.callPlugins('onReady', win);
win.on("ready-to-show", () => { win.on("ready-to-show", () => {
win.show(); win.show();
}); });

View file

@ -1,10 +1,11 @@
let i = 1, k = 1; let i = 1, k = 1;
export default class ExamplePlugin { export default class ExamplePlugin {
/** /**
* Private variables for interaction in plugins * Private variables for interaction in plugins
*/ */
private _win: any; private _win: any;
private _app: any; private _app: any;
private _store: any;
/** /**
* Base Plugin Details (Eventually implemented into a GUI in settings) * Base Plugin Details (Eventually implemented into a GUI in settings)
@ -17,16 +18,17 @@ export default class ExamplePlugin {
/** /**
* Runs on plugin load (Currently run on application start) * Runs on plugin load (Currently run on application start)
*/ */
constructor(app: any) { constructor(app: any, store: any) {
this._app = app; this._app = app;
console.log('Example plugin loaded'); this._store = store;
} console.log('Example plugin loaded');
}
/** /**
* Runs on app ready * Runs on app ready
*/ */
onReady(win: any): void { onReady(win: any): void {
this._win = win; this._win = win;
console.log('Example plugin ready'); console.log('Example plugin ready');
} }
@ -42,7 +44,7 @@ export default class ExamplePlugin {
* @param attributes Music Attributes (attributes.state = current state) * @param attributes Music Attributes (attributes.state = current state)
*/ */
onPlaybackStateDidChange(attributes: object): void { onPlaybackStateDidChange(attributes: object): void {
console.log('onPlaybackStateDidChange has been called ' + i +' times'); console.log('onPlaybackStateDidChange has been called ' + i + ' times');
i++ i++
} }
@ -51,7 +53,7 @@ export default class ExamplePlugin {
* @param attributes Music Attributes * @param attributes Music Attributes
*/ */
onNowPlayingItemDidChange(attributes: object): void { onNowPlayingItemDidChange(attributes: object): void {
console.log('onNowPlayingDidChange has been called ' + k +' times'); console.log('onNowPlayingDidChange has been called ' + k + ' times');
k++ k++
} }

View file

@ -1,28 +1,30 @@
import * as electron from 'electron';
import * as DiscordRPC from 'discord-rpc' import * as DiscordRPC from 'discord-rpc'
export default class DiscordRPCPlugin { export default class DiscordRPCPlugin {
/** /**
* Private variables for interaction in plugins * Private variables for interaction in plugins
*/ */
private _win: any; private _win: Electron.BrowserWindow | undefined;
private _app: any; private _app: any;
private _store: any;
private _discord: any; private _discord: any;
private connect(clientId: any) { private connect(clientId: any) {
this._discord = { isConnected: false }; this._discord = {isConnected: false};
if (this._win.store.store.general.discord_rpc == 0 || this._discord.isConnected) return; if (this._store.general.discord_rpc == 0 || this._discord.isConnected) return;
DiscordRPC.register(clientId) // Apparently needed for ask to join, join, spectate etc. DiscordRPC.register(clientId) // Apparently needed for ask to join, join, spectate etc.
const client = new DiscordRPC.Client({ transport: "ipc" }); const client = new DiscordRPC.Client({transport: "ipc"});
this._discord = Object.assign(client, { error: false, activityCache: null, isConnected: false }); this._discord = Object.assign(client, {error: false, activityCache: null, isConnected: false});
// Login to Discord // Login to Discord
this._discord.login({ clientId }) this._discord.login({clientId})
.then(() => { .then(() => {
this._discord.isConnected = true; this._discord.isConnected = true;
}) })
.catch((e : any) => console.error(`[DiscordRPC][connect] ${e}`)); .catch((e: any) => console.error(`[DiscordRPC][connect] ${e}`));
this._discord.on('ready', () => { this._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})`);
}) })
@ -34,19 +36,19 @@ export default class DiscordRPCPlugin {
}); });
} }
/** /**
* Disconnects from Discord RPC * Disconnects from Discord RPC
*/ */
private disconnect() { private disconnect() {
if (this._win.store.store.general.discord_rpc == 0 || !this._discord.isConnected) return; if (this._store.general.discord_rpc == 0 || !this._discord.isConnected) return;
try { try {
this._discord.destroy().then(() => { this._discord.destroy().then(() => {
this._discord.isConnected = false; this._discord.isConnected = false;
console.log('[DiscordRPC][disconnect] Disconnected from discord.') console.log('[DiscordRPC][disconnect] Disconnected from discord.')
}).catch((e : any) => console.error(`[DiscordRPC][disconnect] ${e}`)); }).catch((e: any) => console.error(`[DiscordRPC][disconnect] ${e}`));
} catch (err) { } catch (err) {
console.error(err) console.error(err)
} }
} }
@ -54,54 +56,57 @@ export default class DiscordRPCPlugin {
* Sets the activity of the client * Sets the activity of the client
* @param {object} attributes * @param {object} attributes
*/ */
private updateActivity(attributes : any) { private updateActivity(attributes: any) {
if (this._win.store.store.general.discord_rpc == 0) return; if (this._store.general.discord_rpc == 0) return;
if (!this._discord.isConnected) { if (!this._discord.isConnected) {
this._discord.clearActivity().catch((e : any) => console.error(`[DiscordRPC][updateActivity] ${e}`)); this._discord.clearActivity().catch((e: any) => console.error(`[DiscordRPC][updateActivity] ${e}`));
return; return;
} }
// console.log('[DiscordRPC][updateActivity] Updating Discord Activity.') // console.log('[DiscordRPC][updateActivity] Updating Discord Activity.')
const listenURL = `https://cider.sh/p?s&id=${attributes.playParams.id}` // cider://play/s/[id] (for song) const listenURL = `https://cider.sh/p?s&id=${attributes.playParams.id}` // cider://play/s/[id] (for song)
//console.log(attributes) //console.log(attributes)
interface ActObject { interface ActObject extends DiscordRPC.Presence {
details?: any, details?: any,
state?: any, state?: any,
startTimestamp?: any, startTimestamp?: any,
endTimestamp?: any, endTimestamp?: any,
largeImageKey? : any, largeImageKey?: any,
largeImageText?: any, largeImageText?: any,
smallImageKey?: any, smallImageKey?: any,
smallImageText?: any, smallImageText?: any,
instance: true, instance: true,
buttons?: [ buttons?: [
{ label: "Listen on Cider", url?: any }, {
label: string,
url: string
}
] ]
} }
let ActivityObject : ActObject | null = { let ActivityObject: ActObject | null = {
details: attributes.name, details: attributes.name,
state: `by ${attributes.artistName}`, state: `by ${attributes.artistName}`,
startTimestamp: attributes.startTime, startTimestamp: attributes.startTime,
endTimestamp: attributes.endTime, 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, largeImageText: attributes.albumName,
smallImageKey: (attributes.status ? 'play' : 'pause'), smallImageKey: (attributes.status ? 'play' : 'pause'),
smallImageText: (attributes.status ? 'Playing' : 'Paused'), smallImageText: (attributes.status ? 'Playing' : 'Paused'),
instance: true, instance: true,
buttons: [ buttons: [
{ label: "Listen on Cider", url: listenURL }, {label: "Listen on Cider", url: listenURL},
] ]
}; };
if (ActivityObject.largeImageKey == "" || ActivityObject.largeImageKey == null) { if (ActivityObject.largeImageKey == "" || ActivityObject.largeImageKey == null) {
ActivityObject.largeImageKey = (this._win.store.store.general.discord_rpc == 1) ? "cider" : "logo" ActivityObject.largeImageKey = (this._store.general.discord_rpc == 1) ? "cider" : "logo"
} }
// Remove the pause/play icon and test for clear activity on pause // Remove the pause/play icon and test for clear activity on pause
if (this._win.store.store.general.discordClearActivityOnPause == 1) { if (this._store.general.discordClearActivityOnPause == 1) {
delete ActivityObject.smallImageKey delete ActivityObject.smallImageKey
delete ActivityObject.smallImageText delete ActivityObject.smallImageText
} }
@ -128,13 +133,11 @@ export default class DiscordRPCPlugin {
} }
// Check if its pausing (false) or playing (true) // Check if its pausing (false) or playing (true)
if (!attributes.status) { if (!attributes.status) {
if (this._win.store.store.general.discordClearActivityOnPause == 1) { if (this._store.general.discordClearActivityOnPause == 1) {
this._discord.clearActivity().catch((e : any) => console.error(`[DiscordRPC][clearActivity] ${e}`)); this._discord.clearActivity().catch((e: any) => console.error(`[DiscordRPC][clearActivity] ${e}`));
ActivityObject = null ActivityObject = null
} else { } else {
delete ActivityObject.startTimestamp delete ActivityObject.startTimestamp
delete ActivityObject.endTimestamp delete ActivityObject.endTimestamp
@ -168,16 +171,17 @@ export default class DiscordRPCPlugin {
/** /**
* Runs on plugin load (Currently run on application start) * Runs on plugin load (Currently run on application start)
*/ */
constructor(app: any) { constructor(app: any, store: any) {
this._app = app; this._app = app;
} this._store = store
}
/** /**
* Runs on app ready * Runs on app ready
*/ */
onReady(win: any): void { onReady(win: any): void {
this._win = win; this._win = win;
this.connect((this._win.store.store.general.discord_rpc == 1) ? '911790844204437504' : '886578863147192350'); this.connect((this._store.general.discord_rpc == 1) ? '911790844204437504' : '886578863147192350');
// electron.ipcMain.on("forceUpdateRPC", (event, attributes : object) => { // electron.ipcMain.on("forceUpdateRPC", (event, attributes : object) => {
// this.updateActivity(attributes) // this.updateActivity(attributes)
// }); // });

View file

@ -1,7 +1,6 @@
import * as electron from 'electron'; import * as electron from 'electron';
import * as fs from 'fs'; import * as fs from 'fs';
import {resolve} from 'path'; import {resolve} from 'path';
//@ts-ignore
export default class LastFMPlugin { export default class LastFMPlugin {
private sessionPath = resolve(electron.app.getPath('userData'), 'session.json'); private sessionPath = resolve(electron.app.getPath('userData'), 'session.json');
@ -15,6 +14,7 @@ export default class LastFMPlugin {
private _win: any; private _win: any;
private _app: any; private _app: any;
private _lastfm: any; private _lastfm: any;
private _store: any;
private authenticateFromFile() { private authenticateFromFile() {
let sessionData = require(this.sessionPath) let sessionData = require(this.sessionPath)
@ -26,12 +26,12 @@ export default class LastFMPlugin {
authenticate() { authenticate() {
try { try {
if (this._win.store.store.lastfm.auth_token) { if (this._store.lastfm.auth_token) {
this._win.store.store.lastfm.enabled = true; this._store.lastfm.enabled = true;
} }
if (!this._win.store.store.lastfm.enabled || !this._win.store.store.lastfm.auth_token) { if (!this._store.lastfm.enabled || !this._store.lastfm.auth_token) {
this._win.store.store.lastfm.enabled = false; this._store.lastfm.enabled = false;
return return
} }
/// dont move this require to top , app wont load /// dont move this require to top , app wont load
@ -47,8 +47,8 @@ export default class LastFMPlugin {
if (err) { if (err) {
console.error("[LastFM][Session] Session file couldn't be opened or doesn't exist,", err) console.error("[LastFM][Session] Session file couldn't be opened or doesn't exist,", err)
console.log("[LastFM][Auth] Beginning authentication from configuration") console.log("[LastFM][Auth] Beginning authentication from configuration")
console.log("[LastFM][tk]", this._win.store.store.lastfm.auth_token) console.log("[LastFM][tk]", this._store.lastfm.auth_token)
this._lastfm.authenticate(this._win.store.store.lastfm.auth_token, (err: any, session: any) => { this._lastfm.authenticate(this._store.lastfm.auth_token, (err: any, session: any) => {
if (err) { if (err) {
throw err; throw err;
} }
@ -78,7 +78,7 @@ export default class LastFMPlugin {
} }
private async scrobbleSong(attributes: any) { private async scrobbleSong(attributes: any) {
await new Promise(resolve => setTimeout(resolve, Math.round(attributes.durationInMillis * (this._win.store.store.lastfm.scrobble_after / 100)))); await new Promise(resolve => setTimeout(resolve, Math.round(attributes.durationInMillis * (this._store.lastfm.scrobble_after / 100))));
const currentAttributes = attributes; const currentAttributes = attributes;
if (!this._lastfm || this._lastfm.cachedAttributes === attributes) { if (!this._lastfm || this._lastfm.cachedAttributes === attributes) {
@ -117,7 +117,7 @@ export default class LastFMPlugin {
} }
private filterArtistName(artist: any) { private filterArtistName(artist: any) {
if (!this._win.store.store.lastfm.enabledRemoveFeaturingArtists) return artist; if (!this._store.lastfm.enabledRemoveFeaturingArtists) return artist;
artist = artist.split(' '); artist = artist.split(' ');
if (artist.includes('&')) { if (artist.includes('&')) {
@ -135,7 +135,7 @@ export default class LastFMPlugin {
} }
private updateNowPlayingSong(attributes: any) { private updateNowPlayingSong(attributes: any) {
if (!this._lastfm || this._lastfm.cachedNowPlayingAttributes === attributes || !this._win.store.store.lastfm.NowPlaying) { if (!this._lastfm || this._lastfm.cachedNowPlayingAttributes === attributes || !this._store.lastfm.NowPlaying) {
return return
} }
@ -177,8 +177,9 @@ export default class LastFMPlugin {
/** /**
* Runs on plugin load (Currently run on application start) * Runs on plugin load (Currently run on application start)
*/ */
constructor(app: any) { constructor(app: any, store: any) {
this._app = app; this._app = app;
this._store = store
electron.app.on('second-instance', (_e: any, argv: any) => { electron.app.on('second-instance', (_e: any, argv: any) => {
// Checks if first instance is authorized and if second instance has protocol args // Checks if first instance is authorized and if second instance has protocol args
argv.forEach((value: any) => { argv.forEach((value: any) => {
@ -187,8 +188,8 @@ export default class LastFMPlugin {
let authURI = String(argv).split('/auth/')[1]; let authURI = String(argv).split('/auth/')[1];
if (authURI.startsWith('lastfm')) { // If we wanted more auth options if (authURI.startsWith('lastfm')) { // If we wanted more auth options
const authKey = authURI.split('lastfm?token=')[1]; const authKey = authURI.split('lastfm?token=')[1];
this._win.store.store.lastfm.enabled = true; this._store.lastfm.enabled = true;
this._win.store.store.lastfm.auth_token = authKey; this._store.lastfm.auth_token = authKey;
console.log(authKey); console.log(authKey);
this._win.win.webContents.send('LastfmAuthenticated', authKey); this._win.win.webContents.send('LastfmAuthenticated', authKey);
this.authenticate(); this.authenticate();
@ -203,8 +204,8 @@ export default class LastFMPlugin {
let authURI = String(arg).split('/auth/')[1]; let authURI = String(arg).split('/auth/')[1];
if (authURI.startsWith('lastfm')) { // If we wanted more auth options if (authURI.startsWith('lastfm')) { // If we wanted more auth options
const authKey = authURI.split('lastfm?token=')[1]; const authKey = authURI.split('lastfm?token=')[1];
this._win.store.store.lastfm.enabled = true; this._store.lastfm.enabled = true;
this._win.store.store.lastfm.auth_token = authKey; this._store.lastfm.auth_token = authKey;
this._win.win.webContents.send('LastfmAuthenticated', authKey); this._win.win.webContents.send('LastfmAuthenticated', authKey);
console.log(authKey); console.log(authKey);
this.authenticate(); this.authenticate();

196
src/main/plugins/mpris.ts Normal file
View file

@ -0,0 +1,196 @@
// @ts-ignore
import * as Player from 'mpris-service';
export default class MPRIS {
/**
* Private variables for interaction in plugins
*/
private _win: any;
private _app: any;
/**
* Base Plugin Details (Eventually implemented into a GUI in settings)
*/
public name: string = 'MPRIS Service';
public description: string = 'Handles MPRIS service calls for Linux systems.';
public version: string = '1.0.0';
public author: string = 'Core';
/**
* MPRIS Service
*/
private mpris: any;
private mprisEvents: Object = {
"playpause": "pausePlay",
"play": "pausePlay",
"pause": "pausePlay",
"next": "nextTrack",
"previous": "previousTrack",
}
/*******************************************************************************************
* Private Methods
* ****************************************************************************************/
/**
* Runs a media event
* @param type - pausePlay, nextTrack, PreviousTrack
* @private
*/
private runMediaEvent(type: string) {
if (this._win) {
this._win.webContents.executeJavaScript(`MusicKitInterop.${type}()`).catch(console.error)
}
}
/**
* Blocks non-linux systems from running this plugin
* @private
*/
private static linuxOnly(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
if (process.platform !== 'linux') {
descriptor.value = function () {
return
}
}
}
/**
* Connects to MPRIS Service
*/
@MPRIS.linuxOnly
private connect() {
this.mpris = Player({
name: 'Cider',
identity: 'Cider',
supportedUriSchemes: [],
supportedMimeTypes: [],
supportedInterfaces: ['player']
});
this.mpris = Object.assign(this.mpris, {
canQuit: true,
canControl: true,
canPause: true,
canPlay: true,
canGoNext: true,
active: true
})
const pos_atr = {durationInMillis: 0};
this.mpris.getPosition = function () {
const durationInMicro = pos_atr.durationInMillis * 1000;
const percentage = parseFloat("0") || 0;
return durationInMicro * percentage;
}
for (const [key, value] of Object.entries(this.mprisEvents)) {
this.mpris.on(key, () => {
this.runMediaEvent(value)
});
}
}
/**
* Update MPRIS Player Attributes
*/
@MPRIS.linuxOnly
private updatePlayer(attributes: any) {
const MetaData = {
'mpris:trackid': this.mpris.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'),
'xesam:title': `${attributes.name}`,
'xesam:album': `${attributes.albumName}`,
'xesam:artist': [`${attributes.artistName}`,],
'xesam:genre': attributes.genreNames
}
if (this.mpris.metadata["mpris:trackid"] === MetaData["mpris:trackid"]) {
return
}
this.mpris.metadata = MetaData
}
/**
* Update MPRIS Player State
* @private
* @param attributes
*/
@MPRIS.linuxOnly
private updatePlayerState(attributes: any) {
let status = 'Stopped';
if (attributes.status) {
status = 'Playing';
} else if (attributes.status === false) {
status = 'Paused';
}
if (this.mpris.playbackStatus === status) {
return
}
this.mpris.playbackStatus = status;
}
/**
* Clear state
* @private
*/
private clearState() {
this.mpris.metadata = {'mpris:trackid': '/org/mpris/MediaPlayer2/TrackList/NoTrack'}
this.mpris.playbackStatus = 'Stopped';
}
/*******************************************************************************************
* Public Methods
* ****************************************************************************************/
/**
* Runs on plugin load (Currently run on application start)
*/
constructor(app: any, _store: any) {
this._app = app;
console.log(`[${this.name}] plugin loaded`);
}
/**
* Runs on app ready
*/
onReady(win: any): void {
this._win = win;
console.log(`[${this.name}] plugin ready`);
this.connect()
}
/**
* Runs on app stop
*/
onBeforeQuit(): void {
console.log(`[${this.name}] plugin stopped`);
this.clearState()
}
/**
* Runs on playback State Change
* @param attributes Music Attributes (attributes.state = current state)
*/
onPlaybackStateDidChange(attributes: object): void {
this.updatePlayerState(attributes)
}
/**
* Runs on song change
* @param attributes Music Attributes
*/
onNowPlayingItemDidChange(attributes: object): void {
this.updatePlayer(attributes);
}
}

View file

@ -1,5 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"experimentalDecorators": true,
"target": "esnext", "target": "esnext",
"module": "commonjs", "module": "commonjs",
"noImplicitAny": true, "noImplicitAny": true,