]> kaliko git repositories - python-musicpdaio.git/blobdiff - musicpdasio.py
Better stream managment
[python-musicpdaio.git] / musicpdasio.py
index 3848ecd0e599f2f2b2b0fba10e432a85c641a362..df730e063342fa5bda252f2a1467f0523e8cb3ce 100644 (file)
@@ -27,6 +27,7 @@ HELLO_PREFIX = "OK MPD "
 ERROR_PREFIX = "ACK "
 SUCCESS = "OK"
 NEXT = "list_OK"
+VERSION = '0.0.1b'
 
 
 class MPDError(Exception):
@@ -50,7 +51,6 @@ class PendingCommandError(MPDError):
 class IteratingError(MPDError):
     pass
 
-loop = asyncio.get_event_loop()
 
 class Response:
     def __init__(self):
@@ -59,10 +59,14 @@ class Response:
         self.err = None
 
     def __repr__(self):
-        return '{0}, {1}… ({2})'.format(self.err, self.resp[:15], self.version)
+        return 'err:{0}, "{1}…" ({2})'.format(
+                self.err,
+                ' '.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()
@@ -71,52 +75,69 @@ 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 = 'Connection lost while reading line'
-            self.future.set_result(self.sess)
-            raise ConnectionError("Connection lost while reading line")
+    def eof_received(self):
+        self.transport.close()
+        err = ConnectionError('Connection lost while reading line')
+        self.future.set_exception(err)
 
-        rcv = rcv.strip('\n')
+    #def connection_lost(self):
+    #    self.eof_received()
 
-        if rcv.startswith(HELLO_PREFIX):
-            self.sess.version = rcv[len(HELLO_PREFIX):]
-            return
-        self.transport.close()
+    def data_received(self, data):
+        rcv = self._hello(data.decode('utf-8'))
 
-        # set the result on the Future so that the main() 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'):
+            self.transport.close()
             self.future.set_result(self.sess)
 
-def command(payload):
-    future = asyncio.Future()
-    if payload:
-        cli = MPDProto(future, payload)
+    def _hello(self, rcv):
+        """Consume HELLO_PREFIX"""
+
+        if rcv.startswith(HELLO_PREFIX):
+            self.sess.version = rcv.split('\n')[0][len(HELLO_PREFIX):]
+            #print('consumed hello prefix: %s' % self.sess.version)
+            return rcv[rcv.find('\n')+1:]
+        return rcv
+
+class MPDClient:
+    loop = asyncio.get_event_loop()
+
+    def __init__(self, host='localhost', port=6600, cred=None):
+        self._host = host
+        self._port = port
+        self._cred = cred
+        self._commands = {
+                'currentsong',
+                'stats',
+                'playlistinfo',
+        }
+
+    def __getattr__(self, attr):
+        command = attr
+        wrapper = self._command
+        if command not in self._commands:
+            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 _command(self, command, args):
+        payload = '{} {}'.format(command ,''.join(args))
+        future = asyncio.Future()
         # kick off a task to create the connection to MPD.
-        asyncio.async(loop.create_connection(lambda: cli, host='192.168.0.20', port=6600))
-    else:
-        future.set_result((None, None, None))
-
-    # return the future for the main() coroutine to wait on.
-    return future
-
-
-def main():
-    """main"""
-    import logging
-    logging.basicConfig(level=logging.DEBUG)
-    res = yield from command('currentsongt')
-    if res.err:
-        raise CommandError(res.err)
-    print(res)
-    print(res.resp)
-
-if __name__ == '__main__':
-    loop.run_until_complete(main())
+        asyncio.async(MPDClient.loop.create_connection(
+                                    lambda: MPDProto(future, payload, self._cred),
+                                    host=self._host,
+                                    port=self._port))
+        MPDClient.loop.run_until_complete(future)
+        #Useless?
+        #if future.exception():
+        #    raise future.exception()
+        # return once completed.
+        return future.result().resp