X-Git-Url: http://git.kaliko.me/?a=blobdiff_plain;f=mpdaio%2Fclient.py;h=c37e0d6cc3946cb573104b23863519227c8d1a50;hb=b1581f5d521911f847e20501c270c840b1eb6494;hp=8a7e9af7eea5e471c6a31d004838d87f94c46932;hpb=d7d38b4e0d278c28a5cebb3480d0e70d319b543a;p=python-musicpdaio.git diff --git a/mpdaio/client.py b/mpdaio/client.py index 8a7e9af..c37e0d6 100644 --- a/mpdaio/client.py +++ b/mpdaio/client.py @@ -10,23 +10,45 @@ from .connection import ConnectionPool, Connection from .exceptions import MPDConnectionError, MPDProtocolError, MPDCommandError from .utils import Range, escape -from . import CONNECTION_MAX, CONNECTION_TIMEOUT -from . import ERROR_PREFIX, SUCCESS, NEXT +from .const import CONNECTION_MAX, CONNECTION_TIMEOUT +from .const import ERROR_PREFIX, SUCCESS, NEXT log = logging.getLogger(__name__) class MPDClient: + """:synopsis: Main class to instanciate building an MPD client. - def __init__(self, host: str | None = None, port: str | int | None = None, password: str | None = None): + :param host: MPD server IP|FQDN to connect to + :param port: MPD port to connect to + :param password: MPD password + + **musicpdaio** tries to come with sane defaults, then running + |mpdaio.MPDClient| with no explicit argument will try default values + to connect to MPD. Cf. :ref:`reference` for more about + :ref:`defaults`. + + The class is also exposed in mpdaio namespace. + + >>> import mpdaio + >>> cli = mpdaio.MPDClient(host='example.org') + >>> print(await cli.currentsong()) + >>> await cli.close() + """ + + def __init__(self, host: str | None = None, + port: str | int | None = None, + password: str | None = None): + #: Connection pool self._pool = ConnectionPool(max_connections=CONNECTION_MAX) self._get_envvars() - #: host used with the current connection (:py:obj:`str`) + #: Host used to make connections (: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] + #: password used to connect (:py:obj:`str`) + self.pwd = 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] + #: connection timeout self.mpd_timeout = CONNECTION_TIMEOUT log.info('Using %s:%s to connect', self.host, self.port) @@ -78,7 +100,7 @@ class MPDClient: def __getattr__(self, attr): command = attr - wrapper = CmdHandler(self._pool, self.host, self.port, self.password, self.mpd_timeout) + wrapper = CmdHandler(self._pool, self.host, self.port, self.pwd, self.mpd_timeout) if command not in wrapper._commands: command = command.replace("_", " ") if command not in wrapper._commands: @@ -87,8 +109,9 @@ class MPDClient: return lambda *args: wrapper(command, args) @property - def version(self): - """MPD protocol version""" + def version(self) -> str: + """MPD protocol version + """ host = (self.host, self.port) version = {_.version for _ in self.connections} if not version: @@ -99,12 +122,13 @@ class MPDClient: return version.pop() @property - def connections(self): - """Open connections""" + def connections(self) -> list[Connection]: + """connections in the pool""" host = (self.host, self.port) return self._pool._connections.get(host, []) - async def close(self): + async def close(self) -> None: + """:synopsis: Close connections in the pool""" await self._pool.close() @@ -116,7 +140,7 @@ class CmdHandler: "clearerror": self._fetch_nothing, "currentsong": self._fetch_object, "idle": self._fetch_list, - # "noidle": None, + "noidle": self._fetch_nothing, "status": self._fetch_object, "stats": self._fetch_object, # Playback Option Commands @@ -261,14 +285,27 @@ class CmdHandler: server, port = self.host self.command = command self.args = args or '' - self.connection = await self.pool.connect(server, port, timeout=self.timeout) + self.connection = await self.pool.connect(server, port, self.timeout) async with self.connection: + await self._init_connection() retval = self._commands[command] await self._write_command(command, args) if callable(retval): return await retval() return retval + async def _init_connection(self): + """Init connection if needed""" + if not self.connection.version: + # TODO: move hello here instead of connection? + # Need to consume hello + pass + if self.password and not self.connection.auth: + # Need to send password + await self._write_command('password', [self.password]) + await self._fetch_nothing() + self.connection.auth = True + async def _write_line(self, line): self.connection.write(f"{line!s}\n".encode()) await self.connection.drain()