music-assistant-server

6.7 KBPY
__init__.py
6.7 KB193 lines • python
1"""Snapcast Player provider for Music Assistant."""
2
3import re
4
5from music_assistant_models.config_entries import (
6    ConfigEntry,
7    ConfigValueOption,
8    ConfigValueType,
9    ProviderConfig,
10)
11from music_assistant_models.enums import ConfigEntryType, ProviderFeature
12from music_assistant_models.errors import SetupFailedError
13from music_assistant_models.provider import ProviderManifest
14
15from music_assistant.helpers.process import check_output
16from music_assistant.mass import MusicAssistant
17from music_assistant.models import ProviderInstanceType
18from music_assistant.providers.snapcast.constants import (
19    CONF_CATEGORY_BUILT_IN,
20    CONF_HELP_LINK,
21    CONF_SERVER_BUFFER_SIZE,
22    CONF_SERVER_CHUNK_MS,
23    CONF_SERVER_CONTROL_PORT,
24    CONF_SERVER_HOST,
25    CONF_SERVER_INITIAL_VOLUME,
26    CONF_SERVER_SEND_AUDIO_TO_MUTED,
27    CONF_SERVER_TRANSPORT_CODEC,
28    CONF_STREAM_IDLE_THRESHOLD,
29    CONF_USE_EXTERNAL_SERVER,
30    DEFAULT_SNAPSERVER_IP,
31    DEFAULT_SNAPSERVER_PORT,
32    DEFAULT_SNAPSTREAM_IDLE_THRESHOLD,
33)
34from music_assistant.providers.snapcast.provider import SnapCastProvider
35
36SUPPORTED_FEATURES = {
37    ProviderFeature.SYNC_PLAYERS,
38    ProviderFeature.REMOVE_PLAYER,
39}
40
41
42async def setup(
43    mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
44) -> ProviderInstanceType:
45    """Initialize provider(instance) with given configuration."""
46    return SnapCastProvider(mass, manifest, config, SUPPORTED_FEATURES)
47
48
49async def get_config_entries(
50    mass: MusicAssistant,  # noqa: ARG001
51    instance_id: str | None = None,  # noqa: ARG001
52    action: str | None = None,  # noqa: ARG001
53    values: dict[str, ConfigValueType] | None = None,  # noqa: ARG001
54) -> tuple[ConfigEntry, ...]:
55    """
56    Return Config entries to setup this provider.
57
58    :param instance_id: id of an existing provider instance (None if new instance setup).
59    :param action: [optional] action key called from config entries UI.
60    :param values: the (intermediate) raw values for config entries sent with the action.
61    """
62    returncode, output = await check_output("snapserver", "-v")
63    snapserver_version = -1
64    if returncode == 0:
65        # Parse version from output, handling potential noise from library warnings
66        # Expected format: "0.27.0" or similar version string
67        output_str = output.decode()
68        if version_match := re.search(r"(\d+)\.(\d+)\.(\d+)", output_str):
69            snapserver_version = int(version_match.group(2))
70    local_snapserver_present = snapserver_version >= 27 and snapserver_version != 30
71    if returncode == 0 and not local_snapserver_present:
72        raise SetupFailedError(
73            f"Invalid snapserver version. Expected >= 27 and != 30, got {snapserver_version}"
74        )
75
76    return (
77        ConfigEntry(
78            key=CONF_SERVER_BUFFER_SIZE,
79            type=ConfigEntryType.INTEGER,
80            range=(200, 6000),
81            default_value=1000,
82            label="Snapserver buffer size",
83            required=False,
84            category=CONF_CATEGORY_BUILT_IN,
85            hidden=not local_snapserver_present,
86            depends_on=CONF_USE_EXTERNAL_SERVER,
87            depends_on_value_not=True,
88            help_link=CONF_HELP_LINK,
89        ),
90        ConfigEntry(
91            key=CONF_SERVER_CHUNK_MS,
92            type=ConfigEntryType.INTEGER,
93            range=(10, 100),
94            default_value=26,
95            label="Snapserver chunk size",
96            required=False,
97            category=CONF_CATEGORY_BUILT_IN,
98            hidden=not local_snapserver_present,
99            depends_on=CONF_USE_EXTERNAL_SERVER,
100            depends_on_value_not=True,
101            help_link=CONF_HELP_LINK,
102        ),
103        ConfigEntry(
104            key=CONF_SERVER_INITIAL_VOLUME,
105            type=ConfigEntryType.INTEGER,
106            range=(0, 100),
107            default_value=25,
108            label="Snapserver initial volume",
109            required=False,
110            category=CONF_CATEGORY_BUILT_IN,
111            hidden=not local_snapserver_present,
112            depends_on=CONF_USE_EXTERNAL_SERVER,
113            depends_on_value_not=True,
114            help_link=CONF_HELP_LINK,
115        ),
116        ConfigEntry(
117            key=CONF_SERVER_SEND_AUDIO_TO_MUTED,
118            type=ConfigEntryType.BOOLEAN,
119            default_value=False,
120            label="Send audio to muted clients",
121            required=False,
122            category=CONF_CATEGORY_BUILT_IN,
123            hidden=not local_snapserver_present,
124            depends_on=CONF_USE_EXTERNAL_SERVER,
125            depends_on_value_not=True,
126            help_link=CONF_HELP_LINK,
127        ),
128        ConfigEntry(
129            key=CONF_SERVER_TRANSPORT_CODEC,
130            type=ConfigEntryType.STRING,
131            options=[
132                ConfigValueOption(
133                    title="FLAC",
134                    value="flac",
135                ),
136                ConfigValueOption(
137                    title="OGG",
138                    value="ogg",
139                ),
140                ConfigValueOption(
141                    title="OPUS",
142                    value="opus",
143                ),
144                ConfigValueOption(
145                    title="PCM",
146                    value="pcm",
147                ),
148            ],
149            default_value="flac",
150            label="Snapserver default transport codec",
151            required=False,
152            category=CONF_CATEGORY_BUILT_IN,
153            hidden=not local_snapserver_present,
154            depends_on=CONF_USE_EXTERNAL_SERVER,
155            depends_on_value_not=True,
156            help_link=CONF_HELP_LINK,
157        ),
158        ConfigEntry(
159            key=CONF_USE_EXTERNAL_SERVER,
160            type=ConfigEntryType.BOOLEAN,
161            default_value=not local_snapserver_present,
162            label="Use existing Snapserver",
163            required=False,
164            advanced=local_snapserver_present,
165        ),
166        ConfigEntry(
167            key=CONF_SERVER_HOST,
168            type=ConfigEntryType.STRING,
169            default_value=DEFAULT_SNAPSERVER_IP,
170            label="Snapcast server ip",
171            required=False,
172            depends_on=CONF_USE_EXTERNAL_SERVER,
173            advanced=local_snapserver_present,
174        ),
175        ConfigEntry(
176            key=CONF_SERVER_CONTROL_PORT,
177            type=ConfigEntryType.INTEGER,
178            default_value=DEFAULT_SNAPSERVER_PORT,
179            label="Snapcast control port",
180            required=False,
181            depends_on=CONF_USE_EXTERNAL_SERVER,
182            advanced=local_snapserver_present,
183        ),
184        ConfigEntry(
185            key=CONF_STREAM_IDLE_THRESHOLD,
186            type=ConfigEntryType.INTEGER,
187            default_value=DEFAULT_SNAPSTREAM_IDLE_THRESHOLD,
188            label="Snapcast idle threshold stream parameter",
189            required=True,
190            advanced=local_snapserver_present,
191        ),
192    )
193