Convert media player enqueue to an enum (#72406)

This commit is contained in:
Paulus Schoutsen 2022-05-26 13:57:00 -07:00
parent f33517ef2c
commit 6bf6a0f7bc
9 changed files with 119 additions and 36 deletions

View file

@ -24,10 +24,7 @@ from homeassistant.components.media_player import (
from homeassistant.components.media_player.browse_media import (
async_process_play_media_url,
)
from homeassistant.components.media_player.const import (
ATTR_MEDIA_ENQUEUE,
MEDIA_TYPE_MUSIC,
)
from homeassistant.components.media_player.const import MEDIA_TYPE_MUSIC
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_HOST,
@ -1023,11 +1020,7 @@ class BluesoundPlayer(MediaPlayerEntity):
return await self.send_bluesound_command(f"Play?seek={float(position)}")
async def async_play_media(self, media_type, media_id, **kwargs):
"""
Send the play_media command to the media player.
If ATTR_MEDIA_ENQUEUE is True, add `media_id` to the queue.
"""
"""Send the play_media command to the media player."""
if self.is_grouped and not self.is_master:
return
@ -1041,9 +1034,6 @@ class BluesoundPlayer(MediaPlayerEntity):
url = f"Play?url={media_id}"
if kwargs.get(ATTR_MEDIA_ENQUEUE):
return await self.send_bluesound_command(url)
return await self.send_bluesound_command(url)
async def async_volume_up(self):

View file

@ -12,6 +12,7 @@ from typing_extensions import ParamSpec
from homeassistant.components import media_source
from homeassistant.components.media_player import (
MediaPlayerEnqueue,
MediaPlayerEntity,
MediaPlayerEntityFeature,
)
@ -73,6 +74,14 @@ CONTROL_TO_SUPPORT = {
heos_const.CONTROL_PLAY_NEXT: MediaPlayerEntityFeature.NEXT_TRACK,
}
HA_HEOS_ENQUEUE_MAP = {
None: heos_const.ADD_QUEUE_REPLACE_AND_PLAY,
MediaPlayerEnqueue.ADD: heos_const.ADD_QUEUE_ADD_TO_END,
MediaPlayerEnqueue.REPLACE: heos_const.ADD_QUEUE_REPLACE_AND_PLAY,
MediaPlayerEnqueue.NEXT: heos_const.ADD_QUEUE_PLAY_NEXT,
MediaPlayerEnqueue.PLAY: heos_const.ADD_QUEUE_PLAY_NOW,
}
_LOGGER = logging.getLogger(__name__)
@ -224,11 +233,8 @@ class HeosMediaPlayer(MediaPlayerEntity):
playlist = next((p for p in playlists if p.name == media_id), None)
if not playlist:
raise ValueError(f"Invalid playlist '{media_id}'")
add_queue_option = (
heos_const.ADD_QUEUE_ADD_TO_END
if kwargs.get(ATTR_MEDIA_ENQUEUE)
else heos_const.ADD_QUEUE_REPLACE_AND_PLAY
)
add_queue_option = HA_HEOS_ENQUEUE_MAP.get(kwargs.get(ATTR_MEDIA_ENQUEUE))
await self._player.add_to_queue(playlist, add_queue_option)
return

View file

@ -147,6 +147,19 @@ ENTITY_IMAGE_CACHE = {CACHE_IMAGES: collections.OrderedDict(), CACHE_MAXSIZE: 16
SCAN_INTERVAL = dt.timedelta(seconds=10)
class MediaPlayerEnqueue(StrEnum):
"""Enqueue types for playing media."""
# add given media item to end of the queue
ADD = "add"
# play the given media item next, keep queue
NEXT = "next"
# play the given media item now, keep queue
PLAY = "play"
# play the given media item now, clear queue
REPLACE = "replace"
class MediaPlayerDeviceClass(StrEnum):
"""Device class for media players."""
@ -169,7 +182,9 @@ DEVICE_CLASS_RECEIVER = MediaPlayerDeviceClass.RECEIVER.value
MEDIA_PLAYER_PLAY_MEDIA_SCHEMA = {
vol.Required(ATTR_MEDIA_CONTENT_TYPE): cv.string,
vol.Required(ATTR_MEDIA_CONTENT_ID): cv.string,
vol.Optional(ATTR_MEDIA_ENQUEUE): cv.boolean,
vol.Optional(ATTR_MEDIA_ENQUEUE): vol.Any(
cv.boolean, vol.Coerce(MediaPlayerEnqueue)
),
vol.Optional(ATTR_MEDIA_EXTRA, default={}): dict,
}
@ -350,10 +365,30 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"async_select_sound_mode",
[MediaPlayerEntityFeature.SELECT_SOUND_MODE],
)
# Remove in Home Assistant 2022.9
def _rewrite_enqueue(value):
"""Rewrite the enqueue value."""
if ATTR_MEDIA_ENQUEUE not in value:
pass
elif value[ATTR_MEDIA_ENQUEUE] is True:
value[ATTR_MEDIA_ENQUEUE] = MediaPlayerEnqueue.ADD
_LOGGER.warning(
"Playing media with enqueue set to True is deprecated. Use 'add' instead"
)
elif value[ATTR_MEDIA_ENQUEUE] is False:
value[ATTR_MEDIA_ENQUEUE] = MediaPlayerEnqueue.PLAY
_LOGGER.warning(
"Playing media with enqueue set to False is deprecated. Use 'play' instead"
)
return value
component.async_register_entity_service(
SERVICE_PLAY_MEDIA,
vol.All(
cv.make_entity_service_schema(MEDIA_PLAYER_PLAY_MEDIA_SCHEMA),
_rewrite_enqueue,
_rename_keys(
media_type=ATTR_MEDIA_CONTENT_TYPE,
media_id=ATTR_MEDIA_CONTENT_ID,

View file

@ -27,7 +27,6 @@ from .const import (
ATTR_INPUT_SOURCE,
ATTR_MEDIA_CONTENT_ID,
ATTR_MEDIA_CONTENT_TYPE,
ATTR_MEDIA_ENQUEUE,
ATTR_MEDIA_VOLUME_LEVEL,
ATTR_MEDIA_VOLUME_MUTED,
ATTR_SOUND_MODE,
@ -118,7 +117,7 @@ async def _async_reproduce_states(
if features & MediaPlayerEntityFeature.PLAY_MEDIA:
await call_service(
SERVICE_PLAY_MEDIA,
[ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_ENQUEUE],
[ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_CONTENT_ID],
)
already_playing = True

View file

@ -151,6 +151,22 @@ play_media:
selector:
text:
enqueue:
name: Enqueue
description: If the content should be played now or be added to the queue.
required: false
selector:
select:
options:
- label: "Play now"
value: "play"
- label: "Play next"
value: "next"
- label: "Add to queue"
value: "add"
- label: "Play now and clear queue"
value: "replace"
select_source:
name: Select source
description: Send the media player the command to change input source.

View file

@ -18,6 +18,7 @@ import voluptuous as vol
from homeassistant.components import media_source, spotify
from homeassistant.components.media_player import (
MediaPlayerEnqueue,
MediaPlayerEntity,
MediaPlayerEntityFeature,
async_process_play_media_url,
@ -537,8 +538,6 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
If media_type is "playlist", media_id should be a Sonos
Playlist name. Otherwise, media_id should be a URI.
If ATTR_MEDIA_ENQUEUE is True, add `media_id` to the queue.
"""
if spotify.is_spotify_media_type(media_type):
media_type = spotify.resolve_spotify_media_type(media_type)
@ -575,7 +574,7 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
)
if result.shuffle:
self.set_shuffle(True)
if kwargs.get(ATTR_MEDIA_ENQUEUE):
if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.ADD:
plex_plugin.add_to_queue(result.media)
else:
soco.clear_queue()
@ -585,7 +584,7 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
share_link = self.coordinator.share_link
if share_link.is_share_link(media_id):
if kwargs.get(ATTR_MEDIA_ENQUEUE):
if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.ADD:
share_link.add_share_link_to_queue(media_id)
else:
soco.clear_queue()
@ -595,7 +594,7 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
# If media ID is a relative URL, we serve it from HA.
media_id = async_process_play_media_url(self.hass, media_id)
if kwargs.get(ATTR_MEDIA_ENQUEUE):
if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.ADD:
soco.add_uri_to_queue(media_id)
else:
soco.play_uri(media_id, force_radio=is_radio)

View file

@ -10,6 +10,7 @@ import voluptuous as vol
from homeassistant.components import media_source
from homeassistant.components.media_player import (
MediaPlayerEnqueue,
MediaPlayerEntity,
MediaPlayerEntityFeature,
)
@ -469,16 +470,17 @@ class SqueezeBoxEntity(MediaPlayerEntity):
await self._player.async_set_power(True)
async def async_play_media(self, media_type, media_id, **kwargs):
"""
Send the play_media command to the media player.
If ATTR_MEDIA_ENQUEUE is True, add `media_id` to the current playlist.
"""
cmd = "play"
"""Send the play_media command to the media player."""
index = None
if kwargs.get(ATTR_MEDIA_ENQUEUE):
enqueue: MediaPlayerEnqueue | None = kwargs.get(ATTR_MEDIA_ENQUEUE)
if enqueue == MediaPlayerEnqueue.ADD:
cmd = "add"
elif enqueue == MediaPlayerEnqueue.NEXT:
cmd = "insert"
else:
cmd = "play"
if media_source.is_media_source_id(media_id):
media_type = MEDIA_TYPE_MUSIC

View file

@ -4,6 +4,8 @@ import base64
from http import HTTPStatus
from unittest.mock import patch
import pytest
from homeassistant.components import media_player
from homeassistant.components.media_player.browse_media import BrowseMedia
from homeassistant.components.websocket_api.const import TYPE_RESULT
@ -251,3 +253,41 @@ async def test_group_members_available_when_off(hass):
state = hass.states.get("media_player.bedroom")
assert state.state == STATE_OFF
assert "group_members" in state.attributes
@pytest.mark.parametrize(
"input,expected",
(
(True, media_player.MediaPlayerEnqueue.ADD),
(False, media_player.MediaPlayerEnqueue.PLAY),
("play", media_player.MediaPlayerEnqueue.PLAY),
("next", media_player.MediaPlayerEnqueue.NEXT),
("add", media_player.MediaPlayerEnqueue.ADD),
("replace", media_player.MediaPlayerEnqueue.REPLACE),
),
)
async def test_enqueue_rewrite(hass, input, expected):
"""Test that group_members are still available when media_player is off."""
await async_setup_component(
hass, "media_player", {"media_player": {"platform": "demo"}}
)
await hass.async_block_till_done()
# Fake group support for DemoYoutubePlayer
with patch(
"homeassistant.components.demo.media_player.DemoYoutubePlayer.play_media",
) as mock_play_media:
await hass.services.async_call(
"media_player",
"play_media",
{
"entity_id": "media_player.bedroom",
"media_content_type": "music",
"media_content_id": "1234",
"enqueue": input,
},
blocking=True,
)
assert len(mock_play_media.mock_calls) == 1
assert mock_play_media.mock_calls[0][2]["enqueue"] == expected

View file

@ -6,7 +6,6 @@ from homeassistant.components.media_player.const import (
ATTR_INPUT_SOURCE,
ATTR_MEDIA_CONTENT_ID,
ATTR_MEDIA_CONTENT_TYPE,
ATTR_MEDIA_ENQUEUE,
ATTR_MEDIA_VOLUME_LEVEL,
ATTR_MEDIA_VOLUME_MUTED,
ATTR_SOUND_MODE,
@ -253,7 +252,6 @@ async def test_play_media(hass):
value_1 = "dummy_1"
value_2 = "dummy_2"
value_3 = "dummy_3"
await async_reproduce_states(
hass,
@ -275,7 +273,6 @@ async def test_play_media(hass):
{
ATTR_MEDIA_CONTENT_TYPE: value_1,
ATTR_MEDIA_CONTENT_ID: value_2,
ATTR_MEDIA_ENQUEUE: value_3,
},
)
],
@ -294,5 +291,4 @@ async def test_play_media(hass):
"entity_id": ENTITY_1,
ATTR_MEDIA_CONTENT_TYPE: value_1,
ATTR_MEDIA_CONTENT_ID: value_2,
ATTR_MEDIA_ENQUEUE: value_3,
}