# -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: 2012-2024 kaliko <kaliko@azylum.org>
+# SPDX-FileCopyrightText: 2008-2010 J. Alexander Treuman <jat@spatialrift.net>
# SPDX-License-Identifier: LGPL-3.0-or-later
import logging
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
+ log.info('Using %s:%s to connect', self.host, self.port)
def _get_envvars(self):
"""
f"'CmdHandler' object has no attribute '{attr}'")
return lambda *args: wrapper(command, args)
+ @property
+ def version(self):
+ """MPD protocol version"""
+ host = (self.host, self.port)
+ version = {_.version for _ in self.connections}
+ if not version:
+ log.warning('No connections yet in the connections pool for %s', host)
+ return ''
+ if len(version) > 1:
+ log.warning('More than one version in the connections pool for %s', host)
+ return version.pop()
+
+ @property
+ def connections(self):
+ """Open connections"""
+ host = (self.host, self.port)
+ return self._pool._connections.get(host, [])
+
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):
self._commands = {
retval = self._commands[command]
await self._write_command(command, args)
if callable(retval):
- log.debug('retvat: %s', retval)
return await retval()
return retval
#log.debug(' '.join(parts))
await self._write_line(' '.join(parts))
- def _read_binary(self, amount):
+ async def _read_binary(self, amount):
chunk = bytearray()
while amount > 0:
- result = self._rbfile.read(amount)
+ result = await self.connection.read(amount)
if len(result) == 0:
- self.disconnect()
+ await self.connection.close()
raise ConnectionError(
"Connection lost while reading binary content")
chunk.extend(result)
return bytes(chunk)
async def _read_line(self, binary=False):
- if binary:
- line = self._rbfile.readline().decode('utf-8')
- else:
- line = await self.connection.readline()
+ line = await self.connection.readline()
line = line.decode('utf-8')
if not line.endswith('\n'):
- await self.close()
+ await self.connection.close()
raise MPDConnectionError("Connection lost while reading line")
line = line.rstrip('\n')
if line.startswith(ERROR_PREFIX):
return pair
async def _read_pairs(self, separator=": ", binary=False):
- """OK"""
pair = await self._read_pair(separator, binary=binary)
while pair:
yield pair
yield value
async def _read_playlist(self):
- for _, value in await self._read_pairs(":"):
+ async for _, value in self._read_pairs(":"):
yield value
async def _read_objects(self, delimiters=None):
if obj:
yield obj
- def _read_command_list(self):
+ async def _read_command_list(self):
try:
for retval in self._command_list:
yield retval()
finally:
self._command_list = None
- self._fetch_nothing()
+ await self._fetch_nothing()
async def _fetch_nothing(self):
line = await self._read_line()
async def _fetch_list(self):
return [_ async for _ in self._read_list()]
- def _fetch_playlist(self):
- return self._read_playlist()
+ async def _fetch_playlist(self):
+ return [_ async for _ in self._read_pairs(':')]
async def _fetch_object(self):
objs = [obj async for obj in self._read_objects()]
async def _fetch_objects(self, delimiters):
return [_ async for _ in self._read_objects(delimiters)]
- def _fetch_changes(self):
- return self._fetch_objects(["cpos"])
+ async def _fetch_changes(self):
+ return await self._fetch_objects(["cpos"])
async def _fetch_songs(self):
return await self._fetch_objects(["file"])
- def _fetch_playlists(self):
- return self._fetch_objects(["playlist"])
+ async def _fetch_playlists(self):
+ return await self._fetch_objects(["playlist"])
- def _fetch_database(self):
- return self._fetch_objects(["file", "directory", "playlist"])
+ async def _fetch_database(self):
+ return await self._fetch_objects(["file", "directory", "playlist"])
- def _fetch_outputs(self):
- return self._fetch_objects(["outputid"])
+ async def _fetch_outputs(self):
+ return await self._fetch_objects(["outputid"])
- def _fetch_plugins(self):
- return self._fetch_objects(["plugin"])
+ async def _fetch_plugins(self):
+ return await self._fetch_objects(["plugin"])
- def _fetch_messages(self):
- return self._fetch_objects(["channel"])
+ async def _fetch_messages(self):
+ return await self._fetch_objects(["channel"])
- def _fetch_mounts(self):
- return self._fetch_objects(["mount"])
+ async def _fetch_mounts(self):
+ return await self._fetch_objects(["mount"])
- def _fetch_neighbors(self):
- return self._fetch_objects(["neighbor"])
+ async def _fetch_neighbors(self):
+ return await self._fetch_objects(["neighbor"])
async def _fetch_composite(self):
obj = {}
- for key, value in self._read_pairs(binary=True):
+ async for key, value in self._read_pairs(binary=True):
key = key.lower()
obj[key] = value
if key == 'binary':
return obj
amount = int(obj['binary'])
try:
- obj['data'] = self._read_binary(amount)
+ obj['data'] = await self._read_binary(amount)
except IOError as err:
raise ConnectionError(
f'Error reading binary content: {err}') from err
f'Expects {amount}B, got {data_bytes}')
# Fetches trailing new line
await self._read_line(binary=True)
+ #ALT: await self.connection.readuntil(b'\n')
# Fetches SUCCESS code
await self._read_line(binary=True)
+ #ALT: await self.connection.readuntil(b'OK\n')
return obj
- def _fetch_command_list(self):
- return self._read_command_list()
+ async def _fetch_command_list(self):
+ return [_ async for _ in self._read_command_list()]