+ self._pool = ConnectionPool(max_connections=CONNECTION_MAX)
+ self._get_envvars()
+ #: host used with the current connection (:py:obj:`str`)
+ self.host = host or self.server_discovery[0]
+ #: password detected in :envvar:`MPD_HOST` environment variable (:py:obj:`str`)
+ self.password = password or self.server_discovery[2]
+ #: port used with the current connection (:py:obj:`int`, :py:obj:`str`)
+ self.port = port or self.server_discovery[1]
+ log.info('logger : "%s"', __name__)
+ #: Protocol version
+ self.version: [None, str] = None
+ self.mpd_timeout = CONNECTION_TIMEOUT
+
+ def _get_envvars(self):
+ """
+ Retrieve MPD env. var. to overrides default "localhost:6600"
+ """
+ # Set some defaults
+ disco_host = 'localhost'
+ disco_port = os.getenv('MPD_PORT', '6600')
+ pwd = None
+ _host = os.getenv('MPD_HOST', '')
+ if _host:
+ # If password is set: MPD_HOST=pass@host
+ if '@' in _host:
+ mpd_host_env = _host.split('@', 1)
+ if mpd_host_env[0]:
+ # A password is actually set
+ log.debug(
+ 'password detected in MPD_HOST, set client pwd attribute')
+ pwd = mpd_host_env[0]
+ if mpd_host_env[1]:
+ disco_host = mpd_host_env[1]
+ log.debug('host detected in MPD_HOST: %s', disco_host)
+ elif mpd_host_env[1]:
+ # No password set but leading @ is an abstract socket
+ disco_host = '@'+mpd_host_env[1]
+ log.debug(
+ 'host detected in MPD_HOST: %s (abstract socket)', disco_host)
+ else:
+ # MPD_HOST is a plain host
+ disco_host = _host
+ log.debug('host detected in MPD_HOST: %s', disco_host)
+ else:
+ # Is socket there
+ xdg_runtime_dir = os.getenv('XDG_RUNTIME_DIR', '/run')
+ rundir = os.path.join(xdg_runtime_dir, 'mpd/socket')
+ if os.path.exists(rundir):
+ disco_host = rundir
+ log.debug(
+ 'host detected in ${XDG_RUNTIME_DIR}/run: %s (unix socket)', disco_host)
+ _mpd_timeout = os.getenv('MPD_TIMEOUT', '')
+ if _mpd_timeout.isdigit():
+ self.mpd_timeout = int(_mpd_timeout)
+ log.debug('timeout detected in MPD_TIMEOUT: %d', self.mpd_timeout)
+ else: # Use CONNECTION_TIMEOUT as default even if MPD_TIMEOUT carries gargage
+ self.mpd_timeout = CONNECTION_TIMEOUT
+ self.server_discovery = (disco_host, disco_port, pwd)
+
+ def __getattr__(self, attr):
+ command = attr
+ wrapper = CmdHandler(self._pool, self.host, self.port, self.password, self.mpd_timeout)
+ if command not in wrapper._commands:
+ command = command.replace("_", " ")
+ if command not in wrapper._commands:
+ raise AttributeError(
+ f"'CmdHandler' object has no attribute '{attr}'")
+ return lambda *args: wrapper(command, args)
+
+ async def close(self):
+ await self._pool.close()
+
+
+class CmdHandler:
+ #TODO: CmdHandler to intanciate in place of MPDClient._execute
+ # The MPDClient.__getattr__ wrapper should instanciate an CmdHandler object
+
+ def __init__(self, pool, server, port, password, timeout):