Skip to content

Commit

Permalink
A collection of small enhancements (#1130)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelveldt authored Mar 10, 2024
1 parent 261ff97 commit c9eee26
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 29 deletions.
1 change: 0 additions & 1 deletion .github/release-drafter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,5 @@ version-resolver:
labels:
- 'new-feature'
- 'new-provider'
- 'enhancement'
- 'refactor'
default: patch
2 changes: 1 addition & 1 deletion .github/workflows/pr-labels.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ jobs:
uses: ludeeus/[email protected]
with:
labels: >-
breaking-change, bugfix, refactor, new-feature, maintenance, ci, dependencies, new-provider
breaking-change, bugfix, refactor, new-feature, maintenance, enhancement, ci, dependencies, new-provider
13 changes: 13 additions & 0 deletions music_assistant/common/models/config_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
CONF_AUTO_PLAY,
CONF_CROSSFADE,
CONF_CROSSFADE_DURATION,
CONF_ENFORCE_MP3,
CONF_EQ_BASS,
CONF_EQ_MID,
CONF_EQ_TREBLE,
Expand Down Expand Up @@ -408,3 +409,15 @@ class CoreConfig(Config):
default_value=False,
advanced=True,
)

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.",
advanced=True,
)
1 change: 1 addition & 0 deletions music_assistant/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
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"

# config default values
DEFAULT_HOST: Final[str] = "0.0.0.0"
Expand Down
2 changes: 1 addition & 1 deletion music_assistant/server/helpers/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ async def is_hass_supervisor() -> bool:

def _check():
try:
urllib.request.urlopen("http://supervisor/core")
urllib.request.urlopen("http://supervisor/core", timeout=1)
except urllib.error.URLError as err:
# this should return a 401 unauthorized if it exists
return getattr(err, "code", 999) == 401
Expand Down
18 changes: 12 additions & 6 deletions music_assistant/server/providers/airplay/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,15 @@ def get_model_from_am(am_property: str | None) -> tuple[str, str]:

def get_primary_ip_address(discovery_info: AsyncServiceInfo) -> str | None:
"""Get primary IP address from zeroconf discovery info."""
return next(
(x for x in discovery_info.parsed_addresses(IPVersion.V4Only) if x != "127.0.0.1"), None
)
for address in discovery_info.parsed_addresses(IPVersion.V4Only):
if address.startswith("127"):
# filter out loopback address
continue
if address.startswith("169.254"):
# filter out APIPA address
continue
return address
return None


class AirplayStreamJob:
Expand Down Expand Up @@ -357,12 +363,12 @@ async def _audio_reader(self) -> None:
# EOF chunk
break
self._cliraop_proc.stdin.write(chunk)
with suppress(BrokenPipeError):
with suppress(BrokenPipeError, ConnectionResetError):
await self._cliraop_proc.stdin.drain()
# send EOF
if self._cliraop_proc.returncode is None and not self._cliraop_proc.stdin.is_closing():
self._cliraop_proc.stdin.write_eof()
with suppress(BrokenPipeError):
with suppress(BrokenPipeError, ConnectionResetError):
await self._cliraop_proc.stdin.drain()
logger.debug("Audio reader finished")

Expand Down Expand Up @@ -453,10 +459,10 @@ async def on_mdns_service_state_change(
if mass_player := self.mass.players.get(player_id):
cur_address = get_primary_ip_address(info)
if cur_address and cur_address != airplay_player.address:
airplay_player.address = cur_address
airplay_player.logger.info(
"Address updated from %s to %s", airplay_player.address, cur_address
)
airplay_player.address = cur_address
mass_player.device_info = DeviceInfo(
model=mass_player.device_info.model,
manufacturer=mass_player.device_info.manufacturer,
Expand Down
17 changes: 4 additions & 13 deletions music_assistant/server/providers/dlna/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

from music_assistant.common.models.config_entries import (
CONF_ENTRY_CROSSFADE_DURATION,
CONF_ENTRY_ENFORCE_MP3,
CONF_ENTRY_FLOW_MODE,
ConfigEntry,
ConfigValueType,
Expand All @@ -38,7 +39,7 @@
)
from music_assistant.common.models.errors import PlayerUnavailableError
from music_assistant.common.models.player import DeviceInfo, Player
from music_assistant.constants import CONF_CROSSFADE, CONF_FLOW_MODE, CONF_PLAYERS
from music_assistant.constants import CONF_CROSSFADE, CONF_ENFORCE_MP3, CONF_FLOW_MODE, CONF_PLAYERS
from music_assistant.server.helpers.didl_lite import create_didl_metadata
from music_assistant.server.models.player_provider import PlayerProvider

Expand All @@ -63,7 +64,7 @@
)

CONF_ENQUEUE_NEXT = "enqueue_next"
CONF_ENFORCE_MP3 = "enforce_mp3"


PLAYER_CONFIG_ENTRIES = (
ConfigEntry(
Expand All @@ -88,17 +89,7 @@
),
CONF_ENTRY_FLOW_MODE,
CONF_ENTRY_CROSSFADE_DURATION,
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.",
advanced=True,
),
CONF_ENTRY_ENFORCE_MP3,
)

CONF_NETWORK_SCAN = "network_scan"
Expand Down
25 changes: 18 additions & 7 deletions music_assistant/server/providers/snapcast/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,18 @@ async def _streamer() -> None:
):
writer.write(pcm_chunk)
await writer.drain()

finally:
await self._snapserver.stream_remove_stream(stream.identifier)
# end of the stream reached
if writer.can_write_eof():
writer.close()
writer.write_eof()
await writer.drain()
# we need to wait a bit before removing the stream to ensure
# that all snapclients have consumed the audio
# https://github.com/music-assistant/hass-music-assistant/issues/1962
await asyncio.sleep(30)
finally:
if not writer.is_closing():
writer.close()
await self._snapserver.stream_remove_stream(stream.identifier)
self.logger.debug("Closed connection to %s:%s", host, port)

# start streaming the queue (pcm) audio in a background task
Expand Down Expand Up @@ -340,12 +345,18 @@ async def _streamer() -> None:
async for pcm_chunk in stream_job.subscribe(player_id):
writer.write(pcm_chunk)
await writer.drain()
finally:
await self._snapserver.stream_remove_stream(stream.identifier)
# end of the stream reached
if writer.can_write_eof():
writer.close()
writer.write_eof()
await writer.drain()
# we need to wait a bit before removing the stream to ensure
# that all snapclients have consumed the audio
# https://github.com/music-assistant/hass-music-assistant/issues/1962
await asyncio.sleep(30)
finally:
if not writer.is_closing():
writer.close()
await self._snapserver.stream_remove_stream(stream.identifier)
self.logger.debug("Closed connection to %s:%s", host, port)

# start streaming the queue (pcm) audio in a background task
Expand Down

0 comments on commit c9eee26

Please sign in to comment.