]> kaliko git repositories - python-musicpd.git/blobdiff - musicpd.py
Update commands list in test
[python-musicpd.git] / musicpd.py
index 008d9d8d4838539fad90031ce538d166971fe5b7..eeb4b9a81c4a04d7b856fa5e679356ddfee78af6 100644 (file)
@@ -1,5 +1,5 @@
 # python-musicpd: Python MPD client library
-# Copyright (C) 2012-2020  kaliko <kaliko@azylum.org>
+# Copyright (C) 2012-2021  kaliko <kaliko@azylum.org>
 # Copyright (C) 2019       Naglis Jonaitis <naglis@mailbox.org>
 # Copyright (C) 2019       Bart Van Loon <bbb@bbbart.be>
 # Copyright (C) 2008-2010  J. Alexander Treuman <jat@spatialrift.net>
@@ -28,9 +28,10 @@ HELLO_PREFIX = "OK MPD "
 ERROR_PREFIX = "ACK "
 SUCCESS = "OK"
 NEXT = "list_OK"
-VERSION = '0.4.5'
-#: seconds before a tcp connection attempt times out
-CONNECTION_TIMEOUT = 5
+VERSION = '0.6.0'
+#: seconds before a tcp connection attempt times out (overriden by MPD_TIMEOUT env. var.)
+CONNECTION_TIMEOUT = 30
+
 
 
 def iterator_wrapper(func):
@@ -173,6 +174,7 @@ class MPDClient:
             "random":             self._fetch_nothing,
             "repeat":             self._fetch_nothing,
             "setvol":             self._fetch_nothing,
+            "getvol":             self._fetch_object,
             "single":             self._fetch_nothing,
             "replay_gain_mode":   self._fetch_nothing,
             "replay_gain_status": self._fetch_item,
@@ -225,6 +227,7 @@ class MPDClient:
             # Database Commands
             "albumart":           self._fetch_composite,
             "count":              self._fetch_object,
+            "getfingerprint":     self._fetch_object,
             "find":               self._fetch_songs,
             "findadd":            self._fetch_nothing,
             "list":               self._fetch_list,
@@ -255,6 +258,7 @@ class MPDClient:
             "kill":               None,
             "password":           self._fetch_nothing,
             "ping":               self._fetch_nothing,
+            "binarylimit":        self._fetch_nothing,
             "tagtypes":           self._fetch_list,
             "tagtypes disable":   self._fetch_nothing,
             "tagtypes enable":    self._fetch_nothing,
@@ -264,6 +268,8 @@ class MPDClient:
             "partition":          self._fetch_nothing,
             "listpartitions":     self._fetch_list,
             "newpartition":       self._fetch_nothing,
+            "delpartition":       self._fetch_nothing,
+            "moveoutput":         self._fetch_nothing,
             # Audio Output Commands
             "disableoutput":      self._fetch_nothing,
             "enableoutput":       self._fetch_nothing,
@@ -293,22 +299,33 @@ class MPDClient:
         """
         self.host = 'localhost'
         self.pwd = None
-        self.port = os.environ.get('MPD_PORT', '6600')
-        mpd_host_env = os.environ.get('MPD_HOST')
-        if mpd_host_env:
-            # If password is set:
-            # mpd_host_env = ['pass', 'host'] because MPD_HOST=pass@host
-            mpd_host_env = mpd_host_env.split('@')
-            mpd_host_env.reverse()
-            self.host = mpd_host_env[0]
-            if len(mpd_host_env) > 1 and mpd_host_env[1]:
-                self.pwd = mpd_host_env[1]
+        self.port = os.getenv('MPD_PORT', '6600')
+        if os.getenv('MPD_HOST'):
+            # If password is set: MPD_HOST=pass@host
+            if '@' in os.getenv('MPD_HOST'):
+                mpd_host_env = os.getenv('MPD_HOST').split('@', 1)
+                if mpd_host_env[0]:
+                    # A password is actually set
+                    self.pwd = mpd_host_env[0]
+                    if mpd_host_env[1]:
+                        self.host = mpd_host_env[1]
+                elif mpd_host_env[1]:
+                    # No password set but leading @ is an abstract socket
+                    self.host = '@'+mpd_host_env[1]
+            else:
+                # MPD_HOST is a plain host
+                self.host = os.getenv('MPD_HOST')
         else:
             # Is socket there
-            xdg_runtime_dir = os.environ.get('XDG_RUNTIME_DIR', '/run')
+            xdg_runtime_dir = os.getenv('XDG_RUNTIME_DIR', '/run')
             rundir = os.path.join(xdg_runtime_dir, 'mpd/socket')
             if os.path.exists(rundir):
                 self.host = rundir
+        self.mpd_timeout = os.getenv('MPD_TIMEOUT')
+        if self.mpd_timeout and self.mpd_timeout.isdigit():
+            self.mpd_timeout = int(self.mpd_timeout)
+        else:  # Use 30s default even is MPD_TIMEOUT carries gargage
+            self.mpd_timeout = CONNECTION_TIMEOUT
 
     def __getattr__(self, attr):
         if attr == 'send_noidle':  # have send_noidle to cancel idle as well as noidle
@@ -552,7 +569,7 @@ class MPDClient:
             obj['data'] = self._read_binary(amount)
         except IOError as err:
             raise ConnectionError('Error reading binary content: %s' % err)
-        if len(obj['data']) != amount:
+        if len(obj['data']) != amount:  # can we ever get there?
             raise ConnectionError('Error reading binary content: '
                       'Expects %sB, got %s' % (amount, len(obj['data'])))
         # Fetches trailing new line
@@ -588,6 +605,9 @@ class MPDClient:
         if not hasattr(socket, "AF_UNIX"):
             raise ConnectionError(
                 "Unix domain sockets not supported on this platform")
+        # abstract socket
+        if path.startswith('@'):
+            path = '\0'+path[1:]
         sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
         sock.connect(path)
         return sock
@@ -605,7 +625,7 @@ class MPDClient:
             sock = None
             try:
                 sock = socket.socket(af, socktype, proto)
-                sock.settimeout(CONNECTION_TIMEOUT)
+                sock.settimeout(self.mpd_timeout)
                 sock.connect(sa)
                 sock.settimeout(None)
                 return sock
@@ -636,6 +656,9 @@ class MPDClient:
 
         The connect method honors MPD_HOST/MPD_PORT environment variables.
 
+        The underlying tcp socket also honors MPD_TIMEOUT environment variable
+        and defaults to :py:obj:`musicpd.CONNECTION_TIMEOUT`.
+
         .. note:: Default host/port
 
           If host evaluate to :py:obj:`False`
@@ -657,7 +680,7 @@ class MPDClient:
             self.port = port
         if self._sock is not None:
             raise ConnectionError("Already connected")
-        if host.startswith("/"):
+        if host[0] in ['/', '@']:
             self._sock = self._connect_unix(host)
         else:
             self._sock = self._connect_tcp(host, port)
@@ -686,6 +709,9 @@ class MPDClient:
         self._reset()
 
     def fileno(self):
+        """Return the socket’s file descriptor (a small integer).
+        This is useful with :py:obj:`select.select`.
+        """
         if self._sock is None:
             raise ConnectionError("Not connected")
         return self._sock.fileno()