Clean up forked_daapd volume saving/setting in async_play_media (#35584)

* Clean up volume saving/setting in async_play_media

* Set source to pipe when queued externally

* Add server version requirement to error string
This commit is contained in:
uvjustin 2020-05-15 01:42:00 +08:00 committed by GitHub
parent cb7b8d94c0
commit 9fd6db4b5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 22 deletions

View File

@ -237,7 +237,7 @@ class ForkedDaapdMaster(MediaPlayerDevice):
self._track_info = defaultdict( self._track_info = defaultdict(
str str
) # _track info is found by matching _player data with _queue data ) # _track info is found by matching _player data with _queue data
self._last_outputs = None # used for device on/off self._last_outputs = [] # used for device on/off
self._last_volume = DEFAULT_UNMUTE_VOLUME self._last_volume = DEFAULT_UNMUTE_VOLUME
self._player_last_updated = None self._player_last_updated = None
self._pipe_control_api = {} self._pipe_control_api = {}
@ -349,6 +349,13 @@ class ForkedDaapdMaster(MediaPlayerDevice):
): ):
self._tts_requested = False self._tts_requested = False
self._tts_queued = True self._tts_queued = True
if (
self._queue["count"] >= 1
and self._queue["items"][0]["data_kind"] == "pipe"
and self._queue["items"][0]["title"] in KNOWN_PIPES
): # if we're playing a pipe, set the source automatically so we can forward controls
self._source = f"{self._queue['items'][0]['title']} (pipe)"
self._update_track_info() self._update_track_info()
event.set() event.set()
@ -407,6 +414,7 @@ class ForkedDaapdMaster(MediaPlayerDevice):
async def async_turn_on(self): async def async_turn_on(self):
"""Restore the last on outputs state.""" """Restore the last on outputs state."""
# restore state # restore state
await self._api.set_volume(volume=self._last_volume * 100)
if self._last_outputs: if self._last_outputs:
futures = [] futures = []
for output in self._last_outputs: for output in self._last_outputs:
@ -418,19 +426,16 @@ class ForkedDaapdMaster(MediaPlayerDevice):
) )
) )
await asyncio.wait(futures) await asyncio.wait(futures)
else: else: # enable all outputs
selected = [] await self._api.set_enabled_outputs(
for output in self._outputs: [output["id"] for output in self._outputs]
selected.append(output["id"]) )
await self._api.set_enabled_outputs(selected)
async def async_turn_off(self): async def async_turn_off(self):
"""Pause player and store outputs state.""" """Pause player and store outputs state."""
await self.async_media_pause() await self.async_media_pause()
if any(
[output["selected"] for output in self._outputs]
): # only store output state if some output is selected
self._last_outputs = self._outputs self._last_outputs = self._outputs
if any([output["selected"] for output in self._outputs]):
await self._api.set_enabled_outputs([]) await self._api.set_enabled_outputs([])
async def async_toggle(self): async def async_toggle(self):
@ -613,8 +618,12 @@ class ForkedDaapdMaster(MediaPlayerDevice):
url = self._api.full_url(url) url = self._api.full_url(url)
return url return url
async def _set_tts_volumes(self): async def _save_and_set_tts_volumes(self):
if self.volume_level: # save master volume
self._last_volume = self.volume_level
self._last_outputs = self._outputs
if self._outputs: if self._outputs:
await self._api.set_volume(volume=self._tts_volume * 100)
futures = [] futures = []
for output in self._outputs: for output in self._outputs:
futures.append( futures.append(
@ -623,7 +632,6 @@ class ForkedDaapdMaster(MediaPlayerDevice):
) )
) )
await asyncio.wait(futures) await asyncio.wait(futures)
await self._api.set_volume(volume=self._tts_volume * 100)
async def _pause_and_wait_for_callback(self): async def _pause_and_wait_for_callback(self):
"""Send pause and wait for the pause callback to be received.""" """Send pause and wait for the pause callback to be received."""
@ -641,14 +649,12 @@ class ForkedDaapdMaster(MediaPlayerDevice):
"""Play a URI.""" """Play a URI."""
if media_type == MEDIA_TYPE_MUSIC: if media_type == MEDIA_TYPE_MUSIC:
saved_state = self.state # save play state saved_state = self.state # save play state
if any([output["selected"] for output in self._outputs]): # save outputs saved_mute = self.is_volume_muted
self._last_outputs = self._outputs
await self._api.set_enabled_outputs([]) # turn off outputs
sleep_future = asyncio.create_task( sleep_future = asyncio.create_task(
asyncio.sleep(self._tts_pause_time) asyncio.sleep(self._tts_pause_time)
) # start timing now, but not exact because of fd buffer + tts latency ) # start timing now, but not exact because of fd buffer + tts latency
await self._pause_and_wait_for_callback() await self._pause_and_wait_for_callback()
await self._set_tts_volumes() await self._save_and_set_tts_volumes()
# save position # save position
saved_song_position = self._player["item_progress_ms"] saved_song_position = self._player["item_progress_ms"]
saved_queue = ( saved_queue = (
@ -678,7 +684,9 @@ class ForkedDaapdMaster(MediaPlayerDevice):
_LOGGER.warning("TTS request timed out") _LOGGER.warning("TTS request timed out")
self._tts_playing_event.clear() self._tts_playing_event.clear()
# TTS done, return to normal # TTS done, return to normal
await self.async_turn_on() # restores outputs await self.async_turn_on() # restore outputs and volumes
if saved_mute: # mute if we were muted
await self.async_mute_volume(True)
if self._use_pipe_control(): # resume pipe if self._use_pipe_control(): # resume pipe
await self._api.add_to_queue( await self._api.add_to_queue(
uris=self._sources_uris[self._source], clear=True uris=self._sources_uris[self._source], clear=True

View File

@ -16,7 +16,7 @@
"websocket_not_enabled": "forked-daapd server websocket not enabled.", "websocket_not_enabled": "forked-daapd server websocket not enabled.",
"wrong_host_or_port": "Unable to connect. Please check host and port.", "wrong_host_or_port": "Unable to connect. Please check host and port.",
"wrong_password": "Incorrect password.", "wrong_password": "Incorrect password.",
"wrong_server_type": "Not a forked-daapd server.", "wrong_server_type": "The forked-daapd integration requires a forked-daapd server with version >= 27.0.",
"unknown_error": "Unknown error." "unknown_error": "Unknown error."
}, },
"abort": { "abort": {

View File

@ -9,7 +9,7 @@
"websocket_not_enabled": "forked-daapd server websocket not enabled.", "websocket_not_enabled": "forked-daapd server websocket not enabled.",
"wrong_host_or_port": "Unable to connect. Please check host and port.", "wrong_host_or_port": "Unable to connect. Please check host and port.",
"wrong_password": "Incorrect password.", "wrong_password": "Incorrect password.",
"wrong_server_type": "Not a forked-daapd server." "wrong_server_type": "The forked-daapd integration requires a forked-daapd server with version >= 27.0."
}, },
"flow_title": "forked-daapd server: {name} ({host})", "flow_title": "forked-daapd server: {name} ({host})",
"step": { "step": {

View File

@ -111,7 +111,7 @@ SAMPLE_PLAYER_STOPPED = {
"item_progress_ms": 5, "item_progress_ms": 5,
} }
SAMPLE_TTS_QUEUE = { SAMPLE_QUEUE_TTS = {
"version": 833, "version": 833,
"count": 1, "count": 1,
"items": [ "items": [
@ -127,11 +127,31 @@ SAMPLE_TTS_QUEUE = {
"length_ms": 0, "length_ms": 0,
"track_number": 1, "track_number": 1,
"media_kind": "music", "media_kind": "music",
"data_kind": "url",
"uri": "tts_proxy_somefile.mp3", "uri": "tts_proxy_somefile.mp3",
} }
], ],
} }
SAMPLE_QUEUE_PIPE = {
"version": 833,
"count": 1,
"items": [
{
"id": 12322,
"title": "librespot-java",
"artist": "some artist",
"album": "some album",
"album_artist": "The xx",
"length_ms": 0,
"track_number": 1,
"media_kind": "music",
"data_kind": "pipe",
"uri": "pipeuri",
}
],
}
SAMPLE_CONFIG = { SAMPLE_CONFIG = {
"websocket_port": 3688, "websocket_port": 3688,
"version": "25.0", "version": "25.0",
@ -272,7 +292,7 @@ async def get_request_return_values_fixture():
"config": SAMPLE_CONFIG, "config": SAMPLE_CONFIG,
"outputs": SAMPLE_OUTPUTS_ON, "outputs": SAMPLE_OUTPUTS_ON,
"player": SAMPLE_PLAYER_PAUSED, "player": SAMPLE_PLAYER_PAUSED,
"queue": SAMPLE_TTS_QUEUE, "queue": SAMPLE_QUEUE_TTS,
} }
@ -630,7 +650,9 @@ async def pipe_control_api_object_fixture(
return pipe_control_api.return_value return pipe_control_api.return_value
async def test_librespot_java_stuff(hass, pipe_control_api_object): async def test_librespot_java_stuff(
hass, get_request_return_values, mock_api_object, pipe_control_api_object
):
"""Test options update and librespot-java stuff.""" """Test options update and librespot-java stuff."""
state = hass.states.get(TEST_MASTER_ENTITY_NAME) state = hass.states.get(TEST_MASTER_ENTITY_NAME)
assert state.attributes[ATTR_INPUT_SOURCE] == "librespot-java (pipe)" assert state.attributes[ATTR_INPUT_SOURCE] == "librespot-java (pipe)"
@ -652,6 +674,13 @@ async def test_librespot_java_stuff(hass, pipe_control_api_object):
) )
state = hass.states.get(TEST_MASTER_ENTITY_NAME) state = hass.states.get(TEST_MASTER_ENTITY_NAME)
assert state.attributes[ATTR_INPUT_SOURCE] == SOURCE_NAME_DEFAULT assert state.attributes[ATTR_INPUT_SOURCE] == SOURCE_NAME_DEFAULT
# test pipe getting queued externally changes source
get_request_return_values["queue"] = SAMPLE_QUEUE_PIPE
updater_update = mock_api_object.start_websocket_handler.call_args[0][2]
await updater_update(["queue"])
await hass.async_block_till_done()
state = hass.states.get(TEST_MASTER_ENTITY_NAME)
assert state.attributes[ATTR_INPUT_SOURCE] == "librespot-java (pipe)"
async def test_librespot_java_play_media(hass, pipe_control_api_object): async def test_librespot_java_play_media(hass, pipe_control_api_object):