From 12fee3df36c783f070dd9f5fdd957402bd4b4aaa Mon Sep 17 00:00:00 2001 From: Core <64542347+coredev-uk@users.noreply.github.com> Date: Tue, 12 Jul 2022 21:26:05 +0100 Subject: [PATCH] mpris overhaul --- src/main/base/plugins.ts | 14 ++-- src/main/base/utils.ts | 3 + src/main/index.ts | 4 +- src/main/plugins/mpris.ts | 142 +++++++++++++++-------------------- src/preload/cider-preload.js | 14 +++- 5 files changed, 84 insertions(+), 93 deletions(-) diff --git a/src/main/base/plugins.ts b/src/main/base/plugins.ts index 24402278..5873af4c 100644 --- a/src/main/base/plugins.ts +++ b/src/main/base/plugins.ts @@ -16,10 +16,10 @@ import {utils} from './utils'; * @see {@link https://github.com/ciderapp/Cider/wiki/Plugins|Documentation} */ export class Plugins { + private static PluginMap: any = {}; private basePluginsPath = path.join(__dirname, '../plugins'); private userPluginsPath = path.join(electron.app.getPath('userData'), 'Plugins'); private readonly pluginsList: any = {}; - private static PluginMap: any = {}; constructor() { this.pluginsList = this.getPlugins(); @@ -35,8 +35,8 @@ export class Plugins { public getPlugins(): any { let plugins: any = {}; - - + + if (fs.existsSync(this.basePluginsPath)) { fs.readdirSync(this.basePluginsPath).forEach(file => { if (file.endsWith('.ts') || file.endsWith('.js')) { @@ -49,8 +49,8 @@ export class Plugins { } }); } - - + + if (fs.existsSync(this.userPluginsPath)) { fs.readdirSync(this.userPluginsPath).forEach(file => { // Plugins V1 @@ -104,9 +104,9 @@ export class Plugins { public callPlugins(event: string, ...args: any[]) { for (const plugin in this.pluginsList) { if (this.pluginsList[plugin][event]) { - try{ + try { this.pluginsList[plugin][event](...args); - }catch(e) { + } catch (e) { console.error(`[${plugin}] An error was encountered: ${e}`); console.error(e) } diff --git a/src/main/base/utils.ts b/src/main/base/utils.ts index f2108528..da47e917 100644 --- a/src/main/base/utils.ts +++ b/src/main/base/utils.ts @@ -26,6 +26,9 @@ export class utils { }, previous: () => { bw.win.webContents.executeJavaScript("MusicKitInterop.previous()") + }, + seek: (seconds: number) => { + bw.win.webContents.executeJavaScript(`MusicKit.getInstance().seekToTime(${seconds})`) } } /** diff --git a/src/main/index.ts b/src/main/index.ts index 4c13a1a8..be6aadce 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -76,8 +76,8 @@ ipcMain.on("nowPlayingItemDidChange", (_event, attributes) => { CiderPlug.callPlugins("onNowPlayingItemDidChange", attributes); }); -ipcMain.on("nowPlayingItemDidChangeLastFM", (_event, attributes) => { - CiderPlug.callPlugin("lastfm.js", "nowPlayingItemDidChangeLastFM", attributes); +ipcMain.on("updatePlaybackProgress", (_event, attributes) => { + CiderPlug.callPlugins("updatePlaybackProgress", attributes); }) app.on("before-quit", () => { diff --git a/src/main/plugins/mpris.ts b/src/main/plugins/mpris.ts index 6b70c5c8..4dc4f83b 100644 --- a/src/main/plugins/mpris.ts +++ b/src/main/plugins/mpris.ts @@ -6,6 +6,11 @@ export default class mpris { * Private variables for interaction in plugins */ private static utils: any; + /** + * MPRIS Service + */ + private static player: Player.Player; + private static globalAttributes: any = {} /** * Base Plugin Details (Eventually implemented into a GUI in settings) @@ -15,30 +20,17 @@ export default class mpris { public version: string = '1.0.0'; public author: string = 'Core'; - /** - * MPRIS Service - */ - private static player: Player.Player; - private static mprisEvents: Object = { - "playpause": "playPause", - "play": "play", - "pause": "pause", - "next": "next", - "previous": "previous", - } - /******************************************************************************************* * Private Methods * ****************************************************************************************/ /** - * Runs a media event - * @param type - pausePlay, next, previous - * @private + * Runs on plugin load (Currently run on application start) */ - private static runMediaEvent(type: string) { - // console.debug(`[Plugin][${this.name}] ${type}.`); - mpris.utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.${type}()`).catch(console.error) + constructor(utils: any) { + mpris.utils = utils + + console.debug(`[Plugin][${mpris.name}] Loading Complete.`); } /** @@ -54,7 +46,6 @@ export default class mpris { } } - /** * Connects to MPRIS Service */ @@ -63,29 +54,45 @@ export default class mpris { const player = Player({ name: 'cider', identity: 'Cider', - supportedUriSchemes: [], - supportedMimeTypes: [], supportedInterfaces: ['player'] }); - console.debug(`[Plugin][${mpris.name}] Successfully connected.`); + console.debug(`[${mpris.name}:connect] Successfully connected.`); - const pos_atr = {durationInMillis: 0}; - player.getPosition = function () { - const durationInMicro = pos_atr.durationInMillis * 1000; - const percentage = parseFloat("0") || 0; - return durationInMicro * percentage; + const renderer = mpris.utils.getWindow().webContents + const loopType: { [key: string]: number; } = { + 'none': 0, + 'track': 1, + 'playlist': 2, } - for (const [key, value] of Object.entries(mpris.mprisEvents)) { - player.on(key, function () { - mpris.runMediaEvent(value) - }); - } + player.on('next', () => mpris.utils.playback.next()) + player.on('previous', () => mpris.utils.playback.previous()) + player.on('playpause', () => mpris.utils.playback.playPause()) + player.on('play', () => mpris.utils.playback.play()) + player.on('pause', () => mpris.utils.playback.pause()) + player.on('quit', () => mpris.utils.getApp().exit()) + player.on('position', (args: { position: any; }) => mpris.utils.playback.seek(args.position)) + player.on('loopStatus', (status: string) => renderer.executeJavaScript(`app.mk.repeatMode = ${loopType[status.toLowerCase()]}`)) + player.on('shuffle', () => renderer.executeJavaScript('app.mk.shuffleMode = (app.mk.shuffleMode === 0) ? 1 : 0')) - player.on('quit', function () { - process.exit(); - }); + mpris.utils.getIPCMain().on('repeatModeDidChange', (_e: any, mode: number) => { + switch (mode) { + case 0: + player.loopStatus = Player.LOOP_STATUS_NONE; + break; + case 1: + player.loopStatus = Player.LOOP_STATUS_TRACK; + break; + case 2: + player.loopStatus = Player.LOOP_STATUS_PLAYLIST; + break; + } + }) + + mpris.utils.getIPCMain().on('shuffleModeDidChange', (_e: any, mode: number) => { + player.shuffle = mode === 1 + }) mpris.player = player; } @@ -93,9 +100,9 @@ export default class mpris { /** * Update M.P.R.I.S Player Attributes */ - private static updatePlayer(attributes: any) { + private static updateMetaData(attributes: any) { - const MetaData = { + mpris.player.metadata = { 'mpris:trackid': mpris.player.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'), @@ -103,33 +110,12 @@ export default class mpris { 'xesam:album': `${attributes.albumName}`, 'xesam:artist': [`${attributes.artistName}`], 'xesam:genre': attributes.genreNames - } - - if (mpris.player.metadata["mpris:trackid"] === MetaData["mpris:trackid"]) { - return - } - - mpris.player.metadata = MetaData; + }; } - /** - * Update M.P.R.I.S Player State - * @private - * @param attributes - */ - private static updatePlayerState(attributes: any) { - switch (attributes.status) { - case true: // Playing - mpris.player.playbackStatus = Player.PLAYBACK_STATUS_PLAYING; - break; - case false: // Paused - mpris.player.playbackStatus = Player.PLAYBACK_STATUS_PAUSED; - break; - default: - mpris.player.playbackStatus = Player.PLAYBACK_STATUS_STOPPED; - break - } - } + /******************************************************************************************* + * Public Methods + * ****************************************************************************************/ /** * Clear state @@ -143,26 +129,12 @@ export default class mpris { mpris.player.playbackStatus = Player.PLAYBACK_STATUS_STOPPED; } - - /******************************************************************************************* - * Public Methods - * ****************************************************************************************/ - - /** - * Runs on plugin load (Currently run on application start) - */ - constructor(utils: any) { - mpris.utils = utils - - console.debug(`[Plugin][${mpris.name}] Loading Complete.`); - } - /** * Runs on app ready */ @mpris.linuxOnly onReady(_: any): void { - console.debug(`[Plugin][${mpris.name}] Ready.`); + console.debug(`[${mpris.name}:onReady] Ready.`); } /** @@ -187,9 +159,9 @@ export default class mpris { * @param attributes Music Attributes (attributes.status = current state) */ @mpris.linuxOnly - onPlaybackStateDidChange(attributes: object): void { - // console.debug(`[Plugin][${mpris.name}] onPlaybackStateDidChange.`); - mpris.updatePlayerState(attributes) + onPlaybackStateDidChange(attributes: any): void { + mpris.globalAttributes = attributes + mpris.player.playbackStatus = attributes?.status ? Player.PLAYBACK_STATUS_PLAYING : Player.PLAYBACK_STATUS_PAUSED } /** @@ -198,8 +170,14 @@ export default class mpris { */ @mpris.linuxOnly onNowPlayingItemDidChange(attributes: object): void { - // console.debug(`[Plugin][${mpris.name}] onMetadataDidChange.`); - mpris.updatePlayer(attributes); + mpris.globalAttributes = attributes + mpris.updateMetaData(attributes); + } + + @mpris.linuxOnly + updatePlaybackProgress(attributes: any): void { + mpris.globalAttributes = attributes + mpris.player.getPosition = () => attributes.currentPlaybackTime * 1000 * 1000; } } diff --git a/src/preload/cider-preload.js b/src/preload/cider-preload.js index add40edf..54e0e26b 100644 --- a/src/preload/cider-preload.js +++ b/src/preload/cider-preload.js @@ -17,6 +17,7 @@ const MusicKitInterop = { /** wsapi */ MusicKit.getInstance().addEventListener(MusicKit.Events.playbackProgressDidChange, () => { ipcRenderer.send('wsapi-updatePlaybackState', MusicKitInterop.getAttributes()); + ipcRenderer.send('updatePlaybackProgress', MusicKitInterop.getAttributes()); }); /** wsapi */ @@ -38,11 +39,19 @@ const MusicKitInterop = { MusicKit.getInstance().addEventListener(MusicKit.Events.authorizationStatusDidChange, () => { global.ipcRenderer.send('authorizationStatusDidChange', MusicKit.getInstance().authorizationStatus) - }) + }); MusicKit.getInstance().addEventListener(MusicKit.Events.mediaPlaybackError, (e) => { console.warn(`[cider:preload] mediaPlaybackError] ${e}`); - }) + }); + + MusicKit.getInstance().addEventListener(MusicKit.Events.shuffleModeDidChange, () => { + global.ipcRenderer.send('shuffleModeDidChange', MusicKit.getInstance().shuffleMode) + }); + + MusicKit.getInstance().addEventListener(MusicKit.Events.repeatModeDidChange, () => { + global.ipcRenderer.send('repeatModeDidChange', MusicKit.getInstance().repeatMode) + }); }, sleep(ms) { @@ -79,6 +88,7 @@ const MusicKitInterop = { ? remainingTimeExport * 1000 : 0; attributes.durationInMillis = attributes?.durationInMillis ?? 0; + attributes.currentPlaybackTime = mk?.currentPlaybackTime ?? 0; attributes.currentPlaybackProgress = currentPlaybackProgress ?? 0; attributes.startTime = Date.now(); attributes.endTime = Math.round(