CONF_CROSSFADE: Final[str] = "crossfade"
CONF_GROUP_MEMBERS: Final[str] = "group_members"
CONF_HIDE_PLAYER: Final[str] = "hide_player"
-CONF_ENFORCE_MP3: Final[str] = "enforce_mp3"
CONF_SYNC_ADJUST: Final[str] = "sync_adjust"
CONF_TTS_PRE_ANNOUNCE: Final[str] = "tts_pre_announce"
CONF_ANNOUNCE_VOLUME_STRATEGY: Final[str] = "announce_volume_strategy"
CONF_POWER_CONTROL: Final[str] = "power_control"
CONF_VOLUME_CONTROL: Final[str] = "volume_control"
CONF_MUTE_CONTROL: Final[str] = "mute_control"
+CONF_OUTPUT_CODEC: Final[str] = "output_codec"
# config default values
DEFAULT_HOST: Final[str] = "0.0.0.0"
default_value=False,
)
-CONF_ENTRY_ENFORCE_MP3 = ConfigEntry(
- key=CONF_ENFORCE_MP3,
- type=ConfigEntryType.BOOLEAN,
- label="Enforce (lossy) mp3 stream",
- default_value=False,
- description="By default, Music Assistant sends lossless, high quality audio "
- "to all players. Some players can not deal with that and require the stream to be packed "
- "into a lossy mp3 codec. \n\n "
- "Only enable when needed. Saves some bandwidth at the cost of audio quality.",
- category="audio",
+CONF_ENTRY_OUTPUT_CODEC = ConfigEntry(
+ key=CONF_OUTPUT_CODEC,
+ type=ConfigEntryType.STRING,
+ label="Output codec to use for streaming audio to the player",
+ default_value="flac",
+ options=[
+ ConfigValueOption("FLAC (lossless, compressed)", "flac"),
+ ConfigValueOption("MP3 (lossy)", "mp3"),
+ ConfigValueOption("AAC (lossy)", "aac"),
+ ConfigValueOption("WAV (lossless, uncompressed)", "wav"),
+ ],
+ description="Select the codec to use for streaming audio to this player. \n"
+ "By default, Music Assistant sends lossless, high quality audio to all players and prefers "
+ "the FLAC codec because it offers some compression while still remaining lossless \n\n"
+ "Some players however do not support FLAC and require the stream to be packed "
+ "into e.g. a lossy mp3 codec or you like to save some network bandwidth. \n\n "
+ "Choosing a lossy codec saves some bandwidth at the cost of audio quality.",
+ category="advanced",
)
-CONF_ENTRY_ENFORCE_MP3_DEFAULT_ENABLED = ConfigEntry.from_dict(
- {**CONF_ENTRY_ENFORCE_MP3.to_dict(), "default_value": True}
+CONF_ENTRY_OUTPUT_CODEC_DEFAULT_MP3 = ConfigEntry.from_dict(
+ {**CONF_ENTRY_OUTPUT_CODEC.to_dict(), "default_value": "mp3"}
)
-CONF_ENTRY_ENFORCE_MP3_HIDDEN = ConfigEntry.from_dict(
- {**CONF_ENTRY_ENFORCE_MP3.to_dict(), "default_value": True, "hidden": True}
+CONF_ENTRY_OUTPUT_CODEC_ENFORCE_MP3 = ConfigEntry.from_dict(
+ {**CONF_ENTRY_OUTPUT_CODEC.to_dict(), "default_value": "mp3", "hidden": True}
)
+
CONF_ENTRY_SYNC_ADJUST = ConfigEntry(
key=CONF_SYNC_ADJUST,
type=ConfigEntryType.INTEGER,
CONF_ENTRY_TTS_PRE_ANNOUNCE,
CONF_ENTRY_SAMPLE_RATES,
CONF_ENTRY_HTTP_PROFILE_FORCED_2,
+ CONF_ENTRY_OUTPUT_CODEC,
)
async def play_media():
await self.mass.players.play_media(
player_id=queue_id,
- media=self.player_media_from_queue_item(queue_item, queue.flow_mode),
+ media=await self.player_media_from_queue_item(queue_item, queue.flow_mode),
)
await asyncio.sleep(2)
setattr(queue, "transitioning", False) # noqa: B010
return index
return None
- def player_media_from_queue_item(self, queue_item: QueueItem, flow_mode: bool) -> PlayerMedia:
+ async def player_media_from_queue_item(
+ self, queue_item: QueueItem, flow_mode: bool
+ ) -> PlayerMedia:
"""Parse PlayerMedia from QueueItem."""
media = PlayerMedia(
- uri=self.mass.streams.resolve_stream_url(queue_item, flow_mode=flow_mode),
+ uri=await self.mass.streams.resolve_stream_url(queue_item, flow_mode=flow_mode),
media_type=MediaType.FLOW_STREAM if flow_mode else queue_item.media_type,
title="Music Assistant" if flow_mode else queue_item.name,
image_url=MASS_LOGO_ONLINE,
return
await self.mass.players.enqueue_next_media(
player_id=queue_id,
- media=self.player_media_from_queue_item(next_item, False),
+ media=await self.player_media_from_queue_item(next_item, False),
)
self.logger.debug(
"Enqueued next track %s on queue %s",
from __future__ import annotations
import os
-import time
import urllib.parse
from collections.abc import AsyncGenerator
from typing import TYPE_CHECKING
CONF_ENTRY_ENABLE_ICY_METADATA,
CONF_HTTP_PROFILE,
CONF_OUTPUT_CHANNELS,
+ CONF_OUTPUT_CODEC,
CONF_PUBLISH_IP,
CONF_SAMPLE_RATES,
CONF_VOLUME_NORMALIZATION_FIXED_GAIN_RADIO,
"""Cleanup on exit."""
await self._server.close()
- def resolve_stream_url(
+ async def resolve_stream_url(
self,
queue_item: QueueItem,
flow_mode: bool = False,
- output_codec: ContentType = ContentType.FLAC,
+ player_id: str | None = None,
) -> str:
"""Resolve the stream URL for the given QueueItem."""
+ if not player_id:
+ player_id = queue_item.queue_id
+ output_codec = ContentType.try_parse(
+ await self.mass.config.get_player_config_value(player_id, CONF_OUTPUT_CODEC)
+ )
fmt = output_codec.value
# handle raw pcm without exact format specifiers
if output_codec.is_pcm() and ";" not in fmt:
fmt += f";codec=pcm;rate={44100};bitrate={16};channels={2}"
- query_params = {}
base_path = "flow" if flow_mode else "single"
- url = f"{self._server.base_url}/{base_path}/{queue_item.queue_id}/{queue_item.queue_item_id}.{fmt}" # noqa: E501
- # we add a timestamp as basic checksum
- # most importantly this is to invalidate any caches
- # but also to handle edge cases such as single track repeat
- query_params["ts"] = str(int(time.time()))
- url += "?" + urllib.parse.urlencode(query_params)
- return url
+ return f"{self._server.base_url}/{base_path}/{queue_item.queue_id}/{queue_item.queue_item_id}.{fmt}" # noqa: E501
async def serve_queue_item_stream(self, request: web.Request) -> web.Response:
"""Stream single queueitem audio to a player."""
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
- output_sample_rate, output_bit_depth, output_channels = parse_pcm_info(
- output_format_str
- )
- if content_type == ContentType.PCM:
- # resolve generic pcm type
- content_type = ContentType.from_bit_depth(output_bit_depth)
+ if default_sample_rate in supported_sample_rates:
+ output_sample_rate = default_sample_rate
else:
- if default_sample_rate in supported_sample_rates:
- output_sample_rate = default_sample_rate
- else:
- output_sample_rate = max(supported_sample_rates)
- output_bit_depth = min(default_bit_depth, player_max_bit_depth)
- output_channels_str = self.mass.config.get_raw_player_config_value(
- player.player_id, CONF_OUTPUT_CHANNELS, "stereo"
- )
- output_channels = 1 if output_channels_str != "stereo" else 2
+ output_sample_rate = max(supported_sample_rates)
+ output_bit_depth = min(default_bit_depth, player_max_bit_depth)
+ output_channels_str = self.mass.config.get_raw_player_config_value(
+ player.player_id, CONF_OUTPUT_CHANNELS, "stereo"
+ )
+ output_channels = 1 if output_channels_str != "stereo" else 2
if not content_type.is_lossless():
output_bit_depth = 16
output_sample_rate = min(48000, output_sample_rate)
+ if output_format_str == "pcm":
+ content_type = ContentType.from_bit_depth(output_bit_depth)
return AudioFormat(
content_type=content_type,
sample_rate=output_sample_rate,
bit_depth=output_bit_depth,
channels=output_channels,
- output_format_str=output_format_str,
)
async def _select_flow_format(
extra_input_args: list[str] | None = None,
audio_output: str | int = "-",
collect_log_history: bool = False,
- loglevel: str = "error",
+ loglevel: str = "info",
) -> None:
"""Initialize AsyncProcess."""
ffmpeg_args = get_ffmpeg_args(
else:
clean_args.append(arg)
args_str = " ".join(clean_args)
- self.logger.log(VERBOSE_LOG_LEVEL, "started with args: %s", args_str)
+ self.logger.debug("started with args: %s", args_str)
self._logger_task = asyncio.create_task(self._log_reader_task())
if isinstance(self.audio_input, AsyncGenerator):
self._stdin_task = asyncio.create_task(self._feed_stdin())
cancelled = False
try:
start = time.time()
- self.logger.log(VERBOSE_LOG_LEVEL, "Start reading audio data from source...")
+ self.logger.debug("Start reading audio data from source...")
# use TimedAsyncGenerator to catch we're stuck waiting on data forever
# don't set this timeout too low because in some cases it can indeed take a while
# for data to arrive (e.g. when there is X amount of seconds in the buffer)
if self.closed:
return
await self.write(chunk)
- self.logger.log(
- VERBOSE_LOG_LEVEL, "Audio data source exhausted in %.2fs", time.time() - start
- )
+ self.logger.debug("Audio data source exhausted in %.2fs", time.time() - start)
generator_exhausted = True
except Exception as err:
cancelled = isinstance(err, asyncio.CancelledError)
input_args += ["-i", input_path]
# collect output args
- output_args = []
+ output_args = [
+ "-ac",
+ str(output_format.channels),
+ "-channel_layout",
+ "mono" if output_format.channels == 1 else "stereo",
+ ]
if output_path.upper() == "NULL":
# devnull stream
- output_args = ["-f", "null", "-"]
- elif output_format.content_type == ContentType.UNKNOWN:
- raise RuntimeError("Invalid output format specified")
+ output_path = "-"
+ output_args = ["-f", "null"]
elif output_format.content_type == ContentType.AAC:
- output_args = ["-f", "adts", "-c:a", "aac", "-b:a", "256k", output_path]
+ output_args = ["-f", "adts", "-c:a", "aac", "-b:a", "256k"]
elif output_format.content_type == ContentType.MP3:
- output_args = ["-f", "mp3", "-b:a", "320k", output_path]
- else:
- if output_format.content_type.is_pcm():
- output_args += ["-acodec", output_format.content_type.name.lower()]
- # use explicit format identifier for all other
- output_args += [
+ output_args = ["-f", "mp3", "-b:a", "320k"]
+ elif output_format.content_type == ContentType.WAV:
+ pcm_format = ContentType.from_bit_depth(output_format.bit_depth)
+ output_args = [
+ # "-ar",
+ # str(output_format.sample_rate),
+ "-acodec",
+ pcm_format.name.lower(),
"-f",
- output_format.content_type.value,
+ "wav",
+ ]
+ elif output_format.content_type == ContentType.FLAC:
+ # use level 0 compression for fastest encoding
+ sample_fmt = "s32" if output_format.bit_depth > 16 else "s16"
+ output_args += ["-sample_fmt", sample_fmt, "-f", "flac", "-compression_level", "0"]
+ elif output_format.content_type.is_pcm():
+ # use explicit format identifier for pcm formats
+ output_args += [
"-ar",
str(output_format.sample_rate),
- "-ac",
- str(output_format.channels),
+ "-acodec",
+ output_format.content_type.name.lower(),
+ "-f",
+ output_format.content_type.value,
]
- if not output_format.content_type.is_pcm() and output_format.content_type.is_lossless():
- if output_format.bit_depth == 24:
- output_args += ["-sample_fmt", "s32"]
- elif output_format.bit_depth == 16:
- output_args += ["-sample_fmt", "s16"]
- if output_format.output_format_str == "flac":
- # use level 0 compression for fastest encoding
- output_args += ["-compression_level", "0"]
- output_args += [output_path]
+ else:
+ raise RuntimeError("Invalid/unsupported output format specified")
+
+ # append (final) output path at the end of the args
+ output_args.append(output_path)
# edge case: source file is not stereo - downmix to stereo
if input_format.channels > 2 and output_format.channels == 2:
from music_assistant.constants import (
CONF_ENTRY_CROSSFADE,
CONF_ENTRY_ENABLE_ICY_METADATA,
- CONF_ENTRY_ENFORCE_MP3,
CONF_ENTRY_FLOW_MODE_ENFORCED,
CONF_ENTRY_HTTP_PROFILE_FORCED_2,
+ CONF_ENTRY_OUTPUT_CODEC,
VERBOSE_LOG_LEVEL,
)
from music_assistant.helpers.util import (
*base_entries,
CONF_ENTRY_HTTP_PROFILE_FORCED_2,
CONF_ENTRY_CROSSFADE,
- CONF_ENTRY_ENFORCE_MP3,
+ CONF_ENTRY_OUTPUT_CODEC,
CONF_ENTRY_FLOW_MODE_ENFORCED,
CONF_ENTRY_ENABLE_ICY_METADATA,
)
from music_assistant.constants import (
BASE_PLAYER_CONFIG_ENTRIES,
- CONF_ENFORCE_MP3,
CONF_ENTRY_CROSSFADE_DURATION,
CONF_ENTRY_CROSSFADE_FLOW_MODE_REQUIRED,
- CONF_ENTRY_ENFORCE_MP3,
+ CONF_ENTRY_OUTPUT_CODEC,
CONF_MUTE_CONTROL,
CONF_PLAYERS,
CONF_POWER_CONTROL,
PLAYER_CONFIG_ENTRIES = (
CONF_ENTRY_CROSSFADE_FLOW_MODE_REQUIRED,
CONF_ENTRY_CROSSFADE_DURATION,
- CONF_ENTRY_ENFORCE_MP3,
+ CONF_ENTRY_OUTPUT_CODEC,
)
# originally/officially cast supports 96k sample rate (even for groups)
) -> None:
"""Handle PLAY MEDIA on given player."""
castplayer = self.castplayers[player_id]
- if self.mass.config.get_raw_player_config_value(player_id, CONF_ENFORCE_MP3, False):
- media.uri = media.uri.replace(".flac", ".mp3")
queuedata = {
"type": "LOAD",
"media": self._create_cc_media_item(media),
async def enqueue_next_media(self, player_id: str, media: PlayerMedia) -> None:
"""Handle enqueuing of the next item on the player."""
castplayer = self.castplayers[player_id]
- if self.mass.config.get_raw_player_config_value(player_id, CONF_ENFORCE_MP3, False):
- media.uri = media.uri.replace(".flac", ".mp3")
next_item_id = None
status = castplayer.cc.media_controller.status
# lookup position of current track in cast queue
from music_assistant_models.player import DeviceInfo, Player, PlayerMedia
from music_assistant.constants import (
- CONF_ENFORCE_MP3,
CONF_ENTRY_CROSSFADE_DURATION,
CONF_ENTRY_CROSSFADE_FLOW_MODE_REQUIRED,
CONF_ENTRY_ENABLE_ICY_METADATA,
- CONF_ENTRY_ENFORCE_MP3,
CONF_ENTRY_FLOW_MODE_DEFAULT_ENABLED,
CONF_ENTRY_HTTP_PROFILE,
+ CONF_ENTRY_OUTPUT_CODEC,
CONF_PLAYERS,
VERBOSE_LOG_LEVEL,
create_sample_rates_config_entry,
PLAYER_CONFIG_ENTRIES = (
CONF_ENTRY_CROSSFADE_FLOW_MODE_REQUIRED,
CONF_ENTRY_CROSSFADE_DURATION,
- CONF_ENTRY_ENFORCE_MP3,
+ CONF_ENTRY_OUTPUT_CODEC,
CONF_ENTRY_HTTP_PROFILE,
CONF_ENTRY_ENABLE_ICY_METADATA,
# enable flow mode by default because
@catch_request_errors
async def play_media(self, player_id: str, media: PlayerMedia) -> None:
"""Handle PLAY MEDIA on given player."""
- if self.mass.config.get_raw_player_config_value(player_id, CONF_ENFORCE_MP3, False):
- media.uri = media.uri.replace(".flac", ".mp3")
dlna_player = self.dlnaplayers[player_id]
# always clear queue (by sending stop) first
if dlna_player.device.can_stop:
async def enqueue_next_media(self, player_id: str, media: PlayerMedia) -> None:
"""Handle enqueuing of the next queue item on the player."""
dlna_player = self.dlnaplayers[player_id]
- if self.mass.config.get_raw_player_config_value(player_id, CONF_ENFORCE_MP3, False):
- media.uri = media.uri.replace(".flac", ".mp3")
didl_metadata = create_didl_metadata(media)
title = media.title or media.uri
try:
from music_assistant_models.player import DeviceInfo, Player, PlayerMedia
from music_assistant.constants import (
- CONF_ENFORCE_MP3,
CONF_ENTRY_CROSSFADE,
CONF_ENTRY_CROSSFADE_DURATION,
- CONF_ENTRY_ENFORCE_MP3_DEFAULT_ENABLED,
CONF_ENTRY_FLOW_MODE_ENFORCED,
+ CONF_ENTRY_OUTPUT_CODEC_DEFAULT_MP3,
CONF_IP_ADDRESS,
CONF_PASSWORD,
CONF_PORT,
CONF_ENTRY_FLOW_MODE_ENFORCED,
CONF_ENTRY_CROSSFADE,
CONF_ENTRY_CROSSFADE_DURATION,
- CONF_ENTRY_ENFORCE_MP3_DEFAULT_ENABLED,
+ CONF_ENTRY_OUTPUT_CODEC_DEFAULT_MP3,
)
async def cmd_volume_set(self, player_id: str, volume_level: int) -> None:
"""Handle PLAY MEDIA on given player."""
if not (player := self.mass.players.get(player_id)):
return
- if self.mass.config.get_raw_player_config_value(player_id, CONF_ENFORCE_MP3, True):
- media.uri = media.uri.replace(".flac", ".mp3")
await self._fully.playSound(media.uri, AUDIOMANAGER_STREAM_MUSIC)
player.current_media = media
player.elapsed_time = 0
from music_assistant_models.player import DeviceInfo, Player, PlayerMedia
from music_assistant.constants import (
- CONF_ENFORCE_MP3,
CONF_ENTRY_CROSSFADE,
CONF_ENTRY_CROSSFADE_DURATION,
CONF_ENTRY_ENABLE_ICY_METADATA,
- CONF_ENTRY_ENFORCE_MP3_DEFAULT_ENABLED,
- CONF_ENTRY_ENFORCE_MP3_HIDDEN,
CONF_ENTRY_FLOW_MODE_ENFORCED,
CONF_ENTRY_HTTP_PROFILE,
CONF_ENTRY_HTTP_PROFILE_FORCED_2,
+ CONF_ENTRY_OUTPUT_CODEC_DEFAULT_MP3,
+ CONF_ENTRY_OUTPUT_CODEC_ENFORCE_MP3,
HIDDEN_ANNOUNCE_VOLUME_CONFIG_ENTRIES,
create_sample_rates_config_entry,
)
DEFAULT_PLAYER_CONFIG_ENTRIES = (
CONF_ENTRY_CROSSFADE,
CONF_ENTRY_CROSSFADE_DURATION,
- CONF_ENTRY_ENFORCE_MP3_DEFAULT_ENABLED,
+ CONF_ENTRY_OUTPUT_CODEC_DEFAULT_MP3,
CONF_ENTRY_HTTP_PROFILE,
CONF_ENTRY_ENABLE_ICY_METADATA,
CONF_ENTRY_FLOW_MODE_ENFORCED,
if bit_depth not in supported_bit_depths:
supported_bit_depths.append(bit_depth)
if not supports_flac:
- base_entries = (*base_entries, CONF_ENTRY_ENFORCE_MP3_HIDDEN)
+ base_entries = (*base_entries, CONF_ENTRY_OUTPUT_CODEC_ENFORCE_MP3)
return (
*base_entries,
# New ESPHome mediaplayer (used in Voice PE) uses FLAC 48khz/16 bits
async def play_media(self, player_id: str, media: PlayerMedia) -> None:
"""Handle PLAY MEDIA on given player."""
- if self.mass.config.get_player_config_value(
- player_id,
- CONF_ENFORCE_MP3,
- ):
- media.uri = media.uri.replace(".flac", ".mp3")
player = self.mass.players.get(player_id, True)
assert player
extra_data = {
CONF_CROSSFADE,
CONF_CROSSFADE_DURATION,
CONF_ENABLE_ICY_METADATA,
- CONF_ENFORCE_MP3,
CONF_ENTRY_CROSSFADE,
CONF_ENTRY_CROSSFADE_DURATION,
CONF_ENTRY_FLOW_MODE_ENFORCED,
CONF_GROUP_MEMBERS,
CONF_HTTP_PROFILE,
CONF_MUTE_CONTROL,
+ CONF_OUTPUT_CODEC,
CONF_POWER_CONTROL,
CONF_SAMPLE_RATES,
CONF_VOLUME_CONTROL,
CONF_ENABLE_ICY_METADATA,
CONF_CROSSFADE,
CONF_CROSSFADE_DURATION,
- CONF_ENFORCE_MP3,
+ CONF_OUTPUT_CODEC,
CONF_FLOW_MODE,
CONF_SAMPLE_RATES,
)
from music_assistant.constants import (
CONF_CROSSFADE,
CONF_CROSSFADE_DURATION,
- CONF_ENFORCE_MP3,
CONF_ENTRY_CROSSFADE,
CONF_ENTRY_CROSSFADE_DURATION,
CONF_ENTRY_DEPRECATED_EQ_BASS,
CONF_ENTRY_DEPRECATED_EQ_MID,
CONF_ENTRY_DEPRECATED_EQ_TREBLE,
- CONF_ENTRY_ENFORCE_MP3,
CONF_ENTRY_HTTP_PROFILE_FORCED_2,
CONF_ENTRY_OUTPUT_CHANNELS,
+ CONF_ENTRY_OUTPUT_CODEC,
CONF_ENTRY_SYNC_ADJUST,
CONF_PORT,
CONF_SYNC_ADJUST,
CONF_ENTRY_DEPRECATED_EQ_TREBLE,
CONF_ENTRY_OUTPUT_CHANNELS,
CONF_ENTRY_CROSSFADE_DURATION,
- CONF_ENTRY_ENFORCE_MP3,
+ CONF_ENTRY_OUTPUT_CODEC,
CONF_ENTRY_SYNC_ADJUST,
CONF_ENTRY_DISPLAY,
CONF_ENTRY_VISUALIZATION,
async with TaskManager(self.mass) as tg:
for slimplayer in self._get_sync_clients(player_id):
url = f"{base_url}&child_player_id={slimplayer.player_id}"
- if self.mass.config.get_raw_player_config_value(
- slimplayer.player_id, CONF_ENFORCE_MP3, False
- ):
- url = url.replace("flac", "mp3")
stream.expected_clients += 1
tg.create_task(
self._handle_play_url(
"""Handle enqueuing of the next queue item on the player."""
if not (slimplayer := self.slimproto.get_player(player_id)):
return
- url = media.uri
- if self.mass.config.get_raw_player_config_value(
- slimplayer.player_id, CONF_ENFORCE_MP3, False
- ):
- url = url.replace("flac", "mp3")
-
await self._handle_play_url(
slimplayer,
- url=url,
+ url=media.uri,
media=media,
enqueue=True,
send_flush=False,
from aiosonos.api.models import SonosCapability
from aiosonos.utils import get_discovery_info
from music_assistant_models.config_entries import ConfigEntry, PlayerConfig
-from music_assistant_models.enums import ConfigEntryType, ContentType, PlayerState, ProviderFeature
+from music_assistant_models.enums import ConfigEntryType, PlayerState, ProviderFeature
from music_assistant_models.errors import PlayerCommandFailed
from music_assistant_models.player import DeviceInfo, PlayerMedia
from zeroconf import ServiceStateChange
from music_assistant.constants import (
CONF_ENTRY_CROSSFADE,
- CONF_ENTRY_ENFORCE_MP3,
CONF_ENTRY_FLOW_MODE_HIDDEN_DISABLED,
+ CONF_ENTRY_OUTPUT_CODEC,
MASS_LOGO_ONLINE,
VERBOSE_LOG_LEVEL,
create_sample_rates_config_entry,
*await super().get_player_config_entries(player_id),
CONF_ENTRY_CROSSFADE,
CONF_ENTRY_FLOW_MODE_HIDDEN_DISABLED,
- CONF_ENTRY_ENFORCE_MP3,
+ CONF_ENTRY_OUTPUT_CODEC,
create_sample_rates_config_entry(
max_sample_rate=48000, max_bit_depth=24, safe_max_bit_depth=24, hidden=True
),
limit=upcoming_window_size + previous_window_size,
offset=max(queue_index - previous_window_size, 0),
)
- enforce_mp3 = self.mass.config.get_raw_player_config_value(
- sonos_player_id, CONF_ENTRY_ENFORCE_MP3.key, CONF_ENTRY_ENFORCE_MP3.default_value
- )
- sonos_queue_items = [
- self._parse_sonos_queue_item(item, enforce_mp3) for item in queue_items
- ]
+ sonos_queue_items = [await self._parse_sonos_queue_item(item) for item in queue_items]
result = {
"includesBeginningOfQueue": offset == 0,
"includesEndOfQueue": mass_queue.items <= (queue_index + len(sonos_queue_items)),
break
return web.Response(status=204)
- def _parse_sonos_queue_item(self, queue_item: QueueItem, enforce_mp3: bool) -> dict[str, Any]:
+ async def _parse_sonos_queue_item(self, queue_item: QueueItem) -> dict[str, Any]:
"""Parse a Sonos queue item to a PlayerMedia object."""
available = queue_item.media_item.available if queue_item.media_item else True
return {
"policies": {},
"track": {
"type": "track",
- "mediaUrl": self.mass.streams.resolve_stream_url(
- queue_item, output_codec=ContentType.MP3 if enforce_mp3 else ContentType.FLAC
- ),
+ "mediaUrl": await self.mass.streams.resolve_stream_url(queue_item),
"contentType": "audio/flac",
"service": {
"name": "Music Assistant",
from music_assistant.constants import (
CONF_CROSSFADE,
- CONF_ENFORCE_MP3,
CONF_ENTRY_CROSSFADE,
- CONF_ENTRY_ENFORCE_MP3,
CONF_ENTRY_FLOW_MODE_HIDDEN_DISABLED,
CONF_ENTRY_HTTP_PROFILE_FORCED_1,
+ CONF_ENTRY_OUTPUT_CODEC,
VERBOSE_LOG_LEVEL,
create_sample_rates_config_entry,
)
return (
*base_entries,
CONF_ENTRY_CROSSFADE,
- CONF_ENTRY_ENFORCE_MP3,
+ CONF_ENTRY_OUTPUT_CODEC,
CONF_ENTRY_FLOW_MODE_HIDDEN_DISABLED,
)
return (
*base_entries,
CONF_ENTRY_CROSSFADE,
CONF_ENTRY_SAMPLE_RATES,
- CONF_ENTRY_ENFORCE_MP3,
+ CONF_ENTRY_OUTPUT_CODEC,
CONF_ENTRY_FLOW_MODE_HIDDEN_DISABLED,
CONF_ENTRY_HTTP_PROFILE_FORCED_1,
)
"accept play_media command, it is synced to another player."
)
raise PlayerCommandFailed(msg)
- if await self.mass.config.get_player_config_value(player_id, CONF_ENFORCE_MP3):
- media.uri = media.uri.replace(".flac", ".mp3")
didl_metadata = create_didl_metadata(media)
await asyncio.to_thread(sonos_player.soco.play_uri, media.uri, meta=didl_metadata)
self.mass.call_later(2, sonos_player.poll_speaker)
async def enqueue_next_media(self, player_id: str, media: PlayerMedia) -> None:
"""Handle enqueuing of the next queue item on the player."""
sonos_player = self.sonosplayers[player_id]
- if await self.mass.config.get_player_config_value(player_id, CONF_ENFORCE_MP3):
- media.uri = media.uri.replace(".flac", ".mp3")
didl_metadata = create_didl_metadata(media)
# set crossfade according to player setting
crossfade = bool(await self.mass.config.get_player_config_value(player_id, CONF_CROSSFADE))