From f22dab76b500a22109a734fa7dafca9d50a24725 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Mon, 9 Aug 2010 18:47:03 +0200 Subject: switch to the new MPD interaction layer remove the old mpclient and our bundled copy of mpd.py --- nephilim/mpclient.py | 491 +--------------------------------------- nephilim/mpd.py | 357 ----------------------------- nephilim/plugins/AlbumCover.py | 6 +- nephilim/plugins/Library.py | 3 +- nephilim/plugins/Lyrics.py | 4 +- nephilim/plugins/Notify.py | 2 +- nephilim/plugins/PlayControl.py | 6 +- nephilim/plugins/Playlist.py | 12 +- nephilim/plugins/Songinfo.py | 2 +- nephilim/plugins/Systray.py | 10 +- nephilim/settings_wg.py | 2 +- nephilim/song.py | 12 +- nephilim/winMain.py | 14 +- 13 files changed, 34 insertions(+), 887 deletions(-) delete mode 100644 nephilim/mpd.py diff --git a/nephilim/mpclient.py b/nephilim/mpclient.py index c118cdb..b3cd027 100644 --- a/nephilim/mpclient.py +++ b/nephilim/mpclient.py @@ -1,6 +1,5 @@ # -# Copyright (C) 2008 jerous -# Copyright (C) 2009 Anton Khirnov +# Copyright (C) 2010 Anton Khirnov # # Nephilim is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,497 +17,13 @@ from PyQt4 import QtCore, QtNetwork from PyQt4.QtCore import pyqtSignal as Signal, pyqtSlot as Slot -import mpd -import socket import logging -from song import Song, PlaylistEntryRef +from song import Song from mpdsocket import MPDSocket -class MPClient(QtCore.QObject): - """This class offers another layer above pympd, with usefull events.""" - # public, read-only - logger = None - - # these don't change while mpd is running - outputs = None - tagtypes = None - urlhandlers = None - commands = None - - # private - __password = None - _client = None - _cur_song = None - _status = {'volume' : 0, 'repeat' : 0, 'random' : 0, - 'songid' : 0, 'playlist' : 0, 'playlistlength' : 0, - 'time' : 0, 'length' : 0, 'xfade' : 0, - 'state' : 'stop', 'single' : 0, - 'consume' : 0} - - _timer_id = None #for querying status changes - _db_timer_id = None #for querying db updates - _db_update = None #time of last db update - - __stats = {'artists': '0', 'albums' : '0', 'songs' : '0', 'uptime' : '0', - 'playtime' : '0', 'db_playtime' : '0', 'db_update' : '0'} - - # SIGNALS - connect_changed = QtCore.pyqtSignal(bool) - db_updated = QtCore.pyqtSignal() - song_changed = QtCore.pyqtSignal(object) - time_changed = QtCore.pyqtSignal(int) - state_changed = QtCore.pyqtSignal(str) - volume_changed = QtCore.pyqtSignal(int) - repeat_changed = QtCore.pyqtSignal(bool) - random_changed = QtCore.pyqtSignal(bool) - single_changed = QtCore.pyqtSignal(bool) - consume_changed = QtCore.pyqtSignal(bool) - playlist_changed = QtCore.pyqtSignal() - - - def __init__(self): - QtCore.QObject.__init__(self) - self.logger = logging.getLogger('mpclient') - self.__update_static() - self._status = dict(MPClient._status) - - def connect_mpd(self, host, port, password = None): - """Connect to MPD@host:port, optionally using password.""" - self.logger.info('Connecting to MPD...') - if self._client: - self.logger.warning('Attempted to connect when already connected.') - return - - self._client = mpd.MPDClient() - self._client.connect_changed.connect(lambda val:self.__finish_connect() if val else self.__finish_disconnect()) - self._client.connect_mpd(host, port) - self.__password = password - - def disconnect_mpd(self): - """Disconnect from MPD.""" - self.logger.info('Disconnecting from MPD...') - if self._client: - self._client.disconnect_mpd() - - def password(self, password): - """Use the password to authenticate with MPD.""" - self.logger.info('Authenticating with MPD.') - if not self.__check_command_ok('password'): - return - try: - self._client.password(password) - self.logger.info('Successfully authenticated') - self.__update_static() - except mpd.CommandError: - self.logger.error('Incorrect MPD password.') - def is_connected(self): - """Returns True if connected to MPD, False otherwise.""" - return self._client != None - - def status(self): - """Get current MPD status.""" - return self._status - def playlistinfo(self): - """Returns a list of songs in current playlist.""" - self.logger.info('Listing current playlist.') - if not self.__check_command_ok('playlistinfo'): - raise StopIteration - for song in self._client.playlistinfo(): - yield Song(song) - raise StopIteration - def library(self): - """Returns a list of all songs in library.""" - self.logger.info('Listing library.') - if not self.__check_command_ok('listallinfo'): - raise StopIteration - for song in self._client.listallinfo(): - if 'file' in song: - yield Song(song) - - raise StopIteration - def current_song(self): - """Returns the current playing song.""" - return self._cur_song - def is_playing(self): - """Returns True if MPD is playing, False otherwise.""" - return self._status['state'] == 'play' - def find(self, *args): - if not self.__check_command_ok('find'): - raise StopIteration - for song in self._client.find(*args): - yield Song(song) - raise StopIteration - def findadd(self, *args): - """Find tracks with given tags and add them to playlist. Takes - a list of (tag, value).""" - self.logger.info('Findadd %s.'%unicode(args)) - if not self.__check_command_ok('findadd'): - return - return self._client.findadd(*args) - def playlistid(self, plid): - """Return a song with a given playlist id.""" - self.logger.info('Getting id %s.'%('of id %s'%(plid) if plid else '')) - if not self.__check_command_ok('play'): - return - ret = None - for it in self._client.playlistid(plid): - ret = Song(it) - return ret - - def update_db(self, paths = None): - """Starts MPD database update.""" - self.logger.info('Updating database %s'%(paths if paths else '.')) - if not self.__check_command_ok('update'): - return - if not paths: - return self._client.update() - self._client.command_list_ok_begin() - for path in paths: - self._client.update(path) - list(self._client.command_list_end()) - - def volume(self): - """Get current volume.""" - return int(self._status['volume']) - def set_volume(self, volume): - """Set volume to volume.""" - self.logger.info('Setting volume to %d.'%volume) - if not self.__check_command_ok('setvol'): - return - volume = min(100, max(0, volume)) - try: - self._client.setvol(volume) - except mpd.CommandError, e: - self.logger.warning('Error setting volume (probably no outputs enabled): %s.'%e) - - def stats(self): - """Get MPD statistics.""" - if not self.__check_command_ok('stats'): - return self.__stats - return self._client.stats() - - def repeat(self, val): - """Set repeat playlist to val (True/False).""" - self.logger.info('Setting repeat to %d.'%val) - if not self.__check_command_ok('repeat'): - return - 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).""" - self.logger.info('Setting random to %d.'%val) - if not self.__check_command_ok('random'): - return - if isinstance(val, bool): - val = 1 if val else 0 - self._client.random(val) - def crossfade(self, time): - """Set crossfading between songs.""" - self.logger.info('Setting crossfade to %d'%time) - if not self.__check_command_ok('crossfade'): - return - self._client.crossfade(time) - def single(self, val): - """Set single playback to val (True, False)""" - self.logger.info('Setting single to %d.'%val) - if not self.__check_command_ok('single'): - return - if isinstance(val, bool): - val = 1 if val else 0 - self._client.single(val) - def consume(self, val): - """Set consume mode to val (True, False)""" - self.logger.info('Setting consume to %d.'%val) - if not self.__check_command_ok('consume'): - return - if isinstance(val, bool): - val = 1 if val else 0 - self._client.consume(val) - - def play(self, id = None): - """Play song with ID id or next song if id is None.""" - self.logger.info('Starting playback %s.'%('of id %s'%(id) if id else '')) - if not self.__check_command_ok('play'): - return - if id: - self._client.playid(id) - else: - self._client.playid() - def pause(self): - """Pause playing.""" - self.logger.info('Pausing playback.') - if not self.__check_command_ok('pause'): - return - self._client.pause(1) - def resume(self): - """Resume playing.""" - self.logger.info('Resuming playback.') - if not self.__check_command_ok('pause'): - return - self._client.pause(0) - def next(self): - """Move on to the next song in the playlist.""" - self.logger.info('Skipping to next song.') - if not self.__check_command_ok('next'): - return - self._client.next() - def previous(self): - """Move back to the previous song in the playlist.""" - self.logger.info('Moving to previous song.') - if not self.__check_command_ok('previous'): - return - self._client.previous() - def stop(self): - """Stop playing.""" - self.logger.info('Stopping playback.') - if not self.__check_command_ok('stop'): - return - self._client.stop() - def seek(self, time): - """Seek to time (in seconds).""" - self.logger.info('Seeking to %d.'%time) - if not self.__check_command_ok('seekid'): - return - if self._status['songid'] > 0: - self._client.seekid(self._status['songid'], time) - - def delete(self, ids): - """Remove all song IDs in list from the playlist.""" - if not self.__check_command_ok('deleteid'): - return - self._client.command_list_ok_begin() - try: - for id in ids: - self.logger.info('Deleting id %s from playlist.'%id) - self._client.deleteid(id) - list(self._client.command_list_end()) - except mpd.CommandError, e: - self.logger.error('Error deleting files: %s.'%e) - def clear(self): - """Clear current playlist.""" - self.logger.info('Clearing playlist.') - if not self.__check_command_ok('clear'): - return - self._client.clear() - def add(self, paths, pos = -1): - """Add all files in paths to the current playlist.""" - if not self.__check_command_ok('addid'): - return - ret = None - self._client.command_list_ok_begin() - for path in paths: - self.logger.info('Adding %s to playlist'%path) - if pos < 0: - self._client.addid(path) - else: - self._client.addid(path, pos) - pos += 1 - try: - ret = list(self._client.command_list_end()) - except mpd.CommandError, e: - self.logger.error('Error adding files: %s.'%e) - if self._status['state'] == 'stop' and ret: - self.play(ret[0]) - def move(self, source, target): - """Move the songs in playlist. Takes one source id and one target position.""" - self.logger.info('Moving %s to %s.'%(source, target)) - if not self.__check_command_ok('moveid'): - return - self._client.moveid(source, target) - - #### private #### - def __finish_connect(self): - if self.__password: - self.password(self.__password) - else: - self.__update_static() - - if not self.__check_command_ok('listallinfo'): - self.logger.error('Don\'t have MPD read permission, diconnecting.') - return self.disconnect_mpd() - - self.__update_current_song() - self._db_update = self.stats()['db_update'] - - self.connect_changed.emit(True) - self.logger.info('Successfully connected to MPD.') - self._timer_id = self.startTimer(500) - self._db_timer_id = self.startTimer(1000) - def __finish_disconnect(self): - self._client = None - - if self._timer_id: - self.killTimer(self._timer_id) - self._timer_id = None - if self._db_timer_id: - self.killTimer(self._db_timer_id) - self._db_timer_id = None - self._status = dict(MPClient._status) - self._cur_song = None - self.__update_static() - self.connect_changed.emit(False) - self.logger.info('Disconnected from MPD.') - def __update_current_song(self): - """Update the current song.""" - song = self._client.currentsong() - if not song: - self._cur_song = None - else: - self._cur_song = Song(song) - def _update_status(self): - """Get current status""" - if not self._client: - return None - ret = self._client.status() - if not ret: - return None - - ret['repeat'] = int(ret['repeat']) - ret['random'] = int(ret['random']) - ret['single'] = int(ret['single']) - ret['consume'] = int(ret['consume']) - ret['volume'] = int(ret['volume']) - 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 'songid' in ret: - ret['songid'] = '-1' - - return ret - def __check_command_ok(self, cmd): - if not self._client: - return self.logger.info('Not connected.') - if not cmd in self.commands: - return self.logger.error('Command %s not accessible'%cmd) - return True - - def __update_static(self): - """Update static values, called on connect/disconnect.""" - if self._client: - self.commands = list(self._client.commands()) - else: - self.commands = [] - - if self.__check_command_ok('outputs'): - outputs = [] - for output in self._client.outputs(): - outputs.append(AudioOutput(self, output['outputname'], output['outputid'], - bool(output['outputenabled']))) - self.outputs = outputs - else: - self.outputs = [] - - if self.__check_command_ok('tagtypes'): - self.tagtypes = map(unicode.lower, self._client.tagtypes()) + ['file'] - else: - self.tagtypes = [] - - if self.__check_command_ok('urlhandlers'): - self.urlhandlers = list(self._client.urlhandlers()) - else: - self.urlhandlers = [] - - def set_output(self, output_id, state): - """Set audio output output_id to state (0/1). Called only by AudioOutput.""" - if not self.__check_command_ok('enableoutput'): - return - if state: - self._client.enableoutput(output_id) - else: - self._client.disableoutput(output_id) - - def timerEvent(self, event): - """Check for changes since last check.""" - if event.timerId() == self._db_timer_id: - #timer for monitoring db changes - db_update = self.stats()['db_update'] - if db_update > self._db_update: - self.logger.info('Database updated.') - self._db_update = db_update - self.db_updated.emit() - return - - - old_status = self._status - self._status = self._update_status() - - if not self._status: - self.logger.error('Error reading status.') - return self.disconnect_mpd() - - if self._status['songid'] != old_status['songid']: - self.__update_current_song() - self.song_changed.emit(PlaylistEntryRef(self, self._status['songid'])) - - if self._status['time'] != old_status['time']: - self.time_changed.emit(self._status['time']) - - if self._status['state'] != old_status['state']: - self.state_changed.emit(self._status['state']) - - if self._status['volume'] != old_status['volume']: - self.volume_changed.emit( int(self._status['volume'])) - - if self._status['repeat'] != old_status['repeat']: - self.repeat_changed.emit(bool(self._status['repeat'])) - - if self._status['random'] != old_status['random']: - self.random_changed.emit(bool(self._status['random'])) - - if self._status['single'] != old_status['single']: - self.single_changed.emit(bool(self._status['single'])) - - if self._status['consume'] != old_status['consume']: - self.consume_changed.emit(bool(self._status['consume'])) - - if self._status['playlist'] != old_status['playlist']: - self.playlist_changed.emit() - - outputs = list(self._client.outputs()) - for i in range(len(outputs)): - if int(outputs[i]['outputenabled']) != int(self.outputs[i].state): - self.outputs[i].mpd_toggle_state() - - class AudioOutput(QtCore.QObject): """This class represents an MPD audio output.""" - # public, const - mpclient = None - name = None - id = None - state = None - - # SIGNALS - state_changed = QtCore.pyqtSignal(bool) - - #### public #### - def __init__(self, mpclient, name, id, state): - QtCore.QObject.__init__(self) - - self.mpclient = mpclient - self.name = name - self.id = id - self.state = state - - @QtCore.pyqtSlot(bool) - def set_state(self, state): - self.mpclient.set_output(self.id, state) - - #### private #### - def mpd_toggle_state(self): - """This is called by mpclient to inform about output state change.""" - self.state = not self.state - self.state_changed.emit(self.state) - -class AudioOutput2(QtCore.QObject): - """This class represents an MPD audio output.""" #### PUBLIC #### # constants @@ -566,7 +81,7 @@ class MPDStatus(dict): except ValueError: self['audio'] = (0, 0, 0) -class MPClient2(QtCore.QObject): +class MPClient(QtCore.QObject): """ A high-level MPD interface. It is mostly asynchronous -- all responses from MPD are read via callbacks. A callback may be None, in which case the data diff --git a/nephilim/mpd.py b/nephilim/mpd.py deleted file mode 100644 index a9671c3..0000000 --- a/nephilim/mpd.py +++ /dev/null @@ -1,357 +0,0 @@ -# Python MPD client library -# Copyright (C) 2008 J. Alexander Treuman -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import socket -import logging -from PyQt4 import QtCore, QtNetwork - - -HELLO_PREFIX = "OK MPD " -ERROR_PREFIX = "ACK " -SUCCESS = "OK" -NEXT = "list_OK" - - -class MPDError(Exception): - pass - -class ConnectionError(MPDError): - pass - -class ProtocolError(MPDError): - pass - -class CommandError(MPDError): - pass - -class CommandListError(MPDError): - pass - - -class _NotConnected(object): - def __getattr__(self, attr): - return self._dummy - - def _dummy(*args): - raise ConnectionError("Not connected") - -class MPDClient(QtCore.QObject): - # public - logger = None - mpd_version = None - - # private - __sock = None - _commandlist = None - - # SIGNALS - connect_changed = QtCore.pyqtSignal(bool) - def __init__(self): - QtCore.QObject.__init__(self) - self.logger = logging.getLogger('mpclient.mpdsocket') - self._commands = { - # Admin Commands - "disableoutput": self._getnone, - "enableoutput": self._getnone, - "kill": None, - "update": self._getitem, - # Informational Commands - "status": self._getobject, - "stats": self._getobject, - "outputs": self._getoutputs, - "commands": self._getlist, - "notcommands": self._getlist, - "tagtypes": self._getlist, - "urlhandlers": self._getlist, - # Database Commands - "find": self._getsongs, - "findadd": self._getnone, - "list": self._getlist, - "listall": self._getdatabase, - "listallinfo": self._getdatabase, - "lsinfo": self._getdatabase, - "search": self._getsongs, - "count": self._getobject, - # Playlist Commands - "add": self._getnone, - "addid": self._getitem, - "clear": self._getnone, - "currentsong": self._getobject, - "delete": self._getnone, - "deleteid": self._getnone, - "load": self._getnone, - "rename": self._getnone, - "move": self._getnone, - "moveid": self._getnone, - "playlist": self._getplaylist, - "playlistinfo": self._getsongs, - "playlistid": self._getsongs, - "plchanges": self._getsongs, - "plchangesposid": self._getchanges, - "rm": self._getnone, - "save": self._getnone, - "shuffle": self._getnone, - "swap": self._getnone, - "swapid": self._getnone, - "listplaylist": self._getlist, - "listplaylistinfo": self._getsongs, - "playlistadd": self._getnone, - "playlistclear": self._getnone, - "playlistdelete": self._getnone, - "playlistmove": self._getnone, - "playlistfind": self._getsongs, - "playlistsearch": self._getsongs, - # Playback Commands - "consume": self._getnone, - "crossfade": self._getnone, - "next": self._getnone, - "pause": self._getnone, - "play": self._getnone, - "playid": self._getnone, - "previous": self._getnone, - "random": self._getnone, - "repeat": self._getnone, - "seek": self._getnone, - "seekid": self._getnone, - "setvol": self._getnone, - "single": self._getnone, - "stop": self._getnone, - "volume": self._getnone, - # Miscellaneous Commands - "clearerror": self._getnone, - "close": None, - "password": self._getnone, - "ping": self._getnone, - } - - def __getattr__(self, attr): - try: - retval = self._commands[attr] - except KeyError: - raise AttributeError("'%s' object has no attribute '%s'" % - (self.__class__.__name__, attr)) - return lambda *args: self._docommand(attr, args, retval) - - def _docommand(self, command, args, retval): - if not self.__sock: - self.logger.error('Cannot send command: not connected.') - return None - if self._commandlist is not None and not callable(retval): - raise CommandListError("%s not allowed in command list" % command) - - self._writecommand(command, args) - - if self._commandlist is None: - if callable(retval): - return retval() - return retval - self._commandlist.append(retval) - - def _writecommand(self, command, args=[]): - parts = [command] - for arg in args: - parts.append('"%s"' % escape(unicode(arg))) - self.__sock.write(' '.join(parts).encode('utf-8') + '\n') - self.__sock.waitForBytesWritten() - - def _readline(self): - while not self.__sock.canReadLine(): - self.__sock.waitForReadyRead() - line = str(self.__sock.readLine()).decode('utf-8') - line = line.rstrip("\n") - if line.startswith(ERROR_PREFIX): - error = line[len(ERROR_PREFIX):].strip() - raise CommandError(error) - if self._commandlist is not None: - if line == NEXT: - return - if line == SUCCESS: - raise ProtocolError("Got unexpected '%s'" % SUCCESS) - elif line == SUCCESS: - return - return line - - def _readitem(self, separator): - line = self._readline() - if line is None: - return - item = line.split(separator, 1) - if len(item) < 2: - raise ProtocolError("Could not parse item: '%s'" % line) - return item - - def _readitems(self, separator=": "): - item = self._readitem(separator) - while item: - yield item - item = self._readitem(separator) - raise StopIteration - - def _readlist(self): - seen = None - for key, value in self._readitems(): - if key != seen: - if seen is not None: - raise ProtocolError("Expected key '%s', got '%s'" % - (seen, key)) - seen = key - yield value - raise StopIteration - - def _readplaylist(self): - for key, value in self._readitems(":"): - yield value - raise StopIteration - - def _readobjects(self, delimiters=[]): - obj = {} - for key, value in self._readitems(): - key = key.lower() - if obj: - if key in delimiters: - yield obj - obj = {} - elif obj.has_key(key): - if not isinstance(obj[key], list): - obj[key] = [obj[key], value] - else: - obj[key].append(value) - continue - obj[key] = value - if obj: - yield obj - raise StopIteration - - def _readcommandlist(self): - for retval in self._commandlist: - yield retval() - self._commandlist = None - self._getnone() - raise StopIteration - - def _getnone(self): - line = self._readline() - if line is not None: - raise ProtocolError("Got unexpected return value: '%s'" % line) - - def _getitem(self): - items = list(self._readitems()) - if len(items) != 1: - return - return items[0][1] - - def _getlist(self): - return self._readlist() - - def _getplaylist(self): - return self._readplaylist() - - def _getobject(self): - objs = list(self._readobjects()) - if not objs: - return {} - return objs[0] - - def _getobjects(self, delimiters): - return self._readobjects(delimiters) - - def _getsongs(self): - return self._getobjects(["file"]) - - def _getdatabase(self): - return self._getobjects(["file", "directory", "playlist"]) - - def _getoutputs(self): - return self._getobjects(["outputid"]) - - def _getchanges(self): - return self._getobjects(["cpos"]) - - def _getcommandlist(self): - try: - return self._readcommandlist() - except CommandError: - self._commandlist = None - raise - - def __handle_error(self, error): - self.logger.error(self.__sock.errorString()) - self.disconnect_mpd() - - def __finish_connect(self): - # read MPD hello - while not self.__sock.canReadLine(): - self.__sock.waitForReadyRead() - line = str(self.__sock.readLine()) - if not line.startswith(HELLO_PREFIX): - self.logger.error('Got invalid MPD hello: %s' % line) - self.disconnect_mpd() - return - self.mpd_version = line[len(HELLO_PREFIX):].strip() - - self.connect_changed.emit(True) - - def connect_mpd(self, host, port): - if self.__sock: - return self.logger.error('Already connected.') - - if not port: - #assume Unix domain socket - self.__sock = QtNetwork.QLocalSocket(self) - c = lambda host, port: self.__sock.connectToServer(host) - else: - self.__sock = QtNetwork.QTcpSocket(self) - c = self.__sock.connectToHost - - self.__sock.error.connect( self.__handle_error) - self.__sock.connected.connect(self.__finish_connect) - c(host, port) - - def disconnect_mpd(self): - if self.__sock: - try: - self.__sock.disconnectFromHost() - except AttributeError: - self.__sock.disconnectFromServer() - - if self.__sock.state() != QtNetwork.QAbstractSocket.UnconnectedState\ - and not self.__sock.waitForDisconnected(5000): - self.__sock.abort() - - self.__sock = None - - self.mpd_version = None - self._commandlist = None - self.connect_changed.emit(False) - - def command_list_ok_begin(self): - if self._commandlist is not None: - raise CommandListError("Already in command list") - self._writecommand("command_list_ok_begin") - self._commandlist = [] - - def command_list_end(self): - if self._commandlist is None: - raise CommandListError("Not in command list") - self._writecommand("command_list_end") - return self._getcommandlist() - - -def escape(text): - return text.replace("\\", "\\\\").replace('"', '\\"') - - -# vim: set expandtab shiftwidth=4 softtabstop=4 textwidth=79: diff --git a/nephilim/plugins/AlbumCover.py b/nephilim/plugins/AlbumCover.py index cb4bf1e..9d4cf38 100644 --- a/nephilim/plugins/AlbumCover.py +++ b/nephilim/plugins/AlbumCover.py @@ -64,7 +64,7 @@ class AlbumCoverWidget(QtGui.QLabel): self.plugin.cover_changed.emit(QtGui.QPixmap()) return - if song != self.plugin.mpclient.current_song(): + if song != self.plugin.mpclient.cur_song: return self.cover = cover @@ -230,7 +230,7 @@ class AlbumCover(Plugin): self.__results = 0 self.__index = len(self.__fetchers) self.o.cover_loaded = False - song = self.mpclient.current_song() + song = self.mpclient.cur_song if not song: self.__cover_dir = '' self.__cover_path = '' @@ -285,7 +285,7 @@ class AlbumCover(Plugin): def select_cover(self): """Prompt user to manually select cover file for current song.""" - song = self.mpclient.current_song() + song = self.mpclient.cur_song if not song: return self.__abort_fetch() diff --git a/nephilim/plugins/Library.py b/nephilim/plugins/Library.py index 0d7e8e1..458c88c 100644 --- a/nephilim/plugins/Library.py +++ b/nephilim/plugins/Library.py @@ -198,13 +198,12 @@ class LibraryWidget(QtGui.QWidget): self.layout().addWidget(self.search_txt) self.layout().addWidget(self.library_view) - self.plugin.mpclient.connect_changed.connect(self.fill_library) self.plugin.mpclient.db_updated.connect(self.fill_library) def fill_library(self): self.logger.info('Refreshing library.') self.grouping.setText('/'.join(self.settings.value('grouping').toStringList())) - self.library_model.fill(self.plugin.mpclient.library(), self.settings.value('grouping').toStringList()) + self.plugin.mpclient.database(lambda songs: self.library_model.fill(songs, self.settings.value('grouping').toStringList())) @QtCore.pyqtSlot(unicode) def filter_library(self, text): diff --git a/nephilim/plugins/Lyrics.py b/nephilim/plugins/Lyrics.py index f0ca266..61d34f1 100644 --- a/nephilim/plugins/Lyrics.py +++ b/nephilim/plugins/Lyrics.py @@ -87,7 +87,7 @@ class LyricsWidget(QtGui.QWidget): return self.__text_view.clear() # a late thread might call this for a previous song - if song != self.plugin.mpclient.current_song(): + if song != self.plugin.mpclient.cur_song: return self.__text_view.clear() @@ -237,7 +237,7 @@ class Lyrics(Plugin): self.__results = 0 self.__index = len(self.__fetchers) self.o.lyrics_loaded = False - song = self.mpclient.current_song() + song = self.mpclient.cur_song if not song: self.__lyrics_dir = '' self.__lyrics_path = '' diff --git a/nephilim/plugins/Notify.py b/nephilim/plugins/Notify.py index 7906861..af97e06 100644 --- a/nephilim/plugins/Notify.py +++ b/nephilim/plugins/Notify.py @@ -119,7 +119,7 @@ class Notify(Plugin): self.mpclient.state_changed.disconnect(self.onStateChange) self.mpclient.volume_changed.disconnect(self.onVolumeChange) def onSongChange(self): - song = self.mpclient.current_song() + song = self.mpclient.cur_song if not song: return self.settings.beginGroup(self.name) diff --git a/nephilim/plugins/PlayControl.py b/nephilim/plugins/PlayControl.py index 983ecd6..c3d01d6 100644 --- a/nephilim/plugins/PlayControl.py +++ b/nephilim/plugins/PlayControl.py @@ -68,7 +68,7 @@ class wgPlayControl(QtGui.QToolBar): self.logger = p.logger self.setObjectName(p.name) - status = self.p.mpclient.status() + status = self.p.mpclient.status self.play_icon = QtGui.QIcon(':icons/media-playback-start.svg') self.pause_icon = QtGui.QIcon(':icons/media-playback-pause.svg') @@ -130,7 +130,7 @@ class wgPlayControl(QtGui.QToolBar): output.state_changed.connect(act.setChecked) def onStateChange(self, new_state): - status = self.p.mpclient.status() + status = self.p.mpclient.status if new_state == 'play': self.play_action.setIcon(self.pause_icon) @@ -143,7 +143,7 @@ class wgPlayControl(QtGui.QToolBar): self.vol_slider.setValue(new_vol) def on_play_click(self): - status=self.p.mpclient.status() + status=self.p.mpclient.status if status['state']=='play': self.logger.info('Toggling playback') self.p.mpclient.pause() diff --git a/nephilim/plugins/Playlist.py b/nephilim/plugins/Playlist.py index df78a44..e6a1852 100644 --- a/nephilim/plugins/Playlist.py +++ b/nephilim/plugins/Playlist.py @@ -58,11 +58,7 @@ class PlaylistWidget(QtGui.QWidget): self.layout().setMargin(0) self.layout().addWidget(self.playlist) - if self.plugin.mpclient.is_connected(): - self.playlist.fill() - - def fill_playlist(self): - self.playlist.fill() + self.plugin.mpclient.playlist(self.playlist.fill) class PlaylistTree(QtGui.QTreeWidget): plugin = None @@ -99,7 +95,7 @@ class PlaylistTree(QtGui.QTreeWidget): self.itemActivated.connect(self._song_activated) self.header().geometriesChanged.connect(self._save_state) - self.plugin.mpclient.playlist_changed.connect(self.fill) + self.plugin.mpclient.playlist_changed.connect(lambda :self.plugin.mpclient.playlist(self.fill)) self.plugin.mpclient.connect_changed.connect(self._update_menu) def _save_state(self): @@ -108,10 +104,10 @@ class PlaylistTree(QtGui.QTreeWidget): def _song_activated(self, item): self.plugin.mpclient.play(item.song['id']) - def fill(self): + def fill(self, songs): columns = self.plugin.settings.value(self.plugin.name + '/columns').toStringList() self.clear() - for song in self.plugin.mpclient.playlistinfo(): + for song in songs: item = PlaylistSongItem(PlaylistEntryRef(self.plugin.mpclient, song['id'])) for i in range(len(columns)): item.setText(i, song['?' + columns[i]]) diff --git a/nephilim/plugins/Songinfo.py b/nephilim/plugins/Songinfo.py index c2dbf76..7184544 100644 --- a/nephilim/plugins/Songinfo.py +++ b/nephilim/plugins/Songinfo.py @@ -63,7 +63,7 @@ class Songinfo(Plugin): def refresh(self): self.logger.info('Refreshing.') metadata = {} - song = self.mpclient.current_song() + song = self.mpclient.cur_song if not song: return self.o.clear() diff --git a/nephilim/plugins/Systray.py b/nephilim/plugins/Systray.py index 1f28597..6a8f1ab 100644 --- a/nephilim/plugins/Systray.py +++ b/nephilim/plugins/Systray.py @@ -43,7 +43,7 @@ class Systray(Plugin): if type(event)==QtGui.QWheelEvent: numDegrees=event.delta() / 8 numSteps=5*numDegrees/15 - self.plugin.mpclient.set_volume(self.plugin.mpclient.volume() + numSteps) + self.plugin.mpclient.set_volume(self.plugin.mpclient.status['volume'] + numSteps) event.accept() return True return False @@ -64,17 +64,17 @@ class Systray(Plugin): self.parent()._wheelEvent = None def update(self): - status = self.mpclient.status() + status = self.mpclient.status if not status: return values = {'state':''} values['state']={'play':'playing', 'stop':'stopped', 'pause':'paused'}[status['state']] if 'time' in status: - values['length'] = sec2min(status['length']) - values['time'] = sec2min(status['time']) + values['length'] = sec2min(status['time'][0]) + values['time'] = sec2min(status['time'][1]) - song = self.mpclient.current_song() + song = self.mpclient.cur_song if song: self.o.setToolTip(expand_tags(self.format, (song,))) else: diff --git a/nephilim/settings_wg.py b/nephilim/settings_wg.py index fd7c443..2d32ea8 100644 --- a/nephilim/settings_wg.py +++ b/nephilim/settings_wg.py @@ -73,7 +73,7 @@ class SettingsWidget(QtGui.QWidget): outputs.layout().addWidget(box) self.xfade = QtGui.QSpinBox() - self.xfade.setValue(int(self.mpclient.status()['xfade'])) + self.xfade.setValue(int(self.mpclient.status['xfade'])) self.xfade.valueChanged.connect(self.mpclient.crossfade) self.setLayout(QtGui.QVBoxLayout()) diff --git a/nephilim/song.py b/nephilim/song.py index 0eef97b..0cf3b5c 100644 --- a/nephilim/song.py +++ b/nephilim/song.py @@ -108,16 +108,13 @@ class SongRef: return bool(self.path) def song(self): - try: - return Song(self.mpclient.find('file', self.path)[0]) - except IndexError: - return Song({}) + return list(self.mpclient.find_sync('file', self.path))[0] class PlaylistEntryRef: """This class stores a reference to a playlist entry instead of full metadata to conserve memory. Song's tags can be accessed as in Song, but it requires a call to MPD for each tag. """ - plid = None + plid = None mpclient = None def __init__(self, mpclient, plid): @@ -131,7 +128,4 @@ class PlaylistEntryRef: return self.plid != '-1' def song(self): - try: - return self.mpclient.playlistid(self.plid) - except IndexError: - return Song({}) + return self.mpclient.get_plist_song(self.plid) diff --git a/nephilim/winMain.py b/nephilim/winMain.py index c8ae92a..865f98f 100644 --- a/nephilim/winMain.py +++ b/nephilim/winMain.py @@ -55,6 +55,7 @@ class winMain(QtGui.QMainWindow): self.__time_slider.setMaximumWidth(self.width()/4) self.__time_slider.sliderReleased.connect( self.__on___time_slider_change) self.__time_label = TimeLabel(self, mpclient) + self.mpclient.time_changed.connect(self.__on_time_change) self.statusBar().addWidget(self.__statuslabel) self.statusBar().addPermanentWidget(self.__time_label) @@ -98,7 +99,6 @@ class winMain(QtGui.QMainWindow): self.mpclient.connect_changed.connect(self.__on_connect_changed) self.mpclient.song_changed.connect(self.__on_song_change) self.mpclient.state_changed.connect(self.__update_state_messages) - self.mpclient.time_changed.connect(self.__on_time_change) self.__update_state_messages() self.show() @@ -166,8 +166,8 @@ class winMain(QtGui.QMainWindow): def __update_state_messages(self): """Update window title and statusbar""" - song = self.mpclient.current_song() - state = self.mpclient.status()['state'] + song = self.mpclient.cur_song + state = self.mpclient.status['state'] state = 'playing' if state == 'play' else 'paused' if state == 'pause' else 'stopped' if song: self.setWindowTitle('%s by %s - %s [%s]'%(song['?title'], song['?artist'], APPNAME, state)) @@ -180,8 +180,8 @@ class winMain(QtGui.QMainWindow): self.mpclient.seek(self.__time_slider.value()) def __on_song_change(self): - status = self.mpclient.status() - self.__time_slider.setMaximum(status['length']) + status = self.mpclient.status + self.__time_slider.setMaximum(status['time'][1]) self.__time_slider.setEnabled(True) self.__update_state_messages() @@ -199,8 +199,8 @@ class TimeLabel(QtGui.QLabel): self._mpclient = mpclient self._mpclient.time_changed.connect(self._update_text) - self._update_text(self._mpclient.status()['time']) + self._update_text(self._mpclient.status['time'][0]) @Slot(int) def _update_text(self, time): - self.setText('%s/%s'%(sec2min(time), sec2min(self._mpclient.status()['length']))) + self.setText('%s/%s'%(sec2min(time), sec2min(self._mpclient.status['time'][1]))) -- cgit v1.2.3