Fix: Final round of fixes for multi value serializing mess
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Tue, 18 Feb 2025 18:52:26 +0000 (19:52 +0100)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Tue, 18 Feb 2025 18:52:26 +0000 (19:52 +0100)
52 files changed:
music_assistant/constants.py
music_assistant/controllers/cache.py
music_assistant/controllers/config.py
music_assistant/controllers/metadata.py
music_assistant/controllers/music.py
music_assistant/controllers/player_queues.py
music_assistant/controllers/streams.py
music_assistant/controllers/webserver.py
music_assistant/models/__init__.py
music_assistant/models/core_controller.py
music_assistant/providers/_template_music_provider/__init__.py
music_assistant/providers/_template_player_provider/__init__.py
music_assistant/providers/_template_plugin_provider/__init__.py
music_assistant/providers/airplay/__init__.py
music_assistant/providers/apple_music/__init__.py
music_assistant/providers/audible/__init__.py
music_assistant/providers/audiobookshelf/__init__.py
music_assistant/providers/bluesound/__init__.py
music_assistant/providers/builtin/__init__.py
music_assistant/providers/chromecast/__init__.py
music_assistant/providers/deezer/__init__.py
music_assistant/providers/dlna/__init__.py
music_assistant/providers/fanarttv/__init__.py
music_assistant/providers/filesystem_local/__init__.py
music_assistant/providers/filesystem_smb/__init__.py
music_assistant/providers/fully_kiosk/__init__.py
music_assistant/providers/hass/__init__.py
music_assistant/providers/hass_players/__init__.py
music_assistant/providers/ibroadcast/__init__.py
music_assistant/providers/jellyfin/__init__.py
music_assistant/providers/musicbrainz/__init__.py
music_assistant/providers/opensubsonic/__init__.py
music_assistant/providers/player_group/__init__.py
music_assistant/providers/plex/__init__.py
music_assistant/providers/podcastfeed/__init__.py
music_assistant/providers/qobuz/__init__.py
music_assistant/providers/radiobrowser/__init__.py
music_assistant/providers/siriusxm/__init__.py
music_assistant/providers/slimproto/__init__.py
music_assistant/providers/snapcast/__init__.py
music_assistant/providers/sonos/__init__.py
music_assistant/providers/sonos_s1/__init__.py
music_assistant/providers/soundcloud/__init__.py
music_assistant/providers/spotify/__init__.py
music_assistant/providers/spotify_connect/__init__.py
music_assistant/providers/test/__init__.py
music_assistant/providers/theaudiodb/__init__.py
music_assistant/providers/tidal/__init__.py
music_assistant/providers/tunein/__init__.py
music_assistant/providers/ytmusic/__init__.py
pyproject.toml
requirements_all.txt

index 1132e07f1fe72e83ca69c80d928e0c2db519a060..c8dc2b2f0c6d5d43d7a4a481b634e4787c7b082f 100644 (file)
@@ -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
index 59aeccba51d620624296be213a5d8f7e3929d1df..8ca5116fea4091f79b15db5b8387582688f6e7b0 100644 (file)
@@ -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:
index 88927b28bdd4a3dc42378103f03e239da07ec828..6a0f6a521334b5c28794b516cb23675cd938c738 100644 (file)
@@ -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).
index cd0a20a016ff2f7084fc7329abefc9dc131f7310..16756306d5cb26228cce45d407409c5c3daa1e4f 100644 (file)
@@ -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 (
index edc8d9c7c8dff8d074fc383d4a3fac3cc84540c6..d8950aaef40462e88f10039920fdbb21f0b16aa7 100644 (file)
@@ -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..."
             )
index 93459cfb87bde36ad822355991f99fdcd110785e..f281411ed6e429eb36f7382d5e2659242b2eb4bf 100644 (file)
@@ -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]
index 2efb1d1664edb9dd78437ef0641a553b664dd1a8..1a039a319bbfa1c7e30390514857988d377ff60b 100644 (file)
@@ -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:
index 44c32a12e1cb436fe6e1ee16a7a0713d2d9a5035..67d24efc68c235ca64e473304c3a8c5193fae755 100644 (file)
@@ -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()
index d9e8847296a12dc41758d925fee3f7affd3f40d3..ce2bab18090b6a236e8065e48d11efb3548bc4ec 100644 (file)
@@ -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.
index f73ed6a5786dde86c09fa5735c18254679b03b48..7f2532c32315b418e892308189ae9118ac70e251 100644 (file)
@@ -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 ()
index 3ecf27c1a5bf2177a0833d0ef925a1590997edab..b960057509957dde914ad8eedd780051296f8c19 100644 (file)
@@ -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.
 
index 8bb1350c3acec73800cbb03c5ea9076ee1eff259..33ff6dea531d61c9032f12312893787049fe5012 100644 (file)
@@ -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 ()
index 0ca1a61363308a2b319fe79e58b53009b2401687..fbed240056b8c48b05ad90b7121f694ff49997be 100644 (file)
@@ -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 ()
index 67c2a0095e87de81b289e719f898ec67f38ee8b0..35463ecaa8269cdb13eef2aab938909de821b710 100644 (file)
@@ -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.
index 60d175e2a9397c20af3b296240027977d6560455..bfee2038c8615614142d59827f34d16ffb745a22 100644 (file)
@@ -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.
index 167f205083fe736fdf36e0a0ee8817e27e47d917..0f348d97b33371843a225cb1dc62d62579e352ed 100644 (file)
@@ -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.
index f47afa54d84d845eb0c80222e8dbd345224a6eae..8131bedc72f9d8a20f749bc356c1cfb371b49b88 100644 (file)
@@ -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.
index 32c488edfaf5862e1b26eeba1efa3b0b8d83b367..08f03eb19b4092e0ccfb8baba3dcda83cc9c109d 100644 (file)
@@ -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
index 047b4258b1b4151c592c6b2fe4a7d718cd171486..9dacc398e8fb9ccab0c253508297fa1c26696a6b 100644 (file)
@@ -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.
index 586b2856a7f10f4242f137b37d3757be5c3d4ff2..f6957c2925c7f628321f31a6238657bad14961a9 100644 (file)
@@ -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.
index 81c9c8524634d403ff123b37ce26494b8692b859..a7a5ae3ea578e92ab673c164ad859e90e9b87628 100644 (file)
@@ -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
index d8b6322c647775b79f1bc2e42fcb1bbed2f1e06f..22836391745a130fa3718b0e80e0449260f5d3b0 100644 (file)
@@ -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.
index f641cc50096e8846d2c7488a5f3fb3a6a8f21352..89a94c5bd413ffb5c42844c654e8d0163a454b1f 100644 (file)
@@ -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.
index 4a14772ca2df8ba4ff159adbe18e3f93025b8c00..797a33d596f69616b4cf24183477c27078f4406b 100644 (file)
@@ -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.
index de4ee2363efaa882c5dcf31aa7f83fa6d7fe95c3..72b2213e644b7bd455500baefffe128b6dbba247 100644 (file)
@@ -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.
index 630a84557db92550f28e42f0b011a187cf76ca8c..b096db65cec4a513d3f654538d9dfd800fdb9505 100644 (file)
@@ -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.
index ace6ee7f7c27d5c737ce6487e70b497057323196..245b6419572ce62021a58233721d2660a690e78a 100644 (file)
@@ -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,
index 93a24a02f0ad6c53534e149531f42722376e5deb..67fe2a2beeb621236660e16d52c2d04ca2d66fea 100644 (file)
@@ -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,
index 397704889fd01ea5dc8df67c33b8bc1e763d6396..2a3a11bc8fbeb60db8087673d43b9c4f5f0e2d72 100644 (file)
@@ -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.
index 7480a7b26ab873481d89cc747d33068d9af7750f..9e698dcf8702c713398a524da5f5f97c96d3f75b 100644 (file)
@@ -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.
index 61daeee9fa583ea04dfc530cc4b2366699f18b27..a7522994c4cd2b99a59d9315ad83110ff7b526c7 100644 (file)
@@ -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.
index eb070f41ddb29b0b27cd14eb056399cf1c5dc577..2505c386a9fb91f5c583bf79462d1a6ebfa2837b 100644 (file)
@@ -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 (
index 259b193d29985048d0b875e892f1abd0b723a07b..792fba029c692158d36d49d08be4fc2d1143b490 100644 (file)
@@ -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.
index 62a521462bb581a2c40894a6889b62c17ceccd56..81f5a22d9f54d4cb3c6b9eabb9cdd47ded5262d2 100644 (file)
@@ -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.
index bc69bfa733ad2efad4069846ebf2138420ababff..9ced926af09aed1da4895f876ccf38958ff36eae 100644 (file)
@@ -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.
index 5b7f774fdc428a9654491284d5ff93e4b5f43f12..0549e45412b22d191c4da96b1931bcb782128130 100644 (file)
@@ -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.
index 3f6178131e6588cd439fbf87ab2237c15dc13747..a70c094428a7e5a27f59c1d25f5f85955ba2e554 100644 (file)
@@ -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,
index cef972086a30dd2dce98786cb409ff72b651a013..7be2408816e5b50f5678fdc2411ca5789be02071 100644 (file)
@@ -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.
index 153503e07e3f63c2cefc5fdc7f59da93dce00b44..02f800b50df6a0c528635eaf6e685f98eef27edf 100644 (file)
@@ -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.
index f2d8d5b616d6ab54499f71c033dbb6cac30cfcfa..1768f6578ba49f07bd65fd287f30cfb868e47d99 100644 (file)
@@ -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.
index 95c95a1db4380f5d8485daa6be6f12c94cbef386..0ccfa50c1d0be7850750d952dc0c238eb6fff6a2 100644 (file)
@@ -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.
index e50683234e7eec8a66e797e46275720a2b046617..23c0474b4c66e9819d916c28858bb53b7f99f9a8 100644 (file)
@@ -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.
index 88c43496db7e95aa683581e7ac6c272764da7faa..e0363ed9918608f0954e6db3e17eadafc3c665fc 100644 (file)
@@ -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.
index c3112413d941b97abb3aadeaf7d905ca89ff790c..804ac419651655b0db3584c21004dff0f3d4b90d 100644 (file)
@@ -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.
index 4b404a9632411b5b4af00f8d4acd2a0b9ff66e46..55a6233fc2fb3232135cf28752a380570465e6b6 100644 (file)
@@ -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.
index e3d3d2bf5b4ce6309eb93a2c10ed9bc65031a4ac..d645ad526476db4c893042566ecd77f70ab49a8e 100644 (file)
@@ -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.
index 7a548ee08ede4cc5cc657efefedd0396bf1a0bd2..1485dc81bb2276a6aac0abcfe907dd11616b830b 100644 (file)
@@ -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.
index 146645515ab333de65adb1a7ef9bb23b56ff5eb5..61859920b2071dac1cabdb95530e01cf59fe3b9c 100644 (file)
@@ -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.
index b459515537959bfd32136303066bd6a8776c55c8..c03a2ddfc33e4e6e5baa623cdb1f2744a7ad1adf 100644 (file)
@@ -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.
index 101b3d3297e3ce20bbee8e90459a046323c8ed81..4e39a0cf352002f297456e3519cf3243dc97aea1 100644 (file)
@@ -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.
index a5cb57d91c9962e3a13f06b6ceeff17313af6cd5..f04b1043094c140bed3c9dc707643d8023171e01 100644 (file)
@@ -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",
index 642e0aeffbb65918d5ab4cd7cb2d5ed6724cd94d..dfea4faed3a87464e5b7a1e037113883f6eae449 100644 (file)
@@ -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