from PyQt4 import QtCore import mpd import socket from song import Song class MPClient(QtCore.QObject): """This class offers another layer above pympd, with usefull events.""" _client = None _cur_lib = None _cur_playlist = None _cur_song = None _status = {'volume' : None, 'repeat' : None, 'random' : None, 'songid' : None, 'playlist' : None, 'playlistlength' : None, 'state' : None, 'time' : None, 'length' : None, 'xfade' : None, 'updatings_db' : None} _timer_id = None _retr_mutex = QtCore.QMutex() def __init__(self): QtCore.QObject.__init__(self) self._cur_lib = [] self._cur_playlist = [] 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._status = MPClient._status self._update_lib() self._update_playlist() self._update_current_song() self.emit(QtCore.SIGNAL('connected')) self.timerEvent(None) self._timer_id = self.startTimer(500) 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 status(self): """Get current MPD status.""" return self._status 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 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._status and self._status['songid'] > 0: self._client.seekid(self._status['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._status['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 _update_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: cur, len = ret['time'].split(':') ret['length'] = int(len) ret['time'] = int(cur) else: ret['length'] = 0 ret['time'] = 0 if not 'updatings_db' in ret: ret['updatings_db'] = 0 if not 'songid' in ret: ret['songid'] = -1 return ret def timerEvent(self, event): """Check for changes since last check.""" old_status = self._status self._status = self._update_status() if not self._status: self._client = None self.emit(QtCore.SIGNAL('disconnected')) self.killTimer(self._timer_id) return self._update_current_song() if self._status['songid'] != old_status['songid']: self.emit(QtCore.SIGNAL('song_changed'), self._status['songid']) if self._status['time'] != old_status['time']: self.emit(QtCore.SIGNAL('time_changed'), self._status['time']) if self._status['state'] != old_status['state']: self.emit(QtCore.SIGNAL('state_changed'), self._status['state']) if self._status['volume'] != old_status['volume']: self.emit(QtCore.SIGNAL('volume_changed'), int(self._status['volume'])) if self._status['playlist'] != old_status['playlist']: self._update_playlist() self.emit(QtCore.SIGNAL('playlist_changed')) if self._status['updatings_db'] and not old_status['updatings_db']: self.emit(QtCore.SIGNAL('update_started')) if not self._status['updatings_db'] and old_status['updatings_db']: self.emit(QtCore.SIGNAL('update_finished'))