
Implementation of MPRIS in TS. LastFM now interacts with store object passed directly into class. Experimental decorators enabled and utilised in MPRIS.
213 lines
7.2 KiB
TypeScript
213 lines
7.2 KiB
TypeScript
import * as DiscordRPC from 'discord-rpc'
|
|
|
|
export default class DiscordRPCPlugin {
|
|
/**
|
|
* Private variables for interaction in plugins
|
|
*/
|
|
private _win: Electron.BrowserWindow | undefined;
|
|
private _app: any;
|
|
private _store: any;
|
|
private _discord: any;
|
|
|
|
private connect(clientId: any) {
|
|
this._discord = {isConnected: false};
|
|
if (this._store.general.discord_rpc == 0 || this._discord.isConnected) return;
|
|
|
|
DiscordRPC.register(clientId) // Apparently needed for ask to join, join, spectate etc.
|
|
const client = new DiscordRPC.Client({transport: "ipc"});
|
|
this._discord = Object.assign(client, {error: false, activityCache: null, isConnected: false});
|
|
|
|
// Login to Discord
|
|
this._discord.login({clientId})
|
|
.then(() => {
|
|
this._discord.isConnected = true;
|
|
})
|
|
.catch((e: any) => console.error(`[DiscordRPC][connect] ${e}`));
|
|
|
|
this._discord.on('ready', () => {
|
|
console.log(`[DiscordRPC][connect] Successfully Connected to Discord. Authed for user: ${client.user.username} (${client.user.id})`);
|
|
})
|
|
|
|
// Handles Errors
|
|
this._discord.on('error', (err: any) => {
|
|
console.error(`[DiscordRPC] ${err}`);
|
|
this.disconnect()
|
|
this._discord.isConnected = false;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Disconnects from Discord RPC
|
|
*/
|
|
private disconnect() {
|
|
if (this._store.general.discord_rpc == 0 || !this._discord.isConnected) return;
|
|
|
|
try {
|
|
this._discord.destroy().then(() => {
|
|
this._discord.isConnected = false;
|
|
console.log('[DiscordRPC][disconnect] Disconnected from discord.')
|
|
}).catch((e: any) => console.error(`[DiscordRPC][disconnect] ${e}`));
|
|
} catch (err) {
|
|
console.error(err)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the activity of the client
|
|
* @param {object} attributes
|
|
*/
|
|
private updateActivity(attributes: any) {
|
|
if (this._store.general.discord_rpc == 0) return;
|
|
|
|
if (!this._discord.isConnected) {
|
|
this._discord.clearActivity().catch((e: any) => console.error(`[DiscordRPC][updateActivity] ${e}`));
|
|
return;
|
|
}
|
|
|
|
// console.log('[DiscordRPC][updateActivity] Updating Discord Activity.')
|
|
|
|
const listenURL = `https://cider.sh/p?s&id=${attributes.playParams.id}` // cider://play/s/[id] (for song)
|
|
//console.log(attributes)
|
|
|
|
interface ActObject extends DiscordRPC.Presence {
|
|
details?: any,
|
|
state?: any,
|
|
startTimestamp?: any,
|
|
endTimestamp?: any,
|
|
largeImageKey?: any,
|
|
largeImageText?: any,
|
|
smallImageKey?: any,
|
|
smallImageText?: any,
|
|
instance: true,
|
|
buttons?: [
|
|
{
|
|
label: string,
|
|
url: string
|
|
}
|
|
]
|
|
}
|
|
|
|
let ActivityObject: ActObject | null = {
|
|
details: attributes.name,
|
|
state: `by ${attributes.artistName}`,
|
|
startTimestamp: attributes.startTime,
|
|
endTimestamp: attributes.endTime,
|
|
largeImageKey: (attributes.artwork.url.replace('{w}', '1024').replace('{h}', '1024')) ?? 'cider',
|
|
largeImageText: attributes.albumName,
|
|
smallImageKey: (attributes.status ? 'play' : 'pause'),
|
|
smallImageText: (attributes.status ? 'Playing' : 'Paused'),
|
|
instance: true,
|
|
buttons: [
|
|
{label: "Listen on Cider", url: listenURL},
|
|
]
|
|
};
|
|
if (ActivityObject.largeImageKey == "" || ActivityObject.largeImageKey == null) {
|
|
ActivityObject.largeImageKey = (this._store.general.discord_rpc == 1) ? "cider" : "logo"
|
|
}
|
|
|
|
// Remove the pause/play icon and test for clear activity on pause
|
|
if (this._store.general.discordClearActivityOnPause == 1) {
|
|
delete ActivityObject.smallImageKey
|
|
delete ActivityObject.smallImageText
|
|
}
|
|
|
|
// Deletes the timestamp if its not greater than 0
|
|
if (!((new Date(attributes.endTime)).getTime() > 0)) {
|
|
delete ActivityObject.startTimestamp
|
|
delete ActivityObject.endTimestamp
|
|
}
|
|
|
|
// Artist check
|
|
if (!attributes.artistName) {
|
|
delete ActivityObject.state
|
|
}
|
|
|
|
// Album text check
|
|
if (!ActivityObject.largeImageText || ActivityObject.largeImageText.length < 2) {
|
|
delete ActivityObject.largeImageText
|
|
}
|
|
|
|
// Checks if the name is greater than 128 because some songs can be that long
|
|
if (ActivityObject.details.length > 128) {
|
|
ActivityObject.details = ActivityObject.details.substring(0, 125) + '...'
|
|
}
|
|
|
|
|
|
// Check if its pausing (false) or playing (true)
|
|
if (!attributes.status) {
|
|
if (this._store.general.discordClearActivityOnPause == 1) {
|
|
this._discord.clearActivity().catch((e: any) => console.error(`[DiscordRPC][clearActivity] ${e}`));
|
|
ActivityObject = null
|
|
} else {
|
|
delete ActivityObject.startTimestamp
|
|
delete ActivityObject.endTimestamp
|
|
ActivityObject.smallImageKey = 'pause'
|
|
ActivityObject.smallImageText = 'Paused'
|
|
}
|
|
}
|
|
|
|
|
|
if (ActivityObject && ActivityObject !== this._discord.activityCache && ActivityObject.details && ActivityObject.state) {
|
|
try {
|
|
// console.log(`[DiscordRPC][setActivity] Setting activity to ${JSON.stringify(ActivityObject)}`);
|
|
this._discord.setActivity(ActivityObject)
|
|
this._discord.activityCache = ActivityObject
|
|
} catch (err) {
|
|
console.error(`[DiscordRPC][setActivity] ${err}`)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Base Plugin Details (Eventually implemented into a GUI in settings)
|
|
*/
|
|
public name: string = 'DiscordRPCPlugin';
|
|
public description: string = 'Discord RPC plugin for Cider';
|
|
public version: string = '0.0.1';
|
|
public author: string = 'vapormusic / Cider Collective';
|
|
|
|
/**
|
|
* Runs on plugin load (Currently run on application start)
|
|
*/
|
|
constructor(app: any, store: any) {
|
|
this._app = app;
|
|
this._store = store
|
|
}
|
|
|
|
/**
|
|
* Runs on app ready
|
|
*/
|
|
onReady(win: any): void {
|
|
this._win = win;
|
|
this.connect((this._store.general.discord_rpc == 1) ? '911790844204437504' : '886578863147192350');
|
|
// electron.ipcMain.on("forceUpdateRPC", (event, attributes : object) => {
|
|
// this.updateActivity(attributes)
|
|
// });
|
|
}
|
|
|
|
/**
|
|
* Runs on app stop
|
|
*/
|
|
onBeforeQuit(): void {
|
|
console.log('Example plugin stopped');
|
|
}
|
|
|
|
/**
|
|
* Runs on playback State Change
|
|
* @param attributes Music Attributes (attributes.state = current state)
|
|
*/
|
|
onPlaybackStateDidChange(attributes: object): void {
|
|
this.updateActivity(attributes)
|
|
}
|
|
|
|
/**
|
|
* Runs on song change
|
|
* @param attributes Music Attributes
|
|
*/
|
|
onNowPlayingItemDidChange(attributes: object): void {
|
|
this.updateActivity(attributes)
|
|
}
|
|
|
|
}
|