from PyQt4 import QtCore import mpd from threading import Thread import socket from clSong import Song class MPClient(QtCore.QObject): """This class offers another layer above pympd, with usefull events.""" _client = None # MPD client # cached objects _cur_lib = None _cur_playlist = None _cur_song = None # objects used for comparison with previous value _cur_songid = None _cur_time = None _cur_state = None _cur_volume = None _updatings_db = None _cur_plist_id = None _timer_id = None _retr_mutex = QtCore.QMutex() def __init__(self): QtCore.QObject.__init__(self) self._client = None self._cur_songid = -1 self._cur_time = -1 self._cur_state = -1 self._cur_volume = -1 self._cur_lib = [] self._cur_playlist = [] self._cur_plist_id = -1 def connect_mpd(self, host, port): """Connect to MPD@host:port. Returns Tue at success, False otherwise.""" if self._client: return True try: self._client = mpd.MPDClient() self._client.connect(host, port) except socket.error: self._client = None return False self.emit(QtCore.SIGNAL('connected')) self._update_lib() self._update_playlist() self._update_current_song() self._timer_id = self.startTimer(500) self.emit(QtCore.SIGNAL('state_changed'), 'stop', self.status()['state']) self.emit(QtCore.SIGNAL('ready')) return True def disconnect_mpd(self): """Disconnect from MPD.""" if self._client: self._client.close() self._client.disconnect() self._client = None def is_connected(self): """Returns True if connected to MPD, False otherwise.""" return self._client != None def playlist(self): """Returns the current playlist.""" if not self.is_connected(): return [] return self._cur_playlist def library(self): """Returns current library.""" if not self.is_connected(): return [] return self._cur_lib def current_song(self): """Returns the current playing song.""" if not self.is_connected(): return None return self._cur_song def update_db(self, paths = None): """Starts MPD database update.""" if not paths: return self._client.update() self._client.command_list_ok_begin() for path in paths: self._client.update(path) self._client.command_list_end() def status(self): """Get current status""" if self.is_connected() == False: return None ret = self._retrieve(self._client.status) if not ret: return None 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 else: ret['length'] = 0 ret['time'] = 0 return ret def outputs(self): """Returns an array of configured MPD audio outputs.""" if self.is_connected(): return self._retrieve(self._client.outputs) else: return [] def set_output(self, output_id, state): """Set audio output output_id to state (0/1).""" if state: self._client.enableoutput(output_id) else: self._client.disableoutput(output_id) def urlhandlers(self): """Returns an array of available url handlers.""" if not self.is_connected(): return [] else: return self._client.urlhandlers() def repeat(self, val): """Set repeat playlist to val (True/False).""" if isinstance(val, bool): val = 1 if val else 0 self._client.repeat(val) def random(self, val): """Set random playback to val (True, False).""" if isinstance(val, bool): val = 1 if val else 0 self._client.random(val) def is_playing(self): """Returns True if MPD is playing, False otherwise.""" return self.status()['state'] == 'play' def play(self, id = None): """Play song with ID id or next song if id is None.""" self._playCalled = True if id: 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._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): """Seek to time.""" if self._cur_songid > 0: self._client.seekid(self._cur_songid, time) def delete(self, list): """Remove all song IDs 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._update_playlist() def clear(self): """Clear current playlist.""" self._client.clear() self._update_playlist() def add(self, paths): """Add all files in paths to the current playlist.""" try: self._client.command_list_ok_begin() for path in paths: self._client.addid(path.encode('utf-8')) ret = self._client.command_list_end() self._update_playlist() if self._cur_state == 'stop': self.play(ret[0]) except mpd.CommandError: logging.error('Cannot add some files, check permissions.') def volume(self): return int(self.status()['volume']) def set_volume(self, volume): """Set volume to volume.""" volume = min(100, max(0, volume)) self._client.setvol(volume) def _retrieve(self, method): """Makes sure only one call is made at a time to MPD.""" self._retr_mutex.lock() try: ret = method() except socket.error: self._retr_mutex.unlock() self._client = None return None self._retr_mutex.unlock() return ret def _update_lib(self): """Update the cached library.""" self._cur_lib = self._array_to_song_array(self._retrieve(self._client.listallinfo)) id = 0 for song in self._cur_lib: song._data['id'] = id id += 1 def _update_playlist(self): """Update the cached playlist.""" self._cur_playlist = self._array_to_song_array(self._retrieve(self._client.playlistinfo)) def _array_to_song_array(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 _update_current_song(self): """Update the current song.""" song = self._retrieve(self._client.currentsong) if not song: self._cur_song = None else: self._cur_song = Song(song) def timerEvent(self, event): """Check for changes since last check.""" status = self.status() if not status: self._client = None self.emit(QtCore.SIGNAL('disconnected')) self.killTimer(self._timer_id) return # check if song has changed self._update_current_song() song = self._cur_song if song: cur_id = song.getID() else: cur_id = -1 if cur_id != self._cur_songid: self.emit(QtCore.SIGNAL('song_changed'), cur_id) self._cur_songid = cur_id # check if the time has changed if 'time' in status: cur_time = status['time'] if cur_time != self._cur_time: self.emit(QtCore.SIGNAL('time_changed'), cur_time) self._cur_time = cur_time # check if the playing state has changed if 'state' in status: cur_state = status['state'] if cur_state != self._cur_state: self.emit(QtCore.SIGNAL('state_changed'), cur_state) self._cur_state = cur_state #check if the volume has changed if 'volume' in status: cur_vol = int(status['volume']) if cur_vol != self._cur_volume: self.emit(QtCore.SIGNAL('volume_changed'), cur_vol) self._cur_volume = cur_vol if 'playlist' in status: cur_plist_id = int(status['playlist']) if cur_plist_id != self._cur_plist_id: self._update_playlist() self.emit(QtCore.SIGNAL('playlist_changed')) self._cur_plist_id = cur_plist_id " update has started" if 'updatings_db' in status and self._updatings_db == None: self._updatings_db = status['updatings_db'] self.emit(QtCore.SIGNAL('update_started')) if not('updatings_db' in status) and self._updatings_db: self._updatings_db = None self.emit(QtCore.SIGNAL('update_finished'))