import { NuclearStatus , NuclearMeta , NuclearPlaylist , PlaybackStatus , IpcEvents } from '@nuclear/core' ; import autobind from 'autobind-decorator' ; import { app , IpcMain , Event } from 'electron' ; import { inject } from 'inversify' ; import MprisService , { MprisPlaylist , MprisMeta , PlaybackStatus as MprisStatus , LoopStatus } from 'mpris-service' ; import NuclearApi from '../../interfaces/nuclear-api' ; import { systemMediaController , systemMediaEvent , SYSTEM_MEDIA_EVENT_KEY } from '../../utils/decorators' ; import { ControllerMeta } from '../../utils/types' ; import Config from '../config' ; import Discord from '../discord' ; import $ipc from '../ipc' ; import Logger , { $systemApiLogger } from '../logger' ; import Store from '../store' ; import Window from '../window' ; const statusMapper = { [ PlaybackStatus . PLAYING ] : MprisService . PLAYBACK_STATUS_PLAYING , [ PlaybackStatus . PAUSED ] : MprisService . PLAYBACK_STATUS_PAUSED } ; const loopStatusMapper = { true : MprisService . LOOP_STATUS_PLAYLIST , false : MprisService . LOOP_STATUS_NONE } ; see https see https see https see https @ systemMediaController ( ) class LinuxMediaService extends MprisService implements NuclearApi { tracks : MprisMeta [ ] ; constructor ( @ inject ( Config ) config , @ inject ( Discord ) private discord , @ inject ( $ipc ) private ipc , @ inject ( $systemApiLogger ) private logger , @ inject ( Store ) private store , @ inject ( Window ) private window ) { super ( { name : config . appid , identity : config . title , supportedMimeTypes : config . supportedFormats . map ( format => `audio/${ format }` ) , supportedUriSchemes : [ 'file' , 'uri' ] , supportedInterfaces : [ 'player' , 'trackList' , 'playlists' ] } ) ; this . tracks = [ ] ; const storedPlaylists = this . store . get ( 'playlists' ) ; this . setPlaylists ( storedPlaylists ? storedPlaylists . map ( this . playlistMapper ) : [ ] ) ; } private getPlayingStatus ( ) { return new Promise ( resolve => { this . window . send ( IpcEvents . PLAYING_STATUS ) ; this . ipc . once ( IpcEvents . PLAYING_STATUS , ( evt , data ) => { resolve ( data ) ; } ) ; } ) ; } @ autobind private trackMapper ( track , index = 0 ) { return { id : track . uuid , 'mpris:trackid' : this . objectPath ( `track/${ index }` ) , 'mpris:artUrl' : track . thumbnail , 'xesam:title' : track . name , 'xesam:artist' : [ track . artist ] } ; } @ autobind private playlistMapper ( playlist , index = 0 ) { return { Id : this . objectPath ( `playlist/${ index }` ) , Name : playlist . name , Icon : playlist . tracks [ 0 ] . thumbnail || '' } ; } @ systemMediaEvent ( 'activatePlaylist' ) onActivatePlaylist ( playlistId ) { this . setActivePlaylist ( playlistId ) ; this . window . send ( IpcEvents . PLAYLIST_ACTIVATE , this . playlists [ this . getPlaylistIndex ( playlistId ) ] . Name ) ; } @ systemMediaEvent ( 'raise' ) onRaise ( ) { this . window . focus ( ) ; } @ systemMediaEvent ( 'quit' ) onQuit ( ) { app . quit ( ) ; } @ systemMediaEvent ( 'shuffle' ) onShuffle ( ) { this . shuffle = ! this . shuffle ; this . window . send ( IpcEvents . SETTINGS , { shuffleQueue : this . shuffle } ) ; } @ systemMediaEvent ( 'loopStatus' ) onLoop ( ) { this . loopStatus = this . loopStatus === MprisService . LOOP_STATUS_PLAYLIST ? MprisService . LOOP_STATUS_NONE : MprisService . LOOP_STATUS_PLAYLIST ; this . window . send ( IpcEvents . SETTINGS , { loopAfterQueueEnd : this . loopStatus === MprisService . LOOP_STATUS_PLAYLIST } ) ; } @ systemMediaEvent ( 'play' ) onPlay ( ) { this . playbackStatus = MprisService . PLAYBACK_STATUS_PLAYING ; this . discord . play ( ) ; this . window . send ( IpcEvents . PLAY ) ; } @ systemMediaEvent ( 'pause' ) onPause ( ) { this . playbackStatus = MprisService . PLAYBACK_STATUS_PAUSED ; this . discord . pause ( ) ; this . window . send ( IpcEvents . PAUSE ) ; } @ systemMediaEvent ( 'stop' ) onStop ( ) { this . playbackStatus = MprisService . PLAYBACK_STATUS_STOPED ; this . discord . clear ( ) ; this . window . send ( IpcEvents . STOP ) ; } @ systemMediaEvent ( 'playpause' ) onPlayPause ( ) { if ( ! this . metadata [ 'xesam:title' ] ) { return ; } if ( this . playbackStatus === MprisService . PLAYBACK_STATUS_PLAYING ) { this . playbackStatus = MprisService . PLAYBACK_STATUS_PAUSED ; this . discord . pause ( ) ; } else { this . playbackStatus = MprisService . PLAYBACK_STATUS_PLAYING ; this . discord . play ( ) ; } this . window . send ( IpcEvents . PLAYPAUSE ) ; } @ systemMediaEvent ( 'volume' ) onVolume ( data ) { this . volume = data ; this . window . send ( IpcEvents . VOLUME , data * 100 ) ; } @ systemMediaEvent ( 'next' ) onNext ( ) { this . window . send ( IpcEvents . NEXT ) ; } @ systemMediaEvent ( 'previous' ) onPrevious ( ) { this . window . send ( IpcEvents . PREVIOUS ) ; } @ systemMediaEvent ( 'goTo' ) onSelectTrack ( trackId ) { this . window . send ( IpcEvents . TRACK_SELECT , this . getTrackIndex ( trackId ) ) ; } sendMetadata ( track ) { this . metadata = this . trackMapper ( track ) ; } addTrack ( track ) { this . tracks = [ ... this . tracks , this . trackMapper ( track , this . tracks . length ) ] ; } removeTrack ( uuid ) { this . tracks = this . tracks . filter ( ( track ) => uuid !== track . id ) ; } clearTrackList ( ) { this . tracks = [ ] ; this . sendMetadata ( { artist : 'Nuclear' , name : '' , uuid : '0' , thumbnail : '' , streams : [ ] } ) ; } play ( ) { this . playbackStatus = MprisService . PLAYBACK_STATUS_PLAYING ; } setVolume ( volume ) { this . volume = volume / 100 ; } pause ( ) { this . playbackStatus = MprisService . PLAYBACK_STATUS_PAUSED ; } setLoopStatus ( data ) { this . loopStatus = loopStatusMapper [ data . toString ( ) ] ; } async listen ( ) { const status = await this . getPlayingStatus ( ) ; this . canControl = true ; this . canEditTracks = true ; this . playbackStatus = statusMapper [ status . playbackStatus ] ; this . volume = status . volume / 100 ; this . shuffle = status . shuffleQueue ; this . loopStatus = loopStatusMapper [ status . loopAfterQueueEnd . toString ( ) ] ; const meta = Reflect . getMetadata ( SYSTEM_MEDIA_EVENT_KEY , LinuxMediaService . prototype ) ; meta . forEach ( ( { eventName , name } ) => { this . on ( eventName , ( event , data ) => { this . logger . logEvent ( { direction : 'in' , event : eventName , data } ) ; const result = ( this as any ) [ name ] ( event , data ) ; if ( result instanceof Promise ) { result . catch ( ( err ) => { this . logger . error ( `error in event ${ eventName } => ${ err . message }` ) ; } ) ; } } ) ; } ) ; } } export default LinuxMediaService ;