]> kaliko git repositories - python-musicpdaio.git/blob - musicpdasio.py
Initial import
[python-musicpdaio.git] / musicpdasio.py
1 # -*- coding: utf-8 -*-
2 #
3 # python-musicpd: Python MPD client library
4 # Copyright (C) 2014-2015  Kaliko Jack <kaliko@azylum.org>
5 #
6 # python-musicpdasio is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # python-musicpd is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU Lesser General Public License for more details.
15 #
16 # You should have received a copy of the GNU Lesser General Public License
17 # along with python-musicpd.  If not, see <http://www.gnu.org/licenses/>.
18
19 try:
20     import asyncio
21 except ImportError:
22     print('Failed to import asyncio, need python >= 3.4')
23
24 import sys
25
26 HELLO_PREFIX = "OK MPD "
27 ERROR_PREFIX = "ACK "
28 SUCCESS = "OK"
29 NEXT = "list_OK"
30
31
32 class MPDError(Exception):
33     pass
34
35 class ConnectionError(MPDError):
36     pass
37
38 class ProtocolError(MPDError):
39     pass
40
41 class CommandError(MPDError):
42     pass
43
44 class CommandListError(MPDError):
45     pass
46
47 class PendingCommandError(MPDError):
48     pass
49
50 class IteratingError(MPDError):
51     pass
52
53 loop = asyncio.get_event_loop()
54
55 class Response:
56     def __init__(self):
57         self.version = None
58         self.resp = ''
59         self.err = None
60
61     def __repr__(self):
62         return '{0}, {1}… ({2})'.format(self.err, self.resp[:15], self.version)
63
64 class MPDProto(asyncio.Protocol):
65     def __init__(self, future, payload):
66         self.future = future
67         self.payload = payload
68         self.sess = Response()
69
70     def connection_made(self, transport):
71         self.transport = transport
72         self.transport.write(bytes('{}\n'.format(self.payload), 'utf-8'))
73
74     def data_received(self, data):
75         rcv = data.decode('utf-8')
76         if '\n' not in rcv:
77             self.sess.err = 'Connection lost while reading line'
78             self.future.set_result(self.sess)
79             raise ConnectionError("Connection lost while reading line")
80
81         rcv = rcv.strip('\n')
82
83         if rcv.startswith(HELLO_PREFIX):
84             self.sess.version = rcv[len(HELLO_PREFIX):]
85             return
86         self.transport.close()
87
88         # set the result on the Future so that the main() coroutine can
89         # resume
90         if rcv.startswith(ERROR_PREFIX):
91             self.sess.err = rcv[len(ERROR_PREFIX):].strip()
92             self.future.set_result(self.sess)
93             return
94         else:
95             self.sess.resp = rcv
96             self.future.set_result(self.sess)
97
98 def command(payload):
99     future = asyncio.Future()
100     if payload:
101         cli = MPDProto(future, payload)
102         # kick off a task to create the connection to MPD.
103         asyncio.async(loop.create_connection(lambda: cli, host='192.168.0.20', port=6600))
104     else:
105         future.set_result((None, None, None))
106
107     # return the future for the main() coroutine to wait on.
108     return future
109
110
111 def main():
112     """main"""
113     import logging
114     logging.basicConfig(level=logging.DEBUG)
115     res = yield from command('currentsongt')
116     if res.err:
117         raise CommandError(res.err)
118     print(res)
119     print(res.resp)
120
121 if __name__ == '__main__':
122     loop.run_until_complete(main())