X-Git-Url: https://git.kaliko.me/?a=blobdiff_plain;f=musicpdasio.py;h=8bd1ba447ed96c2ed6dee96d1603437a9fec9c1b;hb=4ba43706de3560a704140ae24fd4a8b304964bd6;hp=b52a3e3de301a32bb423b40e9790673769a39656;hpb=c5ad0639ad5120f2b36b45744d927c253e913bfc;p=python-musicpdaio.git diff --git a/musicpdasio.py b/musicpdasio.py index b52a3e3..8bd1ba4 100644 --- a/musicpdasio.py +++ b/musicpdasio.py @@ -19,9 +19,15 @@ try: import asyncio except ImportError: + import sys print('Failed to import asyncio, need python >= 3.4') + sys.exit(1) -import sys +import logging + +from os import environ +if 'DEBUG' in environ or 'PYTHONASYNCIODEBUG' in environ: + logging.basicConfig(level=logging.DEBUG) HELLO_PREFIX = "OK MPD " ERROR_PREFIX = "ACK " @@ -56,16 +62,15 @@ class Response: def __init__(self): self.version = None self.resp = '' - self.err = None def __repr__(self): - return 'err:{0}, "{1}…" ({2})'.format( - self.err, + return '"{0}…" ({1})'.format( ' '.join(self.resp.split('\n')[:2]), self.version) class MPDProto(asyncio.Protocol): - def __init__(self, future, payload): + def __init__(self, future, payload, cred): + self.transport = None self.future = future self.payload = payload self.sess = Response() @@ -74,42 +79,50 @@ class MPDProto(asyncio.Protocol): self.transport = transport self.transport.write(bytes('{}\n'.format(self.payload), 'utf-8')) - def data_received(self, data): - rcv = data.decode('utf-8') - if '\n' not in rcv: - self.sess.err = ConnectionError('Connection lost while reading line') - self.future.set_result(self.sess) - raise ConnectionError('Connection lost while reading line') - - rcv = rcv.strip('\n') - - if rcv.startswith(HELLO_PREFIX): - self.sess.version = rcv[len(HELLO_PREFIX):] - return + def eof_received(self): self.transport.close() + err = ConnectionError('Connection lost while reading line') + self.future.set_exception(err) + + def data_received(self, data): + #logging.debug(data.decode('utf-8')) + rcv = self._hello(data.decode('utf-8')) - # set the result on the Future so that the coroutine can - # resume if rcv.startswith(ERROR_PREFIX): - self.sess.err = rcv[len(ERROR_PREFIX):].strip() - self.future.set_result(self.sess) - return - else: - self.sess.resp = rcv + err = rcv[len(ERROR_PREFIX):].strip() + self.future.set_exception(CommandError(err)) + + self.sess.resp += rcv + if rcv.endswith(SUCCESS+'\n'): + logging.debug('set future result') + self.transport.close() self.future.set_result(self.sess) + def _hello(self, rcv): + """Consume HELLO_PREFIX""" + if rcv.startswith(HELLO_PREFIX): + logging.debug('consumed hello prefix') + self.sess.version = rcv.split('\n')[0][len(HELLO_PREFIX):] + return rcv[rcv.find('\n')+1:] + return rcv + class MPDClient: - loop = asyncio.get_event_loop() - def __init__(self, host='localhost', port=6600): + def __init__(self, host='localhost', port=6600, cred=None): + self.eloop = asyncio.get_event_loop() + self.asio = False + self.futures = [] self._host = host self._port = port + self._cred = cred self._commands = { 'currentsong', 'stats', + 'playlistinfo', } def __getattr__(self, attr): + #logging.debug(attr) command = attr wrapper = self._command if command not in self._commands: @@ -119,16 +132,37 @@ class MPDClient: (self.__class__.__name__, attr)) return lambda *args: wrapper(command, args) + @asyncio.coroutine + def _connect(self, proto): + # coroutine allowing Exception handling + # src: http://comments.gmane.org/gmane.comp.python.tulip/1401 + try: + yield from self.eloop.create_connection(lambda: proto, + host=self._host, + port=self._port) + except Exception as err: + proto.future.set_exception(ConnectionError(err)) + def _command(self, command, args): - # TODO: deal with encoding - payload = '{} {}'.format(command ,''.join(args)) + payload = '{} {}'.format(command, ''.join(args)) future = asyncio.Future() - # kick off a task to create the connection to MPD. - asyncio.async(MPDClient.loop.create_connection( - lambda: MPDProto(future, payload), - host=self._host, - port=self._port)) - MPDClient.loop.run_until_complete(future) - # return the future once completed. - return future.result() - + # kick off a task to create the connection to MPD + coro = self._connect(MPDProto(future, payload, self._cred)) + asyncio.async(coro) + self.futures.append(future) + if not self.asio: + # return once completed. + self.eloop.run_until_complete(future) + return future + # alternative w/ callback + #if not self.asio: + # future.add_done_callback(lambda ftr: MPDClient.loop.stop()) + # self.eloop.run_forever() + #return future + + def run(self): + if self.futures: + self.eloop.run_until_complete(asyncio.gather(*self.futures)) + self.futures = [] + else: + logging.info('No task found in queue, need to set self.asio?')