-# Python MPD client library
+# python-mpd: Python MPD client library
# Copyright (C) 2008-2010 J. Alexander Treuman <jat@spatialrift.net>
+# Copyright (C) 2012 Kaliko Jack <kaliko@azylum.org>
#
-# 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
+# python-mpd is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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,
+# python-mpd 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.
+# GNU Lesser 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/>.
+# You should have received a copy of the GNU Lesser General Public License
+# along with python-mpd. If not, see <http://www.gnu.org/licenses/>.
import socket
self._reset()
self._commands = {
# Status Commands
- "clearerror": self._fetch_nothing,
- "currentsong": self._fetch_object,
- "idle": self._fetch_list,
- "noidle": None,
- "status": self._fetch_object,
- "stats": self._fetch_object,
+ "clearerror": self._fetch_nothing,
+ "currentsong": self._fetch_object,
+ "idle": self._fetch_list,
+ "noidle": None,
+ "status": self._fetch_object,
+ "stats": self._fetch_object,
# Playback Option Commands
- "consume": self._fetch_nothing,
- "crossfade": self._fetch_nothing,
- "random": self._fetch_nothing,
- "repeat": self._fetch_nothing,
- "setvol": self._fetch_nothing,
- "single": self._fetch_nothing,
- "volume": self._fetch_nothing,
+ "consume": self._fetch_nothing,
+ "crossfade": self._fetch_nothing,
+ "mixrampdb": self._fetch_nothing,
+ "mixrampdelay": self._fetch_nothing,
+ "random": self._fetch_nothing,
+ "repeat": self._fetch_nothing,
+ "setvol": self._fetch_nothing,
+ "single": self._fetch_nothing,
+ "replay_gain_mode": self._fetch_nothing,
+ "replay_gain_status": self._fetch_item,
+ "volume": self._fetch_nothing,
# Playback Control Commands
- "next": self._fetch_nothing,
- "pause": self._fetch_nothing,
- "play": self._fetch_nothing,
- "playid": self._fetch_nothing,
- "previous": self._fetch_nothing,
- "seek": self._fetch_nothing,
- "seekid": self._fetch_nothing,
- "stop": self._fetch_nothing,
+ "next": self._fetch_nothing,
+ "pause": self._fetch_nothing,
+ "play": self._fetch_nothing,
+ "playid": self._fetch_nothing,
+ "previous": self._fetch_nothing,
+ "seek": self._fetch_nothing,
+ "seekid": self._fetch_nothing,
+ "seekcur": self._fetch_nothing,
+ "stop": self._fetch_nothing,
# Playlist Commands
- "add": self._fetch_nothing,
- "addid": self._fetch_item,
- "clear": self._fetch_nothing,
- "delete": self._fetch_nothing,
- "deleteid": self._fetch_nothing,
- "move": self._fetch_nothing,
- "moveid": self._fetch_nothing,
- "playlist": self._fetch_playlist,
- "playlistfind": self._fetch_songs,
- "playlistid": self._fetch_songs,
- "playlistinfo": self._fetch_songs,
- "playlistsearch": self._fetch_songs,
- "plchanges": self._fetch_songs,
- "plchangesposid": self._fetch_changes,
- "shuffle": self._fetch_nothing,
- "swap": self._fetch_nothing,
- "swapid": self._fetch_nothing,
+ "add": self._fetch_nothing,
+ "addid": self._fetch_item,
+ "clear": self._fetch_nothing,
+ "delete": self._fetch_nothing,
+ "deleteid": self._fetch_nothing,
+ "move": self._fetch_nothing,
+ "moveid": self._fetch_nothing,
+ "playlist": self._fetch_playlist,
+ "playlistfind": self._fetch_songs,
+ "playlistid": self._fetch_songs,
+ "playlistinfo": self._fetch_songs,
+ "playlistsearch": self._fetch_songs,
+ "plchanges": self._fetch_songs,
+ "plchangesposid": self._fetch_changes,
+ "shuffle": self._fetch_nothing,
+ "swap": self._fetch_nothing,
+ "swapid": self._fetch_nothing,
# Stored Playlist Commands
- "listplaylist": self._fetch_list,
- "listplaylistinfo": self._fetch_songs,
- "listplaylists": self._fetch_playlists,
- "load": self._fetch_nothing,
- "playlistadd": self._fetch_nothing,
- "playlistclear": self._fetch_nothing,
- "playlistdelete": self._fetch_nothing,
- "playlistmove": self._fetch_nothing,
- "rename": self._fetch_nothing,
- "rm": self._fetch_nothing,
- "save": self._fetch_nothing,
+ "listplaylist": self._fetch_list,
+ "listplaylistinfo": self._fetch_songs,
+ "listplaylists": self._fetch_playlists,
+ "load": self._fetch_nothing,
+ "playlistadd": self._fetch_nothing,
+ "playlistclear": self._fetch_nothing,
+ "playlistdelete": self._fetch_nothing,
+ "playlistmove": self._fetch_nothing,
+ "rename": self._fetch_nothing,
+ "rm": self._fetch_nothing,
+ "save": self._fetch_nothing,
# Database Commands
- "count": self._fetch_object,
- "find": self._fetch_songs,
- "list": self._fetch_list,
- "listall": self._fetch_database,
- "listallinfo": self._fetch_database,
- "lsinfo": self._fetch_database,
- "search": self._fetch_songs,
- "update": self._fetch_item,
+ "count": self._fetch_object,
+ "find": self._fetch_songs,
+ "findadd": self._fetch_nothing,
+ "list": self._fetch_list,
+ "listall": self._fetch_database,
+ "listallinfo": self._fetch_database,
+ "lsinfo": self._fetch_database,
+ "search": self._fetch_songs,
+ "searchadd": self._fetch_nothing,
+ "searchaddpl": self._fetch_nothing,
+ "update": self._fetch_item,
+ "rescan": self._fetch_item,
+ # Sticker Commands
+ "sticker get": self._fetch_item,
+ "sticker set": self._fetch_nothing,
+ "sticker delete": self._fetch_nothing,
+ "sticker list": self._fetch_list,
+ "sticker find": self._fetch_songs,
# Connection Commands
- "close": None,
- "kill": None,
- "password": self._fetch_nothing,
- "ping": self._fetch_nothing,
+ "close": None,
+ "kill": None,
+ "password": self._fetch_nothing,
+ "ping": self._fetch_nothing,
# Audio Output Commands
- "disableoutput": self._fetch_nothing,
- "enableoutput": self._fetch_nothing,
- "outputs": self._fetch_outputs,
+ "disableoutput": self._fetch_nothing,
+ "enableoutput": self._fetch_nothing,
+ "outputs": self._fetch_outputs,
# Reflection Commands
- "commands": self._fetch_list,
- "notcommands": self._fetch_list,
- "tagtypes": self._fetch_list,
- "urlhandlers": self._fetch_list,
+ "commands": self._fetch_list,
+ "notcommands": self._fetch_list,
+ "tagtypes": self._fetch_list,
+ "urlhandlers": self._fetch_list,
+ "decoders": self._fetch_plugins,
+ # Client to Client
+ "subscribe": self._fetch_nothing,
+ "unsubscribe": self._fetch_nothing,
+ "channels": self._fetch_list,
+ "readmessages": self._fetch_messages,
+ "sendmessage": self._fetch_nothing,
}
def __getattr__(self, attr):
command = attr
wrapper = self._execute
if command not in self._commands:
- raise AttributeError("'%s' object has no attribute '%s'" %
- (self.__class__.__name__, attr))
+ command = command.replace("_", " ")
+ if command not in self._commands:
+ raise AttributeError("'%s' object has no attribute '%s'" %
+ (self.__class__.__name__, attr))
return lambda *args: wrapper(command, args)
def _send(self, command, args):
if self._command_list is not None:
raise CommandListError("Cannot use send_%s in a command list" %
- command)
+ command.replace(" ", "_"))
self._write_command(command, args)
- self._pending.append(command)
+ retval = self._commands[command]
+ if retval is not None:
+ self._pending.append(command)
def _fetch(self, command, args=None):
if self._command_list is not None:
raise CommandListError("Cannot use fetch_%s in a command list" %
- command)
+ command.replace(" ", "_"))
if self._iterating:
raise IteratingError("Cannot use fetch_%s while iterating" %
- command)
+ command.replace(" ", "_"))
if not self._pending:
raise PendingCommandError("No pending commands to fetch")
if self._pending[0] != command:
- raise PendingCommandError("%s is not the currently "
+ raise PendingCommandError("'%s' is not the currently "
"pending command" % command)
del self._pending[0]
retval = self._commands[command]
if callable(retval):
return retval()
+ return retval
def _execute(self, command, args):
if self._iterating:
- raise IteratingError("Cannot execute %s while iterating" % command)
+ raise IteratingError("Cannot execute '%s' while iterating" %
+ command)
if self._pending:
- raise PendingCommandError("Cannot execute %s with "
+ raise PendingCommandError("Cannot execute '%s' with "
"pending commands" % command)
retval = self._commands[command]
if self._command_list is not None:
if not callable(retval):
- raise CommandListError("%s not allowed in command list" %
+ raise CommandListError("'%s' not allowed in command list" %
command)
self._write_command(command, args)
self._command_list.append(retval)
yield obj
def _read_command_list(self):
- for retval in self._command_list:
- yield retval()
- self._command_list = None
+ try:
+ for retval in self._command_list:
+ yield retval()
+ finally:
+ self._command_list = None
self._fetch_nothing()
def _iterator_wrapper(self, iterator):
def _fetch_objects(self, delimiters):
return self._wrap_iterator(self._read_objects(delimiters))
+ def _fetch_changes(self):
+ return self._fetch_objects(["cpos"])
+
def _fetch_songs(self):
return self._fetch_objects(["file"])
def _fetch_outputs(self):
return self._fetch_objects(["outputid"])
- def _fetch_changes(self):
- return self._fetch_objects(["cpos"])
+ def _fetch_plugins(self):
+ return self._fetch_objects(["plugin"])
+
+ def _fetch_messages(self):
+ return self._fetch_objects(["channel"])
def _fetch_command_list(self):
return self._wrap_iterator(self._read_command_list())
flags = socket.AI_ADDRCONFIG
except AttributeError:
flags = 0
- msg = "getaddrinfo returns an empty list"
+ err = None
for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
socket.SOCK_STREAM, socket.IPPROTO_TCP,
flags):
af, socktype, proto, canonname, sa = res
+ sock = None
try:
sock = socket.socket(af, socktype, proto)
sock.connect(sa)
- except socket.error, msg:
- if sock:
+ return sock
+ except socket.error as socket_err:
+ err = socket_err
+ if sock is not None:
sock.close()
- sock = None
- continue
- break
- if not sock:
- raise socket.error(msg)
- return sock
+ if err is not None:
+ raise err
+ else:
+ raise ConnectionError("getaddrinfo returns an empty list")
def connect(self, host, port):
- if self._sock:
+ if self._sock is not None:
raise ConnectionError("Already connected")
if host.startswith("/"):
self._sock = self._connect_unix(host)
else:
self._sock = self._connect_tcp(host, port)
- self._rfile = self._sock.makefile("rb")
- self._wfile = self._sock.makefile("wb")
+ self._rfile = self._sock.makefile("r")
+ self._wfile = self._sock.makefile("w")
try:
self._hello()
except:
self._reset()
def fileno(self):
- if not self._sock:
+ if self._sock is None:
raise ConnectionError("Not connected")
return self._sock.fileno()