from PyQt4 import QtCore from traceback import * from clSong import Song from traceback import print_exc from misc import * import jmpc from threading import Thread 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 _updatings_db=None _timerID=None events={ 'beforeSongChange':'curSongID', 'onSongChange':'oldSongID, newSongID', 'onTimeChange':'oldTime, newTime', 'onStateChange':'oldState, newState', 'onVolumeChange':'oldVolume, newVolume', 'onConnect':'', 'onDisconnect':'', 'onReady':'', # when connected, and initialisation is ready 'onUpdateDBStart':'', # start updating database 'onUpdateDBFinish':'', # when updating database has finished } 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 = jmpc.jmpc() 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(500) 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 updateDB(self, paths): self._client.command_list_ok_begin() for path in paths: self._client.update(path) self._client.command_list_end() 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 def repeat(self,val): self._client.repeat(val) def random(self,val): self._client.random(val) _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.""" self._playCalled=True 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._playCalled=False self._raiseEvent('beforeSongChange', {'curSongID': self._curSongID}) # we only switch to the next song, if some of beforeSongChange's listeners # didn't explicitly call play. If it did, then it ain't good to immediatly # skip to the next song! if not self._playCalled: 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) class simpleThread(Thread): callback=None params=None def __init__(self,callback,params): Thread.__init__(self) self.callback=callback self.params=params def run(self): self.callback(self.params) 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]: try: self.simpleThread(listener, params).run() except: print_exc() 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 if curTime>=status['length']-1: self._raiseEvent('beforeSongChange', {'curSongID':curID}) " 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 'volume' in status: curVolume=int(status['volume']) if curVolume!=self._curVolume: self._raiseEvent('onVolumeChange', {'oldVolume':self._curVolume, 'newVolume':curVolume}) self._curVolume=curVolume " update has started" if 'updatings_db' in status and self._updatings_db==None: self._updatings_db=status['updatings_db'] self._raiseEvent('onUpdateDBStart', {}) if not('updatings_db' in status) and self._updatings_db: self._updatings_db=None self._raiseEvent('onUpdateDBFinish') monty=Monty()