6 HELLO_PREFIX = "OK MPD "
12 class MPDError(Exception):
15 class ProtocolError(MPDError):
18 class CommandError(MPDError):
21 class CommandListError(MPDError):
25 class MPDClient(object):
31 "disableoutput": self._getnone,
32 "enableoutput": self._getnone,
34 "update": self._getitem,
35 # Informational Commands
36 "status": self._getobject,
37 "stats": self._getobject,
38 "outputs": self._getoutputs,
39 "commands": self._getlist,
40 "notcommands": self._getlist,
41 "tagtypes": self._getlist,
42 "urlhandlers": self._getlist,
44 "find": self._getsongs,
45 "list": self._getlist,
46 "listall": self._getdatabase,
47 "listallinfo": self._getdatabase,
48 "lsinfo": self._getdatabase,
49 "search": self._getsongs,
50 "count": self._getobject,
53 "addid": self._getitem,
54 "clear": self._getnone,
55 "currentsong": self._getobject,
56 "delete": self._getnone,
57 "deleteid": self._getnone,
58 "load": self._getnone,
59 "rename": self._getnone,
60 "move": self._getnone,
61 "moveid": self._getnone,
62 "playlist": self._getplaylist,
63 "playlistinfo": self._getsongs,
64 "playlistid": self._getsongs,
65 "plchanges": self._getsongs,
66 "plchangesposid": self._getchanges,
68 "save": self._getnone,
69 "shuffle": self._getnone,
70 "swap": self._getnone,
71 "swapid": self._getnone,
72 "listplaylist": self._getlist,
73 "listplaylistinfo": self._getsongs,
74 "playlistadd": self._getnone,
75 "playlistclear": self._getnone,
76 "playlistdelete": self._getnone,
77 "playlistmove": self._getnone,
78 "playlistfind": self._getsongs,
79 "playlistsearch": self._getsongs,
81 "crossfade": self._getnone,
82 "next": self._getnone,
83 "pause": self._getnone,
84 "play": self._getnone,
85 "playid": self._getnone,
86 "previous": self._getnone,
87 "random": self._getnone,
88 "repeat": self._getnone,
89 "seek": self._getnone,
90 "seekid": self._getnone,
91 "setvol": self._getnone,
92 "stop": self._getnone,
93 "volume": self._getnone,
94 # Miscellaneous Commands
95 "clearerror": self._getnone,
97 "password": self._getnone,
98 "ping": self._getnone,
101 def __getattr__(self, attr):
103 retval = self._commands[attr]
105 raise AttributeError, "'%s' object has no attribute '%s'" % \
106 (self.__class__.__name__, attr)
107 return lambda *args: self._docommand(attr, args, retval)
109 def _docommand(self, command, args, retval):
110 if self._commandlist is not None and not callable(retval):
111 raise CommandListError, "%s not allowed in command list" % command
112 self._writecommand(command, args)
113 if self._commandlist is None:
117 self._commandlist.append(retval)
119 def _writeline(self, line):
120 self._sockfile.write("%s\n" % line)
121 self._sockfile.flush()
123 def _writecommand(self, command, args=[]):
126 parts.append('"%s"' % escape(str(arg)))
127 self._writeline(" ".join(parts))
130 line = self._sockfile.readline().rstrip("\n")
131 if line.startswith(ERROR_PREFIX):
132 error = line[len(ERROR_PREFIX):].strip()
133 raise CommandError, error
134 if self._commandlist is not None:
138 raise ProtocolError, "Got unexpected '%s'" % SUCCESS
139 elif line == SUCCESS:
143 def _readitem(self, separator):
144 line = self._readline()
147 item = line.split(separator, 1)
149 raise ProtocolError, "Could not parse item: '%s'" % line
152 def _readitems(self, separator=": "):
153 item = self._readitem(separator)
156 item = self._readitem(separator)
161 for key, value in self._readitems():
164 raise ProtocolError, "Expected key '%s', got '%s'" % \
170 def _readplaylist(self):
171 for key, value in self._readitems(":"):
175 def _readobjects(self, delimiters=[]):
177 for key, value in self._readitems():
180 if key in delimiters:
183 elif obj.has_key(key):
184 if not isinstance(obj[key], list):
185 obj[key] = [obj[key], value]
187 obj[key].append(value)
194 def _readcommandlist(self):
195 for retval in self._commandlist:
197 self._commandlist = None
201 def _wrapiterator(self, iterator):
203 return list(iterator)
207 line = self._readline()
209 raise ProtocolError, "Got unexpected return value: '%s'" % line
212 items = list(self._readitems())
214 raise ProtocolError, "Expected 1 item, got %i" % len(items)
218 return self._wrapiterator(self._readlist())
220 def _getplaylist(self):
221 return self._wrapiterator(self._readplaylist())
223 def _getobject(self):
224 objs = list(self._readobjects())
229 def _getobjects(self, delimiters):
230 return self._wrapiterator(self._readobjects(delimiters))
233 return self._getobjects(["file"])
235 def _getdatabase(self):
236 return self._getobjects(["file", "directory", "playlist"])
238 def _getoutputs(self):
239 return self._getobjects(["outputid"])
241 def _getchanges(self):
242 return self._getobjects(["cpos"])
244 def _getcommandlist(self):
245 return self._wrapiterator(self._readcommandlist())
248 line = self._sockfile.readline().rstrip("\n")
249 if not line.startswith(HELLO_PREFIX):
250 raise ProtocolError, "Got invalid MPD hello: '%s'" % line
251 self.mpd_version = line[len(HELLO_PREFIX):].strip()
254 self.mpd_version = None
255 self._commandlist = None
256 self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
257 self._sockfile = self._sock.makefile("rb+")
259 def connect(self, host, port):
261 self._sock.connect((host, port))
264 def disconnect(self):
265 self._sockfile.close()
269 def command_list_ok_begin(self):
270 if self._commandlist is not None:
271 raise CommandListError, "Already in command list"
272 self._writecommand("command_list_ok_begin")
273 self._commandlist = []
275 def command_list_end(self):
276 if self._commandlist is None:
277 raise CommandListError, "Not in command list"
278 self._writecommand("command_list_end")
279 return self._getcommandlist()
283 return text.replace("\\", "\\\\").replace('"', '\\"')
286 # vim: set expandtab shiftwidth=4 softtabstop=4 textwidth=79: