// @ts-ignore import * as Player from 'mpris-service'; export default class mpris { /** * Private variables for interaction in plugins */ private static utils: any; /** * MPRIS Service */ private static player: Player.Player; /** * 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'; /******************************************************************************************* * Private Methods * ****************************************************************************************/ /** * Runs on plugin load (Currently run on application start) */ constructor(utils: any) { mpris.utils = utils console.debug(`[Plugin][${mpris.name}] Loading Complete.`); } /** * Blocks non-linux systems from running this plugin * @private * @decorator */ private static linuxOnly(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) { if (process.platform !== 'linux') { descriptor.value = function () { return } } } /** * Connects to MPRIS Service */ private static connect() { const player = Player({ name: 'cider', identity: 'Cider', supportedInterfaces: ['player'] }); console.debug(`[${mpris.name}:connect] Successfully connected.`); const renderer = mpris.utils.getWindow().webContents const loopType: { [key: string]: number; } = { 'none': 0, 'track': 1, 'playlist': 2, } 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 / 1000 / 1000)) 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')) mpris.utils.getIPCMain().on('mpris:playbackTimeDidChange', (event: any, time: number) => { player.getPosition = () => time; }) 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; } /** * Update M.P.R.I.S Player Attributes */ private static updateMetaData(attributes: any) { 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'), 'xesam:title': `${attributes.name}`, 'xesam:album': `${attributes.albumName}`, 'xesam:artist': [`${attributes.artistName}`], 'xesam:genre': attributes.genreNames }; } /******************************************************************************************* * Public Methods * ****************************************************************************************/ /** * Clear state * @private */ private static clearState() { if (!mpris.player) { return } mpris.player.metadata = {'mpris:trackid': '/org/mpris/MediaPlayer2/TrackList/NoTrack'} mpris.player.playbackStatus = Player.PLAYBACK_STATUS_STOPPED; } /** * Runs on app ready */ @mpris.linuxOnly onReady(_: any): void { console.debug(`[${mpris.name}:onReady] Ready.`); } /** * Renderer ready */ @mpris.linuxOnly onRendererReady(): void { mpris.connect() } /** * Runs on app stop */ @mpris.linuxOnly onBeforeQuit(): void { console.debug(`[Plugin][${mpris.name}] Stopped.`); mpris.clearState() } /** * Runs on playback State Change * @param attributes Music Attributes (attributes.status = current state) */ @mpris.linuxOnly onPlaybackStateDidChange(attributes: any): void { mpris.player.playbackStatus = attributes?.status ? Player.PLAYBACK_STATUS_PLAYING : Player.PLAYBACK_STATUS_PAUSED } /** * Runs on song change * @param attributes Music Attributes */ @mpris.linuxOnly onNowPlayingItemDidChange(attributes: object): void { mpris.updateMetaData(attributes); } }