/
/
/
1"""Universal Group Player constants."""
2
3from __future__ import annotations
4
5from typing import Final
6
7from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption
8from music_assistant_models.enums import ConfigEntryType, ContentType, PlayerFeature
9from music_assistant_models.media_items import AudioFormat
10
11from music_assistant.helpers.ffmpeg import DEFAULT_MP3_BIT_RATE
12
13UGP_PREFIX: Final[str] = "ugp_"
14
15# Features the group has no hardware of its own for: the player manager fans these
16# commands out to the members. They are advertised only when a member can actually
17# carry them out, which is the same condition under which the group state for them
18# (group_volume / group_volume_muted) resolves to a value.
19EXTRA_FEATURES_FROM_MEMBERS: Final[set[PlayerFeature]] = {
20 PlayerFeature.VOLUME_SET,
21 PlayerFeature.VOLUME_MUTE,
22}
23
24# Grace period (seconds) before a universal group releases its members after
25# the active stream naturally ends (playback_state transitions to IDLE without
26# an explicit stop). Matches the SyncGroup grace window.
27IDLE_GRACE_SECONDS: Final[float] = 10.0
28
29# Static output format options for the UGP stream. UGP is a heterogeneous-protocol
30# compatibility group, not a hi-res delivery path; every member receives the same
31# encoded stream, so the format is a single deliberate choice instead of a per-member
32# negotiation.
33CONF_UGP_OUTPUT_FORMAT: Final[str] = "ugp_output_format"
34
35UGP_OUTPUT_MP3: Final[str] = "mp3"
36UGP_OUTPUT_FLAC_44100_16: Final[str] = "flac_44100_16"
37UGP_OUTPUT_FLAC_48000_24: Final[str] = "flac_48000_24"
38
39# Map each option to (audio format the server will encode/serve, URL extension).
40# The audio format also drives the internal PCM pivot used by UGPStream so the
41# multiplexer runs at exactly the rate the encoder needs.
42UGP_OUTPUT_FORMATS: Final[dict[str, tuple[AudioFormat, str]]] = {
43 UGP_OUTPUT_MP3: (
44 AudioFormat(
45 content_type=ContentType.MP3,
46 codec_type=ContentType.MP3,
47 sample_rate=44100,
48 bit_depth=16,
49 bit_rate=DEFAULT_MP3_BIT_RATE,
50 ),
51 "mp3",
52 ),
53 UGP_OUTPUT_FLAC_44100_16: (
54 AudioFormat(content_type=ContentType.FLAC, sample_rate=44100, bit_depth=16),
55 "flac",
56 ),
57 UGP_OUTPUT_FLAC_48000_24: (
58 AudioFormat(content_type=ContentType.FLAC, sample_rate=48000, bit_depth=24),
59 "flac",
60 ),
61}
62
63
64CONFIG_ENTRY_UGP_NOTE = ConfigEntry(
65 key="ugp_note",
66 type=ConfigEntryType.ALERT,
67 required=False,
68)
69
70CONF_ENTRY_UGP_OUTPUT_FORMAT = ConfigEntry(
71 key=CONF_UGP_OUTPUT_FORMAT,
72 type=ConfigEntryType.STRING,
73 options=[
74 ConfigValueOption(UGP_OUTPUT_MP3),
75 ConfigValueOption(UGP_OUTPUT_FLAC_44100_16),
76 ConfigValueOption(UGP_OUTPUT_FLAC_48000_24),
77 ],
78 default_value=UGP_OUTPUT_MP3,
79 category="generic",
80 requires_reload=True,
81)
82
83
84def resolve_ugp_output_format(option: str) -> tuple[AudioFormat, str]:
85 """Return the (output AudioFormat, URL extension) for a configured UGP option."""
86 return UGP_OUTPUT_FORMATS.get(option, UGP_OUTPUT_FORMATS[UGP_OUTPUT_MP3])
87