from PyQt4 import QtCore from traceback import * from clSong import Song from traceback import print_exc from misc import * import mpd class Monty(QtCore.QObject): """The Monty class offers another layer above pympd, with usefull events.""" _client=None # MPD client _listeners=None # array of listeners: { event: (listeners)* } # cached objects _curLib=None _curPlaylist=None _curSong=None # objects used for comparison with previous value _curSongID=None _curTime=None _curState=None _curVolume=None _timerID=None events={ 'onSongChange':'oldSongID, newSongID', 'onTimeChange':'oldTime, newTime', 'onStateChange':'oldState, newState', 'onVolumeChange':'oldVolume, newVolume', 'onConnect':'', 'onDisconnect':'', 'onReady':'', # when connected, and initialisation is ready } def __init__(self): QtCore.QObject.__init__(self) self._client=None self._listeners={} self._curSongID=-1 self._curTime=-1 self._curState=-1 self._curVolume=-1 self._curLib=[] self._curPlaylist=[] for event in self.events: self._listeners[event]=[] def connect(self, host, port): """Connect to MPD@$host:$port. Returns true at success, false otherwise.""" if self._client: return self._client = mpd.MPDClient() try: self._client.connect(host, port) except: self._client=None return False self._raiseEvent('onConnect', None) try: self._updateLib() self._updatePlaylist() self._updateCurrentSong() self._timerID=self.startTimer(300) except Exception: print_exc() self._raiseEvent('onStateChange', {'oldState':'stop', 'newState':self.getStatus()['state']}) self._raiseEvent('onReady', None) doEvents() return True def disconnect(self): """Disconnect from MPD.""" if self._client: self._client.close() self._client.disconnect() self._client=None # don't kill timer, as it'll happen in timerEvent def isConnected(self): """Returns true if we're connected to MPD, false otherwise.""" return self._client!=None def listPlaylist(self): """Returns the current playlist.""" if self.isConnected()==False: return None return self._curPlaylist def listLibrary(self): """Returns the library.""" if self.isConnected()==False: return None return self._curLib def getCurrentSong(self): """Returns the current playing song.""" if self.isConnected()==False: return None return self._curSong def getStatus(self): """Returns the status.""" try: if self.isConnected()==False: return None ret=self._retrieve(self._client.status) if 'time' in ret: len=int(ret['time'][ret['time'].find(':')+1:]) cur=int(ret['time'][:ret['time'].find(':')]) ret['length']=len ret['time']=cur return ret except Exception, d: print_exc() return None _retrMutex=QtCore.QMutex() def _retrieve(self, method): """Makes sure only one call is made at a time to MPD.""" self._retrMutex.lock() try: ret=method() except: self._retrMutex.unlock() raise self._retrMutex.unlock() return ret def play(self, id): """Play song with ID $id.""" if id!=None: self._client.playid(id) else: self._client.playid() def pause(self): """Pause playing.""" self._client.pause(1) def resume(self): """Resume playing.""" self._client.pause(0) def next(self): """Move on to the next song in the playlist.""" self._client.next() def previous(self): """Move back to the previous song in the playlist.""" self._client.previous() def stop(self): """Stop playing.""" self._client.stop() def seek(self, time): """Move the current playing time to $time.""" self._client.seekid(self._curSongID, time) def deleteFromPlaylist(self, list): """Remove all songIDs in $list from the playlist.""" self._client.command_list_ok_begin() for id in list: self._client.deleteid(id) self._client.command_list_end() self._updatePlaylist() def addToPlaylist(self, paths): """Add all files in $paths to the current playlist.""" self._client.command_list_ok_begin() for path in paths: self._client.add(path) self._client.command_list_end() self._updatePlaylist() def setVolume(self, volume): """Set volumne to $volume.""" self._client.setvol(volume) def addListener(self, event, callback): """Add $callback to the listeners for $event.""" if not(event in self.events): raise Exception("Unknown event "+event) self._listeners[event].append(callback) def _updateLib(self): """Update the library.""" self._curLib=self._arrayToSongArray(self._retrieve(self._client.listallinfo)) id=0 for song in self._curLib: song._data['id']=id id+=1 def _updatePlaylist(self): """Update the playlist.""" self._curPlaylist=self._arrayToSongArray(self._retrieve(self._client.playlistinfo)) def _arrayToSongArray(self, array): """Convert an array to an array of Songs.""" return map(lambda entry: Song(entry) , filter(lambda entry: not('directory' in entry), array) ) def _updateCurrentSong(self): """Update the current song.""" self._curSong=self._retrieve(self._client.currentsong) if self._curSong==None: return self._curSong=Song(self._curSong) def _raiseEvent(self, event, params): """Call all listeners for $event with parameters $params.""" if not(event in self.events): raise Exception("Unknown raised event "+event) for listener in self._listeners[event]: listener(params) def timerEvent(self, event): "Check for changes since last check." try: self._updateCurrentSong() status=self.getStatus() except: self._curSong=None song=self._curSong if song==None or status==None: self._client=None self._raiseEvent('onDisconnect', None) self.killTimer(self._timerID) return " check if song has changed" if song.getID()>=0: curID=song.getID() if curID!=self._curSongID: self._raiseEvent('onSongChange', {'oldSongID':self._curSongID, 'newSongID':curID}) self._curSongID=curID " check if the time has changed" if 'time' in status: curTime=status['time'] if curTime!=self._curTime: self._raiseEvent('onTimeChange', {'oldTime':self._curTime, 'newTime':curTime}) self._curTime=curTime " check if the playing state has changed" if 'state' in status: curState=status['state'] if curState!=self._curState: self._raiseEvent('onStateChange', {'oldState':self._curState, 'newState':curState}) self._curState=curState " check if the volume has changed" if 'state' in status: curVolume=int(status['volume']) if curVolume!=self._curVolume: self._raiseEvent('onVolumeChange', {'oldVolume':self._curVolume, 'newVolume':curVolume}) self._curVolume=curVolume monty=Monty()