summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Khirnov <wyskas@gmail.com>2010-08-09 18:47:03 +0200
committerAnton Khirnov <wyskas@gmail.com>2010-08-12 20:50:11 +0200
commitf22dab76b500a22109a734fa7dafca9d50a24725 (patch)
tree4d85ea8e31b1a4aeab9df32fc8a519fb198e47db
parent1bd84eeb9026267d741764d01dbfb6acaeecc817 (diff)
switch to the new MPD interaction layer
remove the old mpclient and our bundled copy of mpd.py
-rw-r--r--nephilim/mpclient.py491
-rw-r--r--nephilim/mpd.py357
-rw-r--r--nephilim/plugins/AlbumCover.py6
-rw-r--r--nephilim/plugins/Library.py3
-rw-r--r--nephilim/plugins/Lyrics.py4
-rw-r--r--nephilim/plugins/Notify.py2
-rw-r--r--nephilim/plugins/PlayControl.py6
-rw-r--r--nephilim/plugins/Playlist.py12
-rw-r--r--nephilim/plugins/Songinfo.py2
-rw-r--r--nephilim/plugins/Systray.py10
-rw-r--r--nephilim/settings_wg.py2
-rw-r--r--nephilim/song.py12
-rw-r--r--nephilim/winMain.py14
13 files changed, 34 insertions, 887 deletions
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 <jerous@gmail.com>
-# Copyright (C) 2009 Anton Khirnov <wyskas@gmail.com>
+# Copyright (C) 2010 Anton Khirnov <wyskas@gmail.com>
#
# 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 <jat@spatialrift.net>
-#
-# 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 <http://www.gnu.org/licenses/>.
-
-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])))