item_id=queue_item.item_id,
path=queue_item.uri,
content_type=ContentType(queue_item.uri.split(".")[-1]),
- sample_rate=44100,
- bit_depth=16,
)
else:
# always request the full db track as there might be other qualities available
# set player_id on the streamdetails so we know what players stream
streamdetails.player_id = player_id
# get gain correct / replaygain
- if not queue_item.name == "alert":
+ if queue_item.name == "alert":
+ loudness = 0
+ gain_correct = 0
+ else:
loudness, gain_correct = await get_gain_correct(
mass, player_id, streamdetails.item_id, streamdetails.provider
)
- streamdetails.gain_correct = gain_correct
- streamdetails.loudness = loudness
+ streamdetails.gain_correct = gain_correct
+ streamdetails.loudness = loudness
# set streamdetails as attribute on the media_item
# this way the app knows what content is playing
queue_item.streamdetails = streamdetails
from typing import Dict, List, Optional, Set, Tuple, Union
from music_assistant.constants import (
+ CONF_CROSSFADE_DURATION,
CONF_POWER_CONTROL,
CONF_VOLUME_CONTROL,
EVENT_PLAYER_ADDED,
)
from music_assistant.models.player_queue import PlayerQueue, QueueItem, QueueOption
from music_assistant.models.provider import PlayerProvider, ProviderType
-from music_assistant.models.streamdetails import ContentType, StreamDetails, StreamType
POLL_INTERVAL = 30
self,
player_id: str,
url: str,
- gain_adjust: int = 0,
+ volume: int = 0,
force: bool = True,
announce: bool = False,
+ duration: int = 10,
):
"""
Play alert (e.g. tts message) on selected player.
:param player_id: player_id of the player to handle the command.
:param url: Url to the sound effect/tts message that should be played.
- :param gain_adjust: Adjust volume/gain of audio.
+ :param volume: Volume relative to current player's volume.
:param force: Play alert even if player is currently powered off.
:param announce: Prepend alert sound.
+ :param duration: Amount of time after which queue is restored, default 10 seconds.
"""
player = self.get_player(player_id)
player_queue = self.get_player_queue(player_id)
prev_state = player.calculated_state.state
prev_power = player.calculated_state.powered
+ prev_volume = player.calculated_state.volume_level
+ prev_repeat = player_queue.repeat_enabled
if not player.calculated_state.powered:
if not force:
LOGGER.debug(
)
return
await self.cmd_power_on(player_id)
-
+ # snapshot the queue
+ prev_queue_items = player_queue.items
+ prev_queue_index = player_queue.cur_index
+ prev_queue_crossfade = self.mass.config.get_player_config(
+ player_queue.queue_id
+ )[CONF_CROSSFADE_DURATION]
+
+ # pause playback
+ if prev_state == PlayerState.PLAYING:
+ await self.cmd_pause(player_queue.queue_id)
+ # disable crossfade and repeat if needed
+ if prev_queue_crossfade:
+ self.mass.config.player_settings[player_id][CONF_CROSSFADE_DURATION] = 0
+ if prev_repeat:
+ await player_queue.set_repeat_enabled(False)
+ # set alert volume
+ if volume != 0:
+ await self.cmd_volume_set(player_id, prev_volume + volume)
+ # load alert items in player queue
queue_items = []
if announce:
alert_announce = (
item_id="alert_announce",
provider="url",
name="alert",
- duration=2,
- streamdetails=StreamDetails(
- type=StreamType.URL,
- provider="url",
- item_id="alert_announce",
- path=str(alert_announce),
- content_type=ContentType(url.split(".")[-1]),
- gain_correct=10,
- ),
+ duration=3,
+ uri=str(alert_announce),
)
queue_item.stream_url = "%s/queue/%s/%s" % (
self.mass.web.stream_url,
item_id="alert_sound",
provider="url",
name="alert",
- duration=10,
- streamdetails=StreamDetails(
- type=StreamType.URL,
- provider="url",
- item_id="alert_sound",
- path=url,
- content_type=ContentType(url.split(".")[-1]),
- gain_correct=gain_adjust,
- ),
+ duration=duration,
+ uri=url,
)
- queue_item.stream_url = "%s/queue/%s/%s?alert=true" % (
+ queue_item.stream_url = "%s/queue/%s/%s" % (
self.mass.web.stream_url,
player_id,
queue_item.queue_item_id,
)
queue_items.append(queue_item)
- await player_queue.insert(queue_items, 0)
-
- if prev_power and prev_state in [PlayerState.PLAYING, PlayerState.PAUSED]:
- return
+ # load queue items
+ await player_queue.load(queue_items)
+
+ async def restore_queue():
+ # restore queue
+ if volume:
+ await self.cmd_volume_set(player_id, prev_volume)
+ if prev_queue_crossfade:
+ self.mass.config.player_settings[player_id][
+ CONF_CROSSFADE_DURATION
+ ] = prev_queue_crossfade
+ await player_queue.set_repeat_enabled(prev_repeat)
+ # pylint: disable=protected-access
+ player_queue._items = prev_queue_items
+ player_queue._cur_index = prev_queue_index
+ if prev_power:
+ await player_queue.resume()
+ else:
+ await self.cmd_power_off(player_id)
- # wait until playback completed
- playback_started = False
- count = 0
- while True:
- if not playback_started and player_queue.state == PlayerState.PLAYING:
- playback_started = True
- elif playback_started and (
- player_queue.state != PlayerState.PLAYING
- or (player_queue.cur_item and player_queue.cur_item.name != "alert")
- ):
- break
- if count == 20:
- break
- count += 0.2
- await asyncio.sleep(0.2)
- await self.cmd_power_off(player_id)
+ self.mass.loop.call_later(duration, create_task, restore_queue)
@api_route("players/{player_id}/cmd/stop", method="PUT")
async def cmd_stop(self, player_id: str) -> None:
player = self.get_player(player_id)
if not player:
return
- # send stop if player is playing
- if player.active_queue == player_id and player.state in [
- PlayerState.PLAYING,
- PlayerState.PAUSED,
- ]:
+ # send stop if player is active queue
+ if player.active_queue == player_id and player.state != PlayerState.OFF:
await self.cmd_stop(player_id)
player_config = self.mass.config.player_settings[player.player_id]
# turn off player
self._available = False
self._status_listener: Optional[CastStatusListener] = None
self._is_speaker_group = False
- self._throttler = Throttler(rate_limit=1, period=0.2)
+ self._throttler = Throttler(rate_limit=1, period=0.1)
@property
def player_id(self) -> str:
async def cmd_queue_load(self, queue_items: List[QueueItem]) -> None:
"""Load (overwrite) queue with new items."""
player_queue = self.mass.players.get_player_queue(self.player_id)
- cc_queue_items = self.__create_queue_items(queue_items[:25])
+ cc_queue_items = self.__create_queue_items(queue_items[:50])
repeat_enabled = player_queue.use_queue_stream or player_queue.repeat_enabled
queuedata = {
"type": "QUEUE_LOAD",
"shuffle": False, # handled by our queue controller
"queueType": "PLAYLIST",
"startIndex": 0, # Item index to play after this request or keep same item if undefined
- "items": cc_queue_items, # only load 25 tracks at once or the socket will crash
+ "items": cc_queue_items, # only load 50 tracks at once or the socket will crash
}
await self.chromecast_command(self.__send_player_queue, queuedata)
if len(queue_items) > 50:
- await self.cmd_queue_append(queue_items[26:])
+ await self.cmd_queue_append(queue_items[51:])
async def cmd_queue_append(self, queue_items: List[QueueItem]) -> None:
"""Append new items at the end of the queue."""
cc_queue_items = self.__create_queue_items(queue_items)
- async for chunk in yield_chunks(cc_queue_items, 25):
+ async for chunk in yield_chunks(cc_queue_items, 50):
queuedata = {
"type": "QUEUE_INSERT",
"insertBefore": None,
return {
"opt_itemId": queue_item.queue_item_id,
"autoplay": True,
- "preloadTime": 10,
+ "preloadTime": 0,
"playbackDuration": int(queue_item.duration),
"startTime": 0,
"activeTrackIds": [],
"streamType": "LIVE" if player_queue.use_queue_stream else "BUFFERED",
"metadata": {
"title": queue_item.name,
- "artist": next(iter(queue_item.artists)).name
- if queue_item.artists
- else "",
+ "artist": "/".join(x.name for x in queue_item.artists),
},
"duration": int(queue_item.duration),
},