Support a TOML configuration file

The new config file currently only configures the same options that were
already available through environment variables. However I have ideas
for additional features that would be much nicer to support using a
structured configuration format like TOML rather than environment
variables, so config files exist now!

The previous environment variables are still supported and will be used
if you don't have a config file. I plan to keep supporting the MPD_HOST
and MPD_PORT environment variables forever since they're shared with
other MPD clients such as mpc, but I may eventually drop the environment
variables specific to mpd-now-playable in a future release.
This commit is contained in:
Danielle McLean 2024-06-22 18:19:39 +10:00
parent 796e3df87d
commit dc037a0a4b
Signed by: 00dani
GPG key ID: 6854781A0488421C
12 changed files with 305 additions and 33 deletions

112
pdm.lock
View file

@ -5,7 +5,7 @@
groups = ["default", "all", "dev"]
strategy = ["cross_platform"]
lock_version = "4.4.1"
content_hash = "sha256:2b4fba8ca6883f5a1de28a420870d566efa031d62f9057c59095a25a0e51d36e"
content_hash = "sha256:828f8051de2c2a04cede8130abf86764edb38f0428cd1d52dd797bff43452afe"
[[package]]
name = "aiocache"
@ -54,6 +54,22 @@ files = [
{file = "aiomcache-0.8.2.tar.gz", hash = "sha256:43b220d7f499a32a71871c4f457116eb23460fa216e69c1d32b81e3209e51359"},
]
[[package]]
name = "apischema"
version = "0.18.1"
requires_python = ">=3.7"
summary = "JSON (de)serialization, GraphQL and JSON schema generation using Python typing."
files = [
{file = "apischema-0.18.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:220aa56974f765dc100e875c66c688ff57bad3ae48d0aeaee4fb1ec90c5cd0fd"},
{file = "apischema-0.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:653492010d22acdcbe2240f0ceb3ca59de1b6d34640895e03fda15f944e216d8"},
{file = "apischema-0.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56a8acc8da59cf7a1052b5aec71d2f8ed6137b54aa1492709acb2c47f0547107"},
{file = "apischema-0.18.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d466ccc31cbc95b381037ed5b9e82e034f921eb28f8057263aefc2817678036f"},
{file = "apischema-0.18.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b005d5a4baba32eccec5bb0fbd616f9fd26b6a5fb4f15e9a8bb53a948adade0"},
{file = "apischema-0.18.1-cp312-cp312-win32.whl", hash = "sha256:bd06fc6a52d461bd6540409cb25c1d51aae23b22fcd10b1fb002a3f7f1f15d0f"},
{file = "apischema-0.18.1-cp312-cp312-win_amd64.whl", hash = "sha256:429f13d9e35379bf8187c41a3c05562f8149358128382a90e415c63db528d6a2"},
{file = "apischema-0.18.1.tar.gz", hash = "sha256:355dc4dea7389f5b25f5326c26f06eebee8107efda7e82db8f09ee122cdf0c98"},
]
[[package]]
name = "attrs"
version = "23.2.0"
@ -64,6 +80,41 @@ files = [
{file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"},
]
[[package]]
name = "idna"
version = "3.7"
requires_python = ">=3.5"
summary = "Internationalized Domain Names in Applications (IDNA)"
files = [
{file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
{file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
]
[[package]]
name = "multidict"
version = "6.0.5"
requires_python = ">=3.7"
summary = "multidict implementation"
files = [
{file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"},
{file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"},
{file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"},
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"},
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"},
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"},
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"},
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"},
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"},
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"},
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"},
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"},
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"},
{file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"},
{file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"},
{file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"},
{file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"},
]
[[package]]
name = "mypy"
version = "1.10.0"
@ -215,6 +266,26 @@ files = [
{file = "python_mpd2-3.1.1-py2.py3-none-any.whl", hash = "sha256:86bf1100a0b135959d74a9a7a58cf0515bf30bb54eb25ae6fb8e175e50300fc3"},
]
[[package]]
name = "pytomlpp"
version = "1.0.13"
summary = "A python wrapper for toml++"
files = [
{file = "pytomlpp-1.0.13-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4710c72456c10a90e58084174312abef8f9652b0f91c240c008903c1bd99814d"},
{file = "pytomlpp-1.0.13-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b59acc12339a992404289ab7294f28ba06c7df3c2562e81d316a0e744ab4103b"},
{file = "pytomlpp-1.0.13-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:252e31a5e013a74b898784f4ffb8aa8068e136b910ad11f2af1ee8a5700e6e1e"},
{file = "pytomlpp-1.0.13-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:09e716c0f462d15f2334cecc736957777dd30f8a5bfa5cf8150679da7577d2fd"},
{file = "pytomlpp-1.0.13-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:19dbded2995370e802105fa6dce54ed60f79e58b4eb35fee7ef33f1fb5958f6c"},
{file = "pytomlpp-1.0.13-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f87f6c958309e4c2358b778902c80bd33611d1c392f1abe2c226e3a62909ca4"},
{file = "pytomlpp-1.0.13-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e285aca948b419301fdda1927723287ef28482752782c44c9ee8c57eae7a1dc8"},
{file = "pytomlpp-1.0.13-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:aad6ae19c056ea62a43fec82427ad4675b5c773dc255c4bdcf6da659cd7edff6"},
{file = "pytomlpp-1.0.13-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0e0b34b7a132856567714342e9a622f7be0b4c9bac561a6252f0f85626c1aa4b"},
{file = "pytomlpp-1.0.13-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac06ca7683f5a2737b3888ea1e38d6968abb24fab703bc7ceccbe589d5420e0c"},
{file = "pytomlpp-1.0.13-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35225c1d9d674df87b4682f04af97856049351c38822455b78258248d9309363"},
{file = "pytomlpp-1.0.13-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:dbc9208ac58ea2a9d5ebb77e69d54d146744007f4a704a3f4e56d9881d41ee1c"},
{file = "pytomlpp-1.0.13.tar.gz", hash = "sha256:a0bd639a8f624d1bdf5b3ea94363ca23dbfef38ab7b5b9348881a84afab434ad"},
]
[[package]]
name = "redis"
version = "5.0.4"
@ -259,3 +330,42 @@ files = [
{file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"},
{file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"},
]
[[package]]
name = "xdg-base-dirs"
version = "6.0.1"
requires_python = ">=3.10,<4.0"
summary = "Variables defined by the XDG Base Directory Specification"
files = [
{file = "xdg_base_dirs-6.0.1-py3-none-any.whl", hash = "sha256:63f6ebc1721ced2e86c340856e004ef829501a30a37e17079c52cfaf0e1741b9"},
{file = "xdg_base_dirs-6.0.1.tar.gz", hash = "sha256:b4c8f4ba72d1286018b25eea374ec6fbf4fddda3d4137edf50de95de53e195a6"},
]
[[package]]
name = "yarl"
version = "1.9.4"
requires_python = ">=3.7"
summary = "Yet another URL library"
dependencies = [
"idna>=2.0",
"multidict>=4.0",
]
files = [
{file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"},
{file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"},
{file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"},
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"},
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"},
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"},
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"},
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"},
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"},
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"},
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"},
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"},
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"},
{file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"},
{file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"},
{file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"},
{file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"},
]

View file

@ -10,6 +10,10 @@ dependencies = [
"attrs>=23.1.0",
"pyobjc-framework-MediaPlayer>=10.0",
"python-mpd2>=3.1.0",
"xdg-base-dirs>=6.0.1",
"pytomlpp>=1.0.13",
"apischema>=0.18.1",
"yarl>=1.9.4",
]
readme = "README.md"

View file

@ -2,10 +2,10 @@ from __future__ import annotations
from contextlib import suppress
from typing import Any, Optional, TypeVar
from urllib.parse import parse_qsl, urlparse
from aiocache import Cache
from aiocache.serializers import BaseSerializer, PickleSerializer
from yarl import URL
T = TypeVar("T")
@ -28,24 +28,23 @@ class OrmsgpackSerializer(BaseSerializer):
return ormsgpack.unpackb(value)
def make_cache(url: str, namespace: str = "") -> Cache[T]:
parsed_url = urlparse(url)
backend = Cache.get_scheme_class(parsed_url.scheme)
def make_cache(url: URL, namespace: str = "") -> Cache[T]:
backend = Cache.get_scheme_class(url.scheme)
if backend == Cache.MEMORY:
return Cache(backend)
kwargs: dict[str, Any] = dict(parse_qsl(parsed_url.query))
kwargs: dict[str, Any] = dict(url.query)
if parsed_url.path:
kwargs.update(backend.parse_uri_path(parsed_url.path))
if url.path:
kwargs.update(backend.parse_uri_path(url.path))
if parsed_url.hostname:
kwargs["endpoint"] = parsed_url.hostname
if url.host:
kwargs["endpoint"] = url.host
if parsed_url.port:
kwargs["port"] = parsed_url.port
if url.port:
kwargs["port"] = url.port
if parsed_url.password:
kwargs["password"] = parsed_url.password
if url.password:
kwargs["password"] = url.password
namespace = ":".join(s for s in [kwargs.pop("namespace", ""), namespace] if s)

View file

@ -1,25 +1,19 @@
import asyncio
from os import environ
from corefoundationasyncio import CoreFoundationEventLoop
from .__version__ import __version__
from .cocoa.now_playing import CocoaNowPlaying
from .config.load import loadConfig
from .mpd.listener import MpdStateListener
async def listen() -> None:
port = int(environ.get("MPD_PORT", "6600"))
host = environ.get("MPD_HOST", "localhost")
password = environ.get("MPD_PASSWORD")
cache = environ.get("MPD_NOW_PLAYABLE_CACHE")
if password is None and "@" in host:
password, host = host.split("@", maxsplit=1)
print(f"mpd-now-playable v{__version__}")
listener = MpdStateListener(cache)
config = loadConfig()
listener = MpdStateListener(config.cache)
now_playing = CocoaNowPlaying(listener)
await listener.start(host=host, port=port, password=password)
await listener.start(config.mpd)
await listener.loop(now_playing)

View file

View file

@ -0,0 +1,44 @@
{
"$id": "https://cdn.00dani.me/m/schemata/mpd-now-playable/config-v1.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"definitions": {
"URL": {
"format": "uri",
"type": "string"
}
},
"properties": {
"$schema": {
"$ref": "#/definitions/URL"
},
"cache": {
"$ref": "#/definitions/URL"
},
"mpd": {
"additionalProperties": false,
"default": {
"host": "127.0.0.1",
"port": 6600
},
"properties": {
"host": {
"default": "127.0.0.1",
"format": "hostname",
"type": "string"
},
"password": {
"type": "string"
},
"port": {
"default": 6600,
"maximum": 65535,
"minimum": 1,
"type": "integer"
}
},
"type": "object"
}
},
"type": "object"
}

View file

@ -0,0 +1,28 @@
from dataclasses import field
from typing import NewType, Optional, TypeVar
from apischema import schema
from apischema.conversions import deserializer
from apischema.metadata import none_as_undefined
from yarl import URL
__all__ = ("Host", "Port", "optional")
T = TypeVar("T")
Host = NewType("Host", str)
schema(format="hostname")(Host)
Port = NewType("Port", int)
schema(min=1, max=65535)(Port)
schema(format="uri")(URL)
def optional() -> Optional[T]:
return field(default=None, metadata=none_as_undefined)
@deserializer
def from_yarl(url: str) -> URL:
return URL(url)

View file

@ -0,0 +1,34 @@
from os import environ
from apischema import deserialize
from pytomlpp import load
from xdg_base_dirs import xdg_config_home
from .model import Config
__all__ = "loadConfig"
def loadConfigFromFile() -> Config:
path = xdg_config_home() / "mpd-now-playable" / "config.toml"
data = load(path)
print(f"Loaded your configuration from {path}")
return deserialize(Config, data)
def loadConfigFromEnv() -> Config:
port = int(environ["MPD_PORT"]) if "MPD_PORT" in environ else None
host = environ.get("MPD_HOST")
password = environ.get("MPD_PASSWORD")
cache = environ.get("MPD_NOW_PLAYABLE_CACHE")
if password is None and host is not None and "@" in host:
password, host = host.split("@", maxsplit=1)
data = {"cache": cache, "mpd": {"port": port, "host": host, "password": password}}
return deserialize(Config, data)
def loadConfig() -> Config:
try:
return loadConfigFromFile()
except FileNotFoundError:
return loadConfigFromEnv()

View file

@ -0,0 +1,26 @@
from dataclasses import dataclass, field
from typing import Optional
from apischema import alias
from yarl import URL
from .fields import Host, Port, optional
__all__ = ("MpdConfig", "Config")
@dataclass(frozen=True)
class MpdConfig:
password: Optional[str] = optional()
host: Host = Host("127.0.0.1")
port: Port = Port(6600)
@dataclass(frozen=True)
class Config:
schema: URL = field(
default=URL("https://cdn.00dani.me/m/schemata/mpd-now-playable/config-v1.json"),
metadata=alias("$schema"),
)
cache: Optional[URL] = optional()
mpd: MpdConfig = field(default_factory=MpdConfig)

View file

@ -0,0 +1,28 @@
from json import dump
from pathlib import Path
from pprint import pp
from typing import Any, Mapping
from apischema.json_schema import JsonSchemaVersion, deserialization_schema
from .model import Config
def generate() -> Mapping[str, Any]:
return deserialization_schema(Config, version=JsonSchemaVersion.DRAFT_7)
def write() -> None:
schema = dict(generate())
schema["$id"] = Config.schema.human_repr()
schema_file = Path(__file__).parent / Config.schema.name
print(f"Writing this schema to {schema_file}")
pp(schema)
with open(schema_file, "w") as fp:
dump(schema, fp, indent="\t", sort_keys=True)
fp.write("\n")
if __name__ == "__main__":
write()

View file

@ -2,6 +2,8 @@ from __future__ import annotations
from typing import TypedDict
from yarl import URL
from ..async_tools import run_background_task
from ..cache import Cache, make_cache
from .types import CurrentSongResponse, MpdStateHandler
@ -23,12 +25,15 @@ def calc_track_key(song: CurrentSongResponse) -> str:
return song["file"]
MEMORY = URL("memory://")
class MpdArtworkCache:
mpd: MpdStateHandler
album_cache: Cache[ArtCacheEntry]
track_cache: Cache[ArtCacheEntry]
def __init__(self, mpd: MpdStateHandler, cache_url: str = "memory://"):
def __init__(self, mpd: MpdStateHandler, cache_url: URL = MEMORY):
self.mpd = mpd
self.album_cache = make_cache(cache_url, "album")
self.track_cache = make_cache(cache_url, "track")

View file

@ -4,7 +4,9 @@ from uuid import UUID
from mpd.asyncio import MPDClient
from mpd.base import CommandError
from yarl import URL
from ..config.model import MpdConfig
from ..player import Player
from ..song import PlaybackState, Song, SongListener
from ..type_tools import convert_if_exists
@ -44,20 +46,18 @@ class MpdStateListener(Player):
art_cache: MpdArtworkCache
idle_count = 0
def __init__(self, cache: str | None = None) -> None:
def __init__(self, cache: URL | None = None) -> None:
self.client = MPDClient()
self.art_cache = (
MpdArtworkCache(self, cache) if cache else MpdArtworkCache(self)
)
async def start(
self, host: str = "localhost", port: int = 6600, password: str | None = None
) -> None:
print(f"Connecting to MPD server {host}:{port}...")
await self.client.connect(host, port)
if password is not None:
async def start(self, conf: MpdConfig) -> None:
print(f"Connecting to MPD server {conf.host}:{conf.port}...")
await self.client.connect(conf.host, conf.port)
if conf.password is not None:
print("Authorising to MPD with your password...")
await self.client.password(password)
await self.client.password(conf.password)
print(f"Connected to MPD v{self.client.mpd_version}")
async def refresh(self) -> None: