From 2a66b0b4d57e7e913a702aa38ae1456c122ad8ee Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Tue, 18 Feb 2025 19:52:26 +0100 Subject: [PATCH] Fix: Final round of fixes for multi value serializing mess --- music_assistant/constants.py | 59 ++++++++-------- music_assistant/controllers/cache.py | 4 +- music_assistant/controllers/config.py | 67 ++++++++++++------- music_assistant/controllers/metadata.py | 4 +- music_assistant/controllers/music.py | 6 +- music_assistant/controllers/player_queues.py | 4 +- music_assistant/controllers/streams.py | 23 ++++--- music_assistant/controllers/webserver.py | 4 +- music_assistant/models/__init__.py | 4 +- music_assistant/models/core_controller.py | 4 +- .../_template_music_provider/__init__.py | 8 +-- .../_template_player_provider/__init__.py | 8 +-- .../_template_plugin_provider/__init__.py | 8 +-- music_assistant/providers/airplay/__init__.py | 4 +- .../providers/apple_music/__init__.py | 4 +- music_assistant/providers/audible/__init__.py | 4 +- .../providers/audiobookshelf/__init__.py | 4 +- .../providers/bluesound/__init__.py | 4 +- music_assistant/providers/builtin/__init__.py | 4 +- .../providers/chromecast/__init__.py | 4 +- music_assistant/providers/deezer/__init__.py | 4 +- music_assistant/providers/dlna/__init__.py | 4 +- .../providers/fanarttv/__init__.py | 4 +- .../providers/filesystem_local/__init__.py | 4 +- .../providers/filesystem_smb/__init__.py | 4 +- .../providers/fully_kiosk/__init__.py | 4 +- music_assistant/providers/hass/__init__.py | 27 ++++---- .../providers/hass_players/__init__.py | 12 ++-- .../providers/ibroadcast/__init__.py | 4 +- .../providers/jellyfin/__init__.py | 4 +- .../providers/musicbrainz/__init__.py | 4 +- .../providers/opensubsonic/__init__.py | 4 +- .../providers/player_group/__init__.py | 8 +-- music_assistant/providers/plex/__init__.py | 4 +- .../providers/podcastfeed/__init__.py | 4 +- music_assistant/providers/qobuz/__init__.py | 4 +- .../providers/radiobrowser/__init__.py | 9 +-- .../providers/siriusxm/__init__.py | 4 +- .../providers/slimproto/__init__.py | 4 +- .../providers/snapcast/__init__.py | 4 +- music_assistant/providers/sonos/__init__.py | 4 +- .../providers/sonos_s1/__init__.py | 4 +- .../providers/soundcloud/__init__.py | 4 +- music_assistant/providers/spotify/__init__.py | 4 +- .../providers/spotify_connect/__init__.py | 4 +- music_assistant/providers/test/__init__.py | 4 +- .../providers/theaudiodb/__init__.py | 4 +- music_assistant/providers/tidal/__init__.py | 4 +- music_assistant/providers/tunein/__init__.py | 4 +- music_assistant/providers/ytmusic/__init__.py | 4 +- pyproject.toml | 2 +- requirements_all.txt | 2 +- 52 files changed, 209 insertions(+), 186 deletions(-) diff --git a/music_assistant/constants.py b/music_assistant/constants.py index 1132e07f..c8dc2b2f 100644 --- a/music_assistant/constants.py +++ b/music_assistant/constants.py @@ -1,12 +1,12 @@ """All constants for Music Assistant.""" import pathlib -from typing import Final +from typing import Final, cast from music_assistant_models.config_entries import ( + MULTI_VALUE_SPLITTER, ConfigEntry, ConfigValueOption, - MultiValueConfigEntry, ) from music_assistant_models.enums import ConfigEntryType, ContentType from music_assistant_models.media_items import AudioFormat @@ -408,28 +408,30 @@ CONF_ENTRY_PLAYER_ICON_GROUP = ConfigEntry.from_dict( {**CONF_ENTRY_PLAYER_ICON.to_dict(), "default_value": "mdi-speaker-multiple"} ) -CONF_ENTRY_SAMPLE_RATES = MultiValueConfigEntry( + +CONF_ENTRY_SAMPLE_RATES = ConfigEntry( key=CONF_SAMPLE_RATES, - type=ConfigEntryType.INTEGER_TUPLE, + type=ConfigEntryType.SPLITTED_STRING, + multi_value=True, options=[ - ConfigValueOption("44.1kHz / 16 bits", (44100, 16)), - ConfigValueOption("44.1kHz / 24 bits", (44100, 24)), - ConfigValueOption("48kHz / 16 bits", (48000, 16)), - ConfigValueOption("48kHz / 24 bits", (48000, 24)), - ConfigValueOption("88.2kHz / 16 bits", (88200, 16)), - ConfigValueOption("88.2kHz / 24 bits", (88200, 24)), - ConfigValueOption("96kHz / 16 bits", (96000, 16)), - ConfigValueOption("96kHz / 24 bits", (96000, 24)), - ConfigValueOption("176.4kHz / 16 bits", (176400, 16)), - ConfigValueOption("176.4kHz / 24 bits", (176400, 24)), - ConfigValueOption("192kHz / 16 bits", (192000, 16)), - ConfigValueOption("192kHz / 24 bits", (192000, 24)), - ConfigValueOption("352.8kHz / 16 bits", (352800, 16)), - ConfigValueOption("352.8kHz / 24 bits", (352800, 24)), - ConfigValueOption("384kHz / 16 bits", (384000, 16)), - ConfigValueOption("384kHz / 24 bits", (384000, 24)), + ConfigValueOption("44.1kHz / 16 bits", f"44100{MULTI_VALUE_SPLITTER}16"), + ConfigValueOption("44.1kHz / 24 bits", f"44100{MULTI_VALUE_SPLITTER}24"), + ConfigValueOption("48kHz / 16 bits", f"48000{MULTI_VALUE_SPLITTER}16"), + ConfigValueOption("48kHz / 24 bits", f"48000{MULTI_VALUE_SPLITTER}24"), + ConfigValueOption("88.2kHz / 16 bits", f"88200{MULTI_VALUE_SPLITTER}16"), + ConfigValueOption("88.2kHz / 24 bits", f"88200{MULTI_VALUE_SPLITTER}24"), + ConfigValueOption("96kHz / 16 bits", f"96000{MULTI_VALUE_SPLITTER}16"), + ConfigValueOption("96kHz / 24 bits", f"96000{MULTI_VALUE_SPLITTER}24"), + ConfigValueOption("176.4kHz / 16 bits", f"176400{MULTI_VALUE_SPLITTER}16"), + ConfigValueOption("176.4kHz / 24 bits", f"176400{MULTI_VALUE_SPLITTER}24"), + ConfigValueOption("192kHz / 16 bits", f"192000{MULTI_VALUE_SPLITTER}16"), + ConfigValueOption("192kHz / 24 bits", f"192000{MULTI_VALUE_SPLITTER}24"), + ConfigValueOption("352.8kHz / 16 bits", f"352800{MULTI_VALUE_SPLITTER}16"), + ConfigValueOption("352.8kHz / 24 bits", f"352800{MULTI_VALUE_SPLITTER}24"), + ConfigValueOption("384kHz / 16 bits", f"384000{MULTI_VALUE_SPLITTER}16"), + ConfigValueOption("384kHz / 24 bits", f"384000{MULTI_VALUE_SPLITTER}24"), ], - default_value=[(44100, 16), (48000, 16)], + default_value=[f"44100{MULTI_VALUE_SPLITTER}16", f"44100{MULTI_VALUE_SPLITTER}24"], required=True, label="Sample rates supported by this player", category="advanced", @@ -499,23 +501,24 @@ def create_sample_rates_config_entry( safe_max_bit_depth: int = 16, hidden: bool = False, supported_sample_rates: list[int] | None = None, -) -> MultiValueConfigEntry: +) -> ConfigEntry: """Create sample rates config entry based on player specific helpers.""" assert CONF_ENTRY_SAMPLE_RATES.options - conf_entry = MultiValueConfigEntry.from_dict(CONF_ENTRY_SAMPLE_RATES.to_dict()) + conf_entry = ConfigEntry.from_dict(CONF_ENTRY_SAMPLE_RATES.to_dict()) conf_entry.hidden = hidden options: list[ConfigValueOption] = [] - default_value: list[tuple[int, int]] = [] + default_value: list[str] = [] for option in CONF_ENTRY_SAMPLE_RATES.options: - if not isinstance(option.value, tuple): - continue - sample_rate, bit_depth = option.value + option_value = cast(str, option.value) + sample_rate_str, bit_depth_str = option_value.split(MULTI_VALUE_SPLITTER, 1) + sample_rate = int(sample_rate_str) + bit_depth = int(bit_depth_str) if supported_sample_rates and sample_rate not in supported_sample_rates: continue if sample_rate <= max_sample_rate and bit_depth <= max_bit_depth: options.append(option) if sample_rate <= safe_max_sample_rate and bit_depth <= safe_max_bit_depth: - default_value.append(option.value) + default_value.append(option_value) conf_entry.options = options conf_entry.default_value = default_value return conf_entry diff --git a/music_assistant/controllers/cache.py b/music_assistant/controllers/cache.py index 59aeccba..8ca5116f 100644 --- a/music_assistant/controllers/cache.py +++ b/music_assistant/controllers/cache.py @@ -11,7 +11,7 @@ from collections import OrderedDict from collections.abc import Callable, Iterator, MutableMapping from typing import TYPE_CHECKING, Any, ParamSpec, TypeVar -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType from music_assistant_models.enums import ConfigEntryType from music_assistant.constants import DB_TABLE_CACHE, DB_TABLE_SETTINGS, MASS_LOGGER_NAME @@ -46,7 +46,7 @@ class CacheController(CoreController): async def get_config_entries( self, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """Return all Config Entries for this core module (if any).""" if action == CONF_CLEAR_CACHE: diff --git a/music_assistant/controllers/config.py b/music_assistant/controllers/config.py index 88927b28..6a0f6a52 100644 --- a/music_assistant/controllers/config.py +++ b/music_assistant/controllers/config.py @@ -15,8 +15,9 @@ from aiofiles.os import wrap from cryptography.fernet import Fernet, InvalidToken from music_assistant_models import config_entries from music_assistant_models.config_entries import ( + MULTI_VALUE_SPLITTER, ConfigEntry, - ConfigValueTypes, + ConfigValueType, CoreConfig, PlayerConfig, ProviderConfig, @@ -77,7 +78,7 @@ class ConfigController: self._data: dict[str, Any] = {} self.filename = os.path.join(self.mass.storage_path, "settings.json") self._timer_handle: asyncio.TimerHandle | None = None - self._value_cache: dict[str, ConfigValueTypes] = {} + self._value_cache: dict[str, ConfigValueType] = {} async def setup(self) -> None: """Async initialize of controller.""" @@ -208,7 +209,7 @@ class ConfigController: raise KeyError(msg) @api_command("config/providers/get_value") - async def get_provider_config_value(self, instance_id: str, key: str) -> ConfigValueTypes: + async def get_provider_config_value(self, instance_id: str, key: str) -> ConfigValueType: """Return single configentry value for a provider.""" cache_key = f"prov_conf_value_{instance_id}.{key}" if (cached_value := self._value_cache.get(cache_key)) is not None: @@ -229,7 +230,7 @@ class ConfigController: provider_domain: str, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup/configure a provider. @@ -261,7 +262,7 @@ class ConfigController: async def save_provider_config( self, provider_domain: str, - values: dict[str, ConfigValueTypes], + values: dict[str, ConfigValueType], instance_id: str | None = None, ) -> ProviderConfig: """ @@ -356,9 +357,12 @@ class ConfigController: self, player_id: str, key: str, - ) -> ConfigValueTypes: + unpack_splitted_values: bool = False, + ) -> ConfigValueType: """Return single configentry value for a player.""" conf = await self.get_player_config(player_id) + if unpack_splitted_values: + return conf.values[key].get_splitted_values() return ( conf.values[key].value if conf.values[key].value is not None @@ -366,8 +370,8 @@ class ConfigController: ) def get_raw_player_config_value( - self, player_id: str, key: str, default: ConfigValueTypes = None - ) -> ConfigValueTypes: + self, player_id: str, key: str, default: ConfigValueType = None + ) -> ConfigValueType: """ Return (raw) single configentry value for a player. @@ -380,7 +384,7 @@ class ConfigController: @api_command("config/players/save") async def save_player_config( - self, player_id: str, values: dict[str, ConfigValueTypes] + self, player_id: str, values: dict[str, ConfigValueType] ) -> PlayerConfig: """Save/update PlayerConfig.""" config = await self.get_player_config(player_id) @@ -513,7 +517,7 @@ class ConfigController: provider: str, name: str, enabled: bool, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> None: """ Create default/empty PlayerConfig. @@ -600,7 +604,7 @@ class ConfigController: return CoreConfig.parse(config_entries, raw_conf) @api_command("config/core/get_value") - async def get_core_config_value(self, domain: str, key: str) -> ConfigValueTypes: + async def get_core_config_value(self, domain: str, key: str) -> ConfigValueType: """Return single configentry value for a core controller.""" conf = await self.get_core_config(domain) return ( @@ -614,7 +618,7 @@ class ConfigController: self, domain: str, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to configure a core controller. @@ -635,7 +639,7 @@ class ConfigController: async def save_core_config( self, domain: str, - values: dict[str, ConfigValueTypes], + values: dict[str, ConfigValueType], ) -> CoreConfig: """Save CoreController Config values.""" config = await self.get_core_config(domain) @@ -656,8 +660,8 @@ class ConfigController: return await self.get_core_config(domain) def get_raw_core_config_value( - self, core_module: str, key: str, default: ConfigValueTypes = None - ) -> ConfigValueTypes: + self, core_module: str, key: str, default: ConfigValueType = None + ) -> ConfigValueType: """ Return (raw) single configentry value for a core controller. @@ -669,8 +673,8 @@ class ConfigController: ) def get_raw_provider_config_value( - self, provider_instance: str, key: str, default: ConfigValueTypes = None - ) -> ConfigValueTypes: + self, provider_instance: str, key: str, default: ConfigValueType = None + ) -> ConfigValueType: """ Return (raw) single config(entry) value for a provider. @@ -685,7 +689,7 @@ class ConfigController: self, provider_instance: str, key: str, - value: ConfigValueTypes, + value: ConfigValueType, encrypted: bool = False, ) -> None: """ @@ -707,9 +711,7 @@ class ConfigController: if prov := self.mass.get_provider(provider_instance, return_unavailable=True): prov.config.values[key].value = value - def set_raw_core_config_value( - self, core_module: str, key: str, value: ConfigValueTypes - ) -> None: + def set_raw_core_config_value(self, core_module: str, key: str, value: ConfigValueType) -> None: """ Set (raw) single config(entry) value for a core controller. @@ -720,9 +722,7 @@ class ConfigController: self.set(f"{CONF_CORE}/{core_module}", CoreConfig({}, core_module).to_raw()) self.set(f"{CONF_CORE}/{core_module}/values/{key}", value) - def set_raw_player_config_value( - self, player_id: str, key: str, value: ConfigValueTypes - ) -> None: + def set_raw_player_config_value(self, player_id: str, key: str, value: ConfigValueType) -> None: """ Set (raw) single config(entry) value for a player. @@ -797,6 +797,21 @@ class ConfigController: self._data[CONF_PROVIDERS].pop(instance_id, None) LOGGER.warning("Removed corrupt provider configuration: %s", instance_id) changed = True + # migrate sample_rates config entry + for player_id, player_config in list(self._data.get(CONF_PLAYERS, {}).items()): + if not (values := player_config.get("values")): + continue + if not (sample_rates := values.get("sample_rates")): + continue + if not isinstance(sample_rates, list): + del player_config["values"]["sample_rates"] + if not any(isinstance(x, list) for x in sample_rates): + continue + player_config["values"]["sample_rates"] = [ + f"{x[0]}{MULTI_VALUE_SPLITTER}{x[1]}" if isinstance(x, list) else x + for x in sample_rates + ] + changed = True if changed: await self._async_save() @@ -825,7 +840,7 @@ class ConfigController: await self.mass.load_provider_config(config) async def _update_provider_config( - self, instance_id: str, values: dict[str, ConfigValueTypes] + self, instance_id: str, values: dict[str, ConfigValueType] ) -> ProviderConfig: """Update ProviderConfig.""" config = await self.get_provider_config(instance_id) @@ -865,7 +880,7 @@ class ConfigController: async def _add_provider_config( self, provider_domain: str, - values: dict[str, ConfigValueTypes], + values: dict[str, ConfigValueType], ) -> list[ConfigEntry] | ProviderConfig: """ Add new Provider (instance). diff --git a/music_assistant/controllers/metadata.py b/music_assistant/controllers/metadata.py index cd0a20a0..16756306 100644 --- a/music_assistant/controllers/metadata.py +++ b/music_assistant/controllers/metadata.py @@ -16,7 +16,7 @@ from uuid import uuid4 import aiofiles from aiohttp import web -from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueType from music_assistant_models.enums import ( AlbumType, ConfigEntryType, @@ -131,7 +131,7 @@ class MetaDataController(CoreController): async def get_config_entries( self, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """Return all Config Entries for this core module (if any).""" return ( diff --git a/music_assistant/controllers/music.py b/music_assistant/controllers/music.py index edc8d9c7..d8950aae 100644 --- a/music_assistant/controllers/music.py +++ b/music_assistant/controllers/music.py @@ -11,7 +11,7 @@ from itertools import zip_longest from math import inf from typing import TYPE_CHECKING, Final, cast -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType from music_assistant_models.enums import ( CacheCategory, ConfigEntryType, @@ -113,7 +113,7 @@ class MusicController(CoreController): async def get_config_entries( self, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """Return all Config Entries for this core module (if any).""" entries = ( @@ -1048,6 +1048,8 @@ class MusicController(CoreController): if provider_instance.startswith(("filesystem", "jellyfin", "plex", "opensubsonic")): # removal of a local provider can become messy very fast due to the relations # such as images pointing at the files etc. so we just reset the whole db + # TODO: Handle this more gracefully in the future where we remove the provider + # and traverse the database to also remove all related items. self.logger.warning( "Removal of local provider detected, issuing full database reset..." ) diff --git a/music_assistant/controllers/player_queues.py b/music_assistant/controllers/player_queues.py index 93459cfb..f281411e 100644 --- a/music_assistant/controllers/player_queues.py +++ b/music_assistant/controllers/player_queues.py @@ -19,7 +19,7 @@ import time from types import NoneType from typing import TYPE_CHECKING, Any, TypedDict, cast -from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueType from music_assistant_models.enums import ( CacheCategory, ConfigEntryType, @@ -139,7 +139,7 @@ class PlayerQueuesController(CoreController): async def get_config_entries( self, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """Return all Config Entries for this core module (if any).""" enqueue_options = [ConfigValueOption(x.name, x.value) for x in QueueOption] diff --git a/music_assistant/controllers/streams.py b/music_assistant/controllers/streams.py index 2efb1d16..1a039a31 100644 --- a/music_assistant/controllers/streams.py +++ b/music_assistant/controllers/streams.py @@ -16,7 +16,7 @@ from typing import TYPE_CHECKING from aiofiles.os import wrap from aiohttp import web -from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueType from music_assistant_models.enums import ( ConfigEntryType, ContentType, @@ -115,7 +115,7 @@ class StreamsController(CoreController): async def get_config_entries( self, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """Return all Config Entries for this core module (if any).""" default_ip = await get_ip() @@ -1006,11 +1006,14 @@ class StreamsController(CoreController): ) -> AudioFormat: """Parse (player specific) output format details for given format string.""" content_type: ContentType = ContentType.try_parse(output_format_str) - supported_rates_conf = await self.mass.config.get_player_config_value( - player.player_id, CONF_SAMPLE_RATES + supported_rates_conf: list[ + tuple[str, str] + ] = await self.mass.config.get_player_config_value( + player.player_id, CONF_SAMPLE_RATES, unpack_splitted_values=True ) - supported_sample_rates: tuple[int] = tuple(x[0] for x in supported_rates_conf) - supported_bit_depths: tuple[int] = tuple(x[1] for x in supported_rates_conf) + supported_sample_rates: tuple[int] = tuple(int(x[0]) for x in supported_rates_conf) + supported_bit_depths: tuple[int] = tuple(int(x[1]) for x in supported_rates_conf) + player_max_bit_depth = max(supported_bit_depths) if content_type.is_pcm() or content_type == ContentType.WAV: # parse pcm details from format string @@ -1046,10 +1049,12 @@ class StreamsController(CoreController): player: Player, ) -> AudioFormat: """Parse (player specific) flow stream PCM format.""" - supported_rates_conf = await self.mass.config.get_player_config_value( - player.player_id, CONF_SAMPLE_RATES + supported_rates_conf: list[ + tuple[str, str] + ] = await self.mass.config.get_player_config_value( + player.player_id, CONF_SAMPLE_RATES, unpack_splitted_values=True ) - supported_sample_rates: tuple[int] = tuple(x[0] for x in supported_rates_conf) + supported_sample_rates: tuple[int] = tuple(int(x[0]) for x in supported_rates_conf) output_sample_rate = DEFAULT_PCM_FORMAT.sample_rate for sample_rate in (192000, 96000, 48000, 44100): if sample_rate in supported_sample_rates: diff --git a/music_assistant/controllers/webserver.py b/music_assistant/controllers/webserver.py index 44c32a12..67d24efc 100644 --- a/music_assistant/controllers/webserver.py +++ b/music_assistant/controllers/webserver.py @@ -39,7 +39,7 @@ from music_assistant.models.core_controller import CoreController if TYPE_CHECKING: from collections.abc import Awaitable - from music_assistant_models.config_entries import ConfigValueTypes, CoreConfig + from music_assistant_models.config_entries import ConfigValueType, CoreConfig from music_assistant_models.event import MassEvent DEFAULT_SERVER_PORT = 8095 @@ -73,7 +73,7 @@ class WebserverController(CoreController): async def get_config_entries( self, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """Return all Config Entries for this core module (if any).""" default_publish_ip = await get_ip() diff --git a/music_assistant/models/__init__.py b/music_assistant/models/__init__.py index d9e88472..ce2bab18 100644 --- a/music_assistant/models/__init__.py +++ b/music_assistant/models/__init__.py @@ -10,7 +10,7 @@ from .player_provider import PlayerProvider from .plugin import PluginProvider if TYPE_CHECKING: - from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes, ProviderConfig + from music_assistant_models.config_entries import ConfigEntry, ConfigValueType, ProviderConfig from music_assistant_models.provider import ProviderManifest from music_assistant import MusicAssistant @@ -33,7 +33,7 @@ class ProviderModuleType(Protocol): mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/models/core_controller.py b/music_assistant/models/core_controller.py index f73ed6a5..7f2532c3 100644 --- a/music_assistant/models/core_controller.py +++ b/music_assistant/models/core_controller.py @@ -11,7 +11,7 @@ from music_assistant_models.provider import ProviderManifest from music_assistant.constants import CONF_LOG_LEVEL, MASS_LOGGER_NAME if TYPE_CHECKING: - from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes, CoreConfig + from music_assistant_models.config_entries import ConfigEntry, ConfigValueType, CoreConfig from music_assistant import MusicAssistant @@ -38,7 +38,7 @@ class CoreController: async def get_config_entries( self, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """Return all Config Entries for this core module (if any).""" return () diff --git a/music_assistant/providers/_template_music_provider/__init__.py b/music_assistant/providers/_template_music_provider/__init__.py index 3ecf27c1..b9600575 100644 --- a/music_assistant/providers/_template_music_provider/__init__.py +++ b/music_assistant/providers/_template_music_provider/__init__.py @@ -58,7 +58,7 @@ from music_assistant_models.streamdetails import StreamDetails from music_assistant.models.music_provider import MusicProvider if TYPE_CHECKING: - from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes, ProviderConfig + from music_assistant_models.config_entries import ConfigEntry, ConfigValueType, ProviderConfig from music_assistant_models.provider import ProviderManifest from music_assistant.mass import MusicAssistant @@ -79,7 +79,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. @@ -90,9 +90,9 @@ async def get_config_entries( """ # ruff: noqa: ARG001 # Config Entries are used to configure the Music Provider if needed. - # See the models of ConfigEntry and ConfigValueTypes for more information what is supported. + # See the models of ConfigEntry and ConfigValueType for more information what is supported. # The ConfigEntry is a dataclass that represents a single configuration entry. - # The ConfigValueTypes is an Enum that represents the type of value that + # The ConfigValueType is an Enum that represents the type of value that # can be stored in a ConfigEntry. # If your provider does not need any configuration, you can return an empty tuple. diff --git a/music_assistant/providers/_template_player_provider/__init__.py b/music_assistant/providers/_template_player_provider/__init__.py index 8bb1350c..33ff6dea 100644 --- a/music_assistant/providers/_template_player_provider/__init__.py +++ b/music_assistant/providers/_template_player_provider/__init__.py @@ -43,7 +43,7 @@ from music_assistant.models.player_provider import PlayerProvider if TYPE_CHECKING: from music_assistant_models.config_entries import ( ConfigEntry, - ConfigValueTypes, + ConfigValueType, PlayerConfig, ProviderConfig, ) @@ -68,7 +68,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. @@ -79,9 +79,9 @@ async def get_config_entries( """ # ruff: noqa: ARG001 # Config Entries are used to configure the Player Provider if needed. - # See the models of ConfigEntry and ConfigValueTypes for more information what is supported. + # See the models of ConfigEntry and ConfigValueType for more information what is supported. # The ConfigEntry is a dataclass that represents a single configuration entry. - # The ConfigValueTypes is an Enum that represents the type of value that + # The ConfigValueType is an Enum that represents the type of value that # can be stored in a ConfigEntry. # If your provider does not need any configuration, you can return an empty tuple. return () diff --git a/music_assistant/providers/_template_plugin_provider/__init__.py b/music_assistant/providers/_template_plugin_provider/__init__.py index 0ca1a613..fbed2400 100644 --- a/music_assistant/providers/_template_plugin_provider/__init__.py +++ b/music_assistant/providers/_template_plugin_provider/__init__.py @@ -43,7 +43,7 @@ from music_assistant_models.media_items.audio_format import AudioFormat from music_assistant.models.plugin import PluginProvider, PluginSource if TYPE_CHECKING: - from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes, ProviderConfig + from music_assistant_models.config_entries import ConfigEntry, ConfigValueType, ProviderConfig from music_assistant_models.event import MassEvent from music_assistant_models.provider import ProviderManifest @@ -65,7 +65,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. @@ -76,9 +76,9 @@ async def get_config_entries( """ # ruff: noqa: ARG001 # Config Entries are used to configure the Provider if needed. - # See the models of ConfigEntry and ConfigValueTypes for more information what is supported. + # See the models of ConfigEntry and ConfigValueType for more information what is supported. # The ConfigEntry is a dataclass that represents a single configuration entry. - # The ConfigValueTypes is an Enum that represents the type of value that + # The ConfigValueType is an Enum that represents the type of value that # can be stored in a ConfigEntry. # If your provider does not need any configuration, you can return an empty tuple. return () diff --git a/music_assistant/providers/airplay/__init__.py b/music_assistant/providers/airplay/__init__.py index 67c2a009..35463eca 100644 --- a/music_assistant/providers/airplay/__init__.py +++ b/music_assistant/providers/airplay/__init__.py @@ -4,7 +4,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, cast -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes, ProviderConfig +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType, ProviderConfig from music_assistant_models.enums import ConfigEntryType from music_assistant_models.provider import ProviderManifest @@ -24,7 +24,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/apple_music/__init__.py b/music_assistant/providers/apple_music/__init__.py index 60d175e2..bfee2038 100644 --- a/music_assistant/providers/apple_music/__init__.py +++ b/music_assistant/providers/apple_music/__init__.py @@ -8,7 +8,7 @@ import os from typing import TYPE_CHECKING, Any import aiofiles -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType from music_assistant_models.enums import ( AlbumType, ConfigEntryType, @@ -82,7 +82,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/audible/__init__.py b/music_assistant/providers/audible/__init__.py index 167f2050..0f348d97 100644 --- a/music_assistant/providers/audible/__init__.py +++ b/music_assistant/providers/audible/__init__.py @@ -13,7 +13,7 @@ import audible from music_assistant_models.config_entries import ( ConfigEntry, ConfigValueOption, - ConfigValueTypes, + ConfigValueType, ProviderConfig, ) from music_assistant_models.enums import ConfigEntryType, EventType, MediaType, ProviderFeature @@ -60,7 +60,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, # noqa: ARG001 action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/audiobookshelf/__init__.py b/music_assistant/providers/audiobookshelf/__init__.py index f47afa54..8131bedc 100644 --- a/music_assistant/providers/audiobookshelf/__init__.py +++ b/music_assistant/providers/audiobookshelf/__init__.py @@ -24,7 +24,7 @@ from aioaudiobookshelf.schema.library import ( ) from aioaudiobookshelf.schema.library import LibraryMediaType as AbsLibraryMediaType from mashumaro.mixins.dict import DataClassDictMixin -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes, ProviderConfig +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType, ProviderConfig from music_assistant_models.enums import ( ConfigEntryType, ContentType, @@ -136,7 +136,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/bluesound/__init__.py b/music_assistant/providers/bluesound/__init__.py index 32c488ed..08f03eb1 100644 --- a/music_assistant/providers/bluesound/__init__.py +++ b/music_assistant/providers/bluesound/__init__.py @@ -28,7 +28,7 @@ from music_assistant.helpers.util import ( from music_assistant.models.player_provider import PlayerProvider if TYPE_CHECKING: - from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes, ProviderConfig + from music_assistant_models.config_entries import ConfigEntry, ConfigValueType, ProviderConfig from music_assistant_models.provider import ProviderManifest from zeroconf.asyncio import AsyncServiceInfo @@ -78,7 +78,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """Set up legacy BluOS devices.""" # ruff: noqa: ARG001 diff --git a/music_assistant/providers/builtin/__init__.py b/music_assistant/providers/builtin/__init__.py index 047b4258..9dacc398 100644 --- a/music_assistant/providers/builtin/__init__.py +++ b/music_assistant/providers/builtin/__init__.py @@ -45,7 +45,7 @@ from music_assistant.helpers.uri import parse_uri from music_assistant.models.music_provider import MusicProvider if TYPE_CHECKING: - from music_assistant_models.config_entries import ConfigValueTypes, ProviderConfig + from music_assistant_models.config_entries import ConfigValueType, ProviderConfig from music_assistant_models.provider import ProviderManifest from music_assistant.mass import MusicAssistant @@ -108,7 +108,7 @@ async def get_config_entries( mass: MusicAssistant, # noqa: ARG001 instance_id: str | None = None, # noqa: ARG001 action: str | None = None, # noqa: ARG001 - values: dict[str, ConfigValueTypes] | None = None, # noqa: ARG001 + values: dict[str, ConfigValueType] | None = None, # noqa: ARG001 ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/chromecast/__init__.py b/music_assistant/providers/chromecast/__init__.py index 586b2856..f6957c29 100644 --- a/music_assistant/providers/chromecast/__init__.py +++ b/music_assistant/providers/chromecast/__init__.py @@ -47,7 +47,7 @@ from music_assistant.models.player_provider import PlayerProvider from .helpers import CastStatusListener, ChromecastInfo if TYPE_CHECKING: - from music_assistant_models.config_entries import ConfigValueTypes, ProviderConfig + from music_assistant_models.config_entries import ConfigValueType, ProviderConfig from music_assistant_models.provider import ProviderManifest from pychromecast.controllers.media import MediaStatus from pychromecast.controllers.receiver import CastStatus @@ -101,7 +101,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/deezer/__init__.py b/music_assistant/providers/deezer/__init__.py index 81c9c852..a7a5ae3e 100644 --- a/music_assistant/providers/deezer/__init__.py +++ b/music_assistant/providers/deezer/__init__.py @@ -11,7 +11,7 @@ import deezer from aiohttp import ClientSession, ClientTimeout from Crypto.Cipher import Blowfish from deezer import exceptions as deezer_exceptions -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes, ProviderConfig +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType, ProviderConfig from music_assistant_models.enums import ( AlbumType, ConfigEntryType, @@ -122,7 +122,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, # noqa: ARG001 action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """Return Config entries to setup this provider.""" # Action is to launch oauth flow diff --git a/music_assistant/providers/dlna/__init__.py b/music_assistant/providers/dlna/__init__.py index d8b6322c..22836391 100644 --- a/music_assistant/providers/dlna/__init__.py +++ b/music_assistant/providers/dlna/__init__.py @@ -22,7 +22,7 @@ from async_upnp_client.client_factory import UpnpFactory from async_upnp_client.exceptions import UpnpError, UpnpResponseError from async_upnp_client.profiles.dlna import DmrDevice, TransportState from async_upnp_client.search import async_search -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType from music_assistant_models.enums import ConfigEntryType, PlayerFeature, PlayerState, PlayerType from music_assistant_models.errors import PlayerUnavailableError from music_assistant_models.player import DeviceInfo, Player, PlayerMedia @@ -88,7 +88,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/fanarttv/__init__.py b/music_assistant/providers/fanarttv/__init__.py index f641cc50..89a94c5b 100644 --- a/music_assistant/providers/fanarttv/__init__.py +++ b/music_assistant/providers/fanarttv/__init__.py @@ -16,7 +16,7 @@ from music_assistant.helpers.throttle_retry import Throttler from music_assistant.models.metadata_provider import MetadataProvider if TYPE_CHECKING: - from music_assistant_models.config_entries import ConfigValueTypes, ProviderConfig + from music_assistant_models.config_entries import ConfigValueType, ProviderConfig from music_assistant_models.media_items import Album, Artist from music_assistant_models.provider import ProviderManifest @@ -51,7 +51,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/filesystem_local/__init__.py b/music_assistant/providers/filesystem_local/__init__.py index 4a14772c..797a33d5 100644 --- a/music_assistant/providers/filesystem_local/__init__.py +++ b/music_assistant/providers/filesystem_local/__init__.py @@ -90,7 +90,7 @@ from .helpers import ( ) if TYPE_CHECKING: - from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes, ProviderConfig + from music_assistant_models.config_entries import ConfigEntry, ConfigValueType, ProviderConfig from music_assistant_models.provider import ProviderManifest from music_assistant.mass import MusicAssistant @@ -116,7 +116,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/filesystem_smb/__init__.py b/music_assistant/providers/filesystem_smb/__init__.py index de4ee236..72b2213e 100644 --- a/music_assistant/providers/filesystem_smb/__init__.py +++ b/music_assistant/providers/filesystem_smb/__init__.py @@ -6,7 +6,7 @@ import os import platform from typing import TYPE_CHECKING -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType from music_assistant_models.enums import ConfigEntryType from music_assistant_models.errors import LoginFailed @@ -56,7 +56,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/fully_kiosk/__init__.py b/music_assistant/providers/fully_kiosk/__init__.py index 630a8455..b096db65 100644 --- a/music_assistant/providers/fully_kiosk/__init__.py +++ b/music_assistant/providers/fully_kiosk/__init__.py @@ -8,7 +8,7 @@ import time from typing import TYPE_CHECKING from fullykiosk import FullyKiosk -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType from music_assistant_models.enums import ConfigEntryType, PlayerFeature, PlayerState, PlayerType from music_assistant_models.errors import PlayerUnavailableError, SetupFailedError from music_assistant_models.player import DeviceInfo, Player, PlayerMedia @@ -47,7 +47,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/hass/__init__.py b/music_assistant/providers/hass/__init__.py index ace6ee7f..245b6419 100644 --- a/music_assistant/providers/hass/__init__.py +++ b/music_assistant/providers/hass/__init__.py @@ -24,12 +24,7 @@ from hass_client.utils import ( get_token, get_websocket_url, ) -from music_assistant_models.config_entries import ( - ConfigEntry, - ConfigValueOption, - ConfigValueTypes, - MultiValueConfigEntry, -) +from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueType from music_assistant_models.enums import ConfigEntryType from music_assistant_models.errors import LoginFailed, SetupFailedError from music_assistant_models.player_control import PlayerControl @@ -70,7 +65,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. @@ -188,21 +183,24 @@ async def get_config_entries( return ( *base_entries, - MultiValueConfigEntry( + ConfigEntry( key=CONF_POWER_CONTROLS, type=ConfigEntryType.STRING, + multi_value=True, label=CONF_POWER_CONTROLS, default_value=[], ), - MultiValueConfigEntry( + ConfigEntry( key=CONF_VOLUME_CONTROLS, type=ConfigEntryType.STRING, + multi_value=True, label=CONF_VOLUME_CONTROLS, default_value=[], ), - MultiValueConfigEntry( + ConfigEntry( key=CONF_MUTE_CONTROLS, type=ConfigEntryType.STRING, + multi_value=True, label=CONF_MUTE_CONTROLS, default_value=[], ), @@ -254,9 +252,10 @@ async def _get_player_control_config_entries(hass: HomeAssistantClient) -> tuple all_mute_entities.sort(key=lambda x: x.title) all_volume_entities.sort(key=lambda x: x.title) return ( - MultiValueConfigEntry( + ConfigEntry( key=CONF_POWER_CONTROLS, type=ConfigEntryType.STRING, + multi_value=True, label="Player Power Control entities", required=True, options=all_power_entities, @@ -265,9 +264,10 @@ async def _get_player_control_config_entries(hass: HomeAssistantClient) -> tuple "like to import as player Power controls in Music Assistant.", category="player_controls", ), - MultiValueConfigEntry( + ConfigEntry( key=CONF_VOLUME_CONTROLS, type=ConfigEntryType.STRING, + multi_value=True, label="Player Volume Control entities", required=True, options=all_volume_entities, @@ -276,9 +276,10 @@ async def _get_player_control_config_entries(hass: HomeAssistantClient) -> tuple "like to import as player Volume controls in Music Assistant.", category="player_controls", ), - MultiValueConfigEntry( + ConfigEntry( key=CONF_MUTE_CONTROLS, type=ConfigEntryType.STRING, + multi_value=True, label="Player Mute Control entities", required=True, options=all_mute_entities, diff --git a/music_assistant/providers/hass_players/__init__.py b/music_assistant/providers/hass_players/__init__.py index 93a24a02..67fe2a2b 100644 --- a/music_assistant/providers/hass_players/__init__.py +++ b/music_assistant/providers/hass_players/__init__.py @@ -12,12 +12,7 @@ import time from typing import TYPE_CHECKING, Any from hass_client.exceptions import FailedCommand -from music_assistant_models.config_entries import ( - ConfigEntry, - ConfigValueOption, - ConfigValueTypes, - MultiValueConfigEntry, -) +from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueType from music_assistant_models.enums import ConfigEntryType, PlayerFeature, PlayerState, PlayerType from music_assistant_models.errors import SetupFailedError from music_assistant_models.player import DeviceInfo, Player, PlayerMedia @@ -129,7 +124,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, # noqa: ARG001 action: str | None = None, # noqa: ARG001 - values: dict[str, ConfigValueTypes] | None = None, # noqa: ARG001 + values: dict[str, ConfigValueType] | None = None, # noqa: ARG001 ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. @@ -145,9 +140,10 @@ async def get_config_entries( name = f"{state['attributes']['friendly_name']} ({state['entity_id']})" player_entities.append(ConfigValueOption(name, state["entity_id"])) return ( - MultiValueConfigEntry( + ConfigEntry( key=CONF_PLAYERS, type=ConfigEntryType.STRING, + multi_value=True, label="Player entities", required=True, options=player_entities, diff --git a/music_assistant/providers/ibroadcast/__init__.py b/music_assistant/providers/ibroadcast/__init__.py index 39770488..2a3a11bc 100644 --- a/music_assistant/providers/ibroadcast/__init__.py +++ b/music_assistant/providers/ibroadcast/__init__.py @@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any from aiohttp import ClientSession from ibroadcastaio import IBroadcastClient -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType from music_assistant_models.enums import ( AlbumType, ConfigEntryType, @@ -74,7 +74,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/jellyfin/__init__.py b/music_assistant/providers/jellyfin/__init__.py index 7480a7b2..9e698dcf 100644 --- a/music_assistant/providers/jellyfin/__init__.py +++ b/music_assistant/providers/jellyfin/__init__.py @@ -11,7 +11,7 @@ from typing import TYPE_CHECKING from aiojellyfin import MediaLibrary as JellyMediaLibrary from aiojellyfin import NotFound, authenticate_by_name from aiojellyfin.session import SessionConfiguration -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes, ProviderConfig +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType, ProviderConfig from music_assistant_models.enums import ConfigEntryType, MediaType, ProviderFeature, StreamType from music_assistant_models.errors import LoginFailed, MediaNotFoundError from music_assistant_models.media_items import ( @@ -71,7 +71,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/musicbrainz/__init__.py b/music_assistant/providers/musicbrainz/__init__.py index 61daeee9..a7522994 100644 --- a/music_assistant/providers/musicbrainz/__init__.py +++ b/music_assistant/providers/musicbrainz/__init__.py @@ -23,7 +23,7 @@ from music_assistant.helpers.util import parse_title_and_version from music_assistant.models.metadata_provider import MetadataProvider if TYPE_CHECKING: - from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes, ProviderConfig + from music_assistant_models.config_entries import ConfigEntry, ConfigValueType, ProviderConfig from music_assistant_models.media_items import Album, Track from music_assistant_models.provider import ProviderManifest @@ -47,7 +47,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/opensubsonic/__init__.py b/music_assistant/providers/opensubsonic/__init__.py index eb070f41..2505c386 100644 --- a/music_assistant/providers/opensubsonic/__init__.py +++ b/music_assistant/providers/opensubsonic/__init__.py @@ -4,7 +4,7 @@ from __future__ import annotations from typing import TYPE_CHECKING -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes, ProviderConfig +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType, ProviderConfig from music_assistant_models.enums import ConfigEntryType from music_assistant.constants import CONF_PASSWORD, CONF_PATH, CONF_PORT, CONF_USERNAME @@ -35,7 +35,7 @@ async def get_config_entries( mass: MusicAssistant, # noqa: ARG001 instance_id: str | None = None, # noqa: ARG001 action: str | None = None, # noqa: ARG001 - values: dict[str, ConfigValueTypes] | None = None, # noqa: ARG001 + values: dict[str, ConfigValueType] | None = None, # noqa: ARG001 ) -> tuple[ConfigEntry, ...]: """Return Config entries to setup this provider.""" return ( diff --git a/music_assistant/providers/player_group/__init__.py b/music_assistant/providers/player_group/__init__.py index 259b193d..792fba02 100644 --- a/music_assistant/providers/player_group/__init__.py +++ b/music_assistant/providers/player_group/__init__.py @@ -18,8 +18,7 @@ from aiohttp import web from music_assistant_models.config_entries import ( ConfigEntry, ConfigValueOption, - ConfigValueTypes, - MultiValueConfigEntry, + ConfigValueType, PlayerConfig, ) from music_assistant_models.constants import PLAYER_CONTROL_NATIVE, PLAYER_CONTROL_NONE @@ -101,9 +100,10 @@ CONF_ENTRY_GROUP_TYPE = ConfigEntry( hidden=True, required=True, ) -CONF_ENTRY_GROUP_MEMBERS = MultiValueConfigEntry( +CONF_ENTRY_GROUP_MEMBERS = ConfigEntry( key=CONF_GROUP_MEMBERS, type=ConfigEntryType.STRING, + multi_value=True, label="Group members", default_value=[], description="Select all players you want to be part of this group", @@ -143,7 +143,7 @@ async def get_config_entries( mass: MusicAssistant, # noqa: ARG001 instance_id: str | None = None, # noqa: ARG001 action: str | None = None, # noqa: ARG001 - values: dict[str, ConfigValueTypes] | None = None, # noqa: ARG001 + values: dict[str, ConfigValueType] | None = None, # noqa: ARG001 ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/plex/__init__.py b/music_assistant/providers/plex/__init__.py index 62a52146..81f5a22d 100644 --- a/music_assistant/providers/plex/__init__.py +++ b/music_assistant/providers/plex/__init__.py @@ -14,7 +14,7 @@ import requests from music_assistant_models.config_entries import ( ConfigEntry, ConfigValueOption, - ConfigValueTypes, + ConfigValueType, ProviderConfig, ) from music_assistant_models.enums import ( @@ -105,7 +105,7 @@ async def get_config_entries( # noqa: PLR0915 mass: MusicAssistant, instance_id: str | None = None, # noqa: ARG001 action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/podcastfeed/__init__.py b/music_assistant/providers/podcastfeed/__init__.py index bc69bfa7..9ced926a 100644 --- a/music_assistant/providers/podcastfeed/__init__.py +++ b/music_assistant/providers/podcastfeed/__init__.py @@ -14,7 +14,7 @@ from io import BytesIO from typing import TYPE_CHECKING import podcastparser -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType from music_assistant_models.enums import ( ConfigEntryType, ContentType, @@ -63,7 +63,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/qobuz/__init__.py b/music_assistant/providers/qobuz/__init__.py index 5b7f774f..0549e454 100644 --- a/music_assistant/providers/qobuz/__init__.py +++ b/music_assistant/providers/qobuz/__init__.py @@ -9,7 +9,7 @@ from contextlib import suppress from typing import TYPE_CHECKING from aiohttp import client_exceptions -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType from music_assistant_models.enums import ( AlbumType, ConfigEntryType, @@ -91,7 +91,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/radiobrowser/__init__.py b/music_assistant/providers/radiobrowser/__init__.py index 3f617813..a70c0944 100644 --- a/music_assistant/providers/radiobrowser/__init__.py +++ b/music_assistant/providers/radiobrowser/__init__.py @@ -5,7 +5,7 @@ from __future__ import annotations from collections.abc import AsyncGenerator, Sequence from typing import TYPE_CHECKING, cast -from music_assistant_models.config_entries import ConfigEntry, MultiValueConfigEntry +from music_assistant_models.config_entries import ConfigEntry from music_assistant_models.enums import ( ConfigEntryType, ContentType, @@ -44,7 +44,7 @@ SUPPORTED_FEATURES = { } if TYPE_CHECKING: - from music_assistant_models.config_entries import ConfigValueTypes, ProviderConfig + from music_assistant_models.config_entries import ConfigValueType, ProviderConfig from music_assistant_models.provider import ProviderManifest from music_assistant.mass import MusicAssistant @@ -64,7 +64,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. @@ -74,12 +74,13 @@ async def get_config_entries( """ # ruff: noqa: ARG001 D205 return ( - MultiValueConfigEntry( + ConfigEntry( # RadioBrowser doesn't support a library feature at all # but MA users like to favorite their radio stations and # have that included in backups so we store it in the config. key=CONF_STORED_RADIOS, type=ConfigEntryType.STRING, + multi_value=True, label=CONF_STORED_RADIOS, default_value=[], required=False, diff --git a/music_assistant/providers/siriusxm/__init__.py b/music_assistant/providers/siriusxm/__init__.py index cef97208..7be24088 100644 --- a/music_assistant/providers/siriusxm/__init__.py +++ b/music_assistant/providers/siriusxm/__init__.py @@ -5,7 +5,7 @@ from __future__ import annotations from collections.abc import AsyncGenerator, Awaitable, Sequence from typing import TYPE_CHECKING, Any, cast -from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueType from music_assistant_models.enums import ( ConfigEntryType, ContentType, @@ -58,7 +58,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/slimproto/__init__.py b/music_assistant/providers/slimproto/__init__.py index 153503e0..02f800b5 100644 --- a/music_assistant/providers/slimproto/__init__.py +++ b/music_assistant/providers/slimproto/__init__.py @@ -22,7 +22,7 @@ from aioslimproto.server import SlimServer from music_assistant_models.config_entries import ( ConfigEntry, ConfigValueOption, - ConfigValueTypes, + ConfigValueType, PlayerConfig, ) from music_assistant_models.enums import ( @@ -150,7 +150,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/snapcast/__init__.py b/music_assistant/providers/snapcast/__init__.py index f2d8d5b6..1768f657 100644 --- a/music_assistant/providers/snapcast/__init__.py +++ b/music_assistant/providers/snapcast/__init__.py @@ -13,7 +13,7 @@ from contextlib import suppress from typing import TYPE_CHECKING, Final, cast from bidict import bidict -from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueType from music_assistant_models.enums import ( ConfigEntryType, ContentType, @@ -114,7 +114,7 @@ async def get_config_entries( mass: MusicAssistant, # noqa: ARG001 instance_id: str | None = None, # noqa: ARG001 action: str | None = None, # noqa: ARG001 - values: dict[str, ConfigValueTypes] | None = None, # noqa: ARG001 + values: dict[str, ConfigValueType] | None = None, # noqa: ARG001 ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/sonos/__init__.py b/music_assistant/providers/sonos/__init__.py index 95c95a1d..0ccfa50c 100644 --- a/music_assistant/providers/sonos/__init__.py +++ b/music_assistant/providers/sonos/__init__.py @@ -17,7 +17,7 @@ from music_assistant.constants import VERBOSE_LOG_LEVEL from .provider import CONF_IPS, SonosPlayerProvider if TYPE_CHECKING: - from music_assistant_models.config_entries import ConfigValueTypes, ProviderConfig + from music_assistant_models.config_entries import ConfigValueType, ProviderConfig from music_assistant_models.provider import ProviderManifest from music_assistant import MusicAssistant @@ -41,7 +41,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/sonos_s1/__init__.py b/music_assistant/providers/sonos_s1/__init__.py index e5068323..23c0474b 100644 --- a/music_assistant/providers/sonos_s1/__init__.py +++ b/music_assistant/providers/sonos_s1/__init__.py @@ -15,7 +15,7 @@ from collections import OrderedDict from dataclasses import dataclass, field from typing import TYPE_CHECKING, cast -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType from music_assistant_models.enums import ( ConfigEntryType, PlayerFeature, @@ -89,7 +89,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/soundcloud/__init__.py b/music_assistant/providers/soundcloud/__init__.py index 88c43496..e0363ed9 100644 --- a/music_assistant/providers/soundcloud/__init__.py +++ b/music_assistant/providers/soundcloud/__init__.py @@ -5,7 +5,7 @@ from __future__ import annotations import time from typing import TYPE_CHECKING, Any -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType from music_assistant_models.enums import ( ConfigEntryType, ContentType, @@ -69,7 +69,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/spotify/__init__.py b/music_assistant/providers/spotify/__init__.py index c3112413..804ac419 100644 --- a/music_assistant/providers/spotify/__init__.py +++ b/music_assistant/providers/spotify/__init__.py @@ -10,7 +10,7 @@ import time from typing import TYPE_CHECKING, Any, cast from urllib.parse import urlencode -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType from music_assistant_models.enums import ( AlbumType, ConfigEntryType, @@ -125,7 +125,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/spotify_connect/__init__.py b/music_assistant/providers/spotify_connect/__init__.py index 4b404a96..55a6233f 100644 --- a/music_assistant/providers/spotify_connect/__init__.py +++ b/music_assistant/providers/spotify_connect/__init__.py @@ -35,7 +35,7 @@ from music_assistant.providers.spotify.helpers import get_librespot_binary if TYPE_CHECKING: from aiohttp.web import Request - from music_assistant_models.config_entries import ConfigValueTypes, ProviderConfig + from music_assistant_models.config_entries import ConfigValueType, ProviderConfig from music_assistant_models.event import MassEvent from music_assistant_models.provider import ProviderManifest @@ -60,7 +60,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, # noqa: ARG001 action: str | None = None, # noqa: ARG001 - values: dict[str, ConfigValueTypes] | None = None, # noqa: ARG001 + values: dict[str, ConfigValueType] | None = None, # noqa: ARG001 ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/test/__init__.py b/music_assistant/providers/test/__init__.py index e3d3d2bf..d645ad52 100644 --- a/music_assistant/providers/test/__init__.py +++ b/music_assistant/providers/test/__init__.py @@ -35,7 +35,7 @@ from music_assistant.constants import MASS_LOGO, SILENCE_FILE_LONG, VARIOUS_ARTI from music_assistant.models.music_provider import MusicProvider if TYPE_CHECKING: - from music_assistant_models.config_entries import ConfigValueTypes, ProviderConfig + from music_assistant_models.config_entries import ConfigValueType, ProviderConfig from music_assistant_models.provider import ProviderManifest from music_assistant.mass import MusicAssistant @@ -74,7 +74,7 @@ async def get_config_entries( mass: MusicAssistant, # noqa: ARG001 instance_id: str | None = None, # noqa: ARG001 action: str | None = None, # noqa: ARG001 - values: dict[str, ConfigValueTypes] | None = None, # noqa: ARG001 + values: dict[str, ConfigValueType] | None = None, # noqa: ARG001 ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/theaudiodb/__init__.py b/music_assistant/providers/theaudiodb/__init__.py index 7a548ee0..1485dc81 100644 --- a/music_assistant/providers/theaudiodb/__init__.py +++ b/music_assistant/providers/theaudiodb/__init__.py @@ -33,7 +33,7 @@ from music_assistant.helpers.throttle_retry import Throttler from music_assistant.models.metadata_provider import MetadataProvider if TYPE_CHECKING: - from music_assistant_models.config_entries import ConfigValueTypes, ProviderConfig + from music_assistant_models.config_entries import ConfigValueType, ProviderConfig from music_assistant_models.provider import ProviderManifest from music_assistant.mass import MusicAssistant @@ -95,7 +95,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/tidal/__init__.py b/music_assistant/providers/tidal/__init__.py index 14664551..61859920 100644 --- a/music_assistant/providers/tidal/__init__.py +++ b/music_assistant/providers/tidal/__init__.py @@ -11,7 +11,7 @@ from datetime import datetime, timedelta from enum import StrEnum from typing import TYPE_CHECKING, ParamSpec, TypeVar, cast -from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueType from music_assistant_models.enums import ( AlbumType, CacheCategory, @@ -165,7 +165,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, # noqa: ARG001 action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/tunein/__init__.py b/music_assistant/providers/tunein/__init__.py index b4595155..c03a2ddf 100644 --- a/music_assistant/providers/tunein/__init__.py +++ b/music_assistant/providers/tunein/__init__.py @@ -4,7 +4,7 @@ from __future__ import annotations from typing import TYPE_CHECKING -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType from music_assistant_models.enums import ( ConfigEntryType, ContentType, @@ -56,7 +56,7 @@ async def get_config_entries( mass: MusicAssistant, instance_id: str | None = None, action: str | None = None, - values: dict[str, ConfigValueTypes] | None = None, + values: dict[str, ConfigValueType] | None = None, ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/music_assistant/providers/ytmusic/__init__.py b/music_assistant/providers/ytmusic/__init__.py index 101b3d32..4e39a0cf 100644 --- a/music_assistant/providers/ytmusic/__init__.py +++ b/music_assistant/providers/ytmusic/__init__.py @@ -11,7 +11,7 @@ from urllib.parse import unquote import yt_dlp from duration_parser import parse as parse_str_duration -from music_assistant_models.config_entries import ConfigEntry, ConfigValueTypes +from music_assistant_models.config_entries import ConfigEntry, ConfigValueType from music_assistant_models.enums import ( AlbumType, ConfigEntryType, @@ -136,7 +136,7 @@ async def get_config_entries( mass: MusicAssistant, # noqa: ARG001 instance_id: str | None = None, # noqa: ARG001 action: str | None = None, # noqa: ARG001 - values: dict[str, ConfigValueTypes] | None = None, # noqa: ARG001 + values: dict[str, ConfigValueType] | None = None, # noqa: ARG001 ) -> tuple[ConfigEntry, ...]: """ Return Config entries to setup this provider. diff --git a/pyproject.toml b/pyproject.toml index a5cb57d9..f04b1043 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ dependencies = [ "mashumaro==3.15", "memory-tempfile==2.2.3", "music-assistant-frontend==2.11.5", - "music-assistant-models==1.1.28", + "music-assistant-models==1.1.29", "mutagen==1.47.0", "orjson==3.10.12", "pillow==11.1.0", diff --git a/requirements_all.txt b/requirements_all.txt index 642e0aef..dfea4fae 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -26,7 +26,7 @@ ifaddr==0.2.0 mashumaro==3.15 memory-tempfile==2.2.3 music-assistant-frontend==2.11.5 -music-assistant-models==1.1.28 +music-assistant-models==1.1.29 mutagen==1.47.0 orjson==3.10.12 pillow==11.1.0 -- 2.34.1