diff --git a/pdm.lock b/pdm.lock index 7fb9d9b..5b50d08 100644 --- a/pdm.lock +++ b/pdm.lock @@ -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"}, +] diff --git a/pyproject.toml b/pyproject.toml index 3e4b995..970ae21 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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" diff --git a/src/mpd_now_playable/cache.py b/src/mpd_now_playable/cache.py index 8ae0af3..8682058 100644 --- a/src/mpd_now_playable/cache.py +++ b/src/mpd_now_playable/cache.py @@ -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) diff --git a/src/mpd_now_playable/cli.py b/src/mpd_now_playable/cli.py index 1c85636..bd25127 100644 --- a/src/mpd_now_playable/cli.py +++ b/src/mpd_now_playable/cli.py @@ -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) diff --git a/src/mpd_now_playable/config/__init__.py b/src/mpd_now_playable/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/mpd_now_playable/config/config-v1.json b/src/mpd_now_playable/config/config-v1.json new file mode 100644 index 0000000..a09d89d --- /dev/null +++ b/src/mpd_now_playable/config/config-v1.json @@ -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" +} diff --git a/src/mpd_now_playable/config/fields.py b/src/mpd_now_playable/config/fields.py new file mode 100644 index 0000000..37ff8b1 --- /dev/null +++ b/src/mpd_now_playable/config/fields.py @@ -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) diff --git a/src/mpd_now_playable/config/load.py b/src/mpd_now_playable/config/load.py new file mode 100644 index 0000000..e74aa04 --- /dev/null +++ b/src/mpd_now_playable/config/load.py @@ -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() diff --git a/src/mpd_now_playable/config/model.py b/src/mpd_now_playable/config/model.py new file mode 100644 index 0000000..7336486 --- /dev/null +++ b/src/mpd_now_playable/config/model.py @@ -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) diff --git a/src/mpd_now_playable/config/schema.py b/src/mpd_now_playable/config/schema.py new file mode 100644 index 0000000..314e925 --- /dev/null +++ b/src/mpd_now_playable/config/schema.py @@ -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() diff --git a/src/mpd_now_playable/mpd/artwork_cache.py b/src/mpd_now_playable/mpd/artwork_cache.py index f99dbf2..b4b8146 100644 --- a/src/mpd_now_playable/mpd/artwork_cache.py +++ b/src/mpd_now_playable/mpd/artwork_cache.py @@ -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") diff --git a/src/mpd_now_playable/mpd/listener.py b/src/mpd_now_playable/mpd/listener.py index db46f19..9d6ba45 100644 --- a/src/mpd_now_playable/mpd/listener.py +++ b/src/mpd_now_playable/mpd/listener.py @@ -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: