Improve roku play media handling (#66429)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
parent
cdd5d22b38
commit
9d5dc2ce24
6 changed files with 266 additions and 75 deletions
|
@ -2,10 +2,12 @@
|
||||||
DOMAIN = "roku"
|
DOMAIN = "roku"
|
||||||
|
|
||||||
# Attributes
|
# Attributes
|
||||||
|
ATTR_ARTIST_NAME = "artist_name"
|
||||||
ATTR_CONTENT_ID = "content_id"
|
ATTR_CONTENT_ID = "content_id"
|
||||||
ATTR_FORMAT = "format"
|
ATTR_FORMAT = "format"
|
||||||
ATTR_KEYWORD = "keyword"
|
ATTR_KEYWORD = "keyword"
|
||||||
ATTR_MEDIA_TYPE = "media_type"
|
ATTR_MEDIA_TYPE = "media_type"
|
||||||
|
ATTR_THUMBNAIL = "thumbnail"
|
||||||
|
|
||||||
# Default Values
|
# Default Values
|
||||||
DEFAULT_PORT = 8060
|
DEFAULT_PORT = 8060
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"domain": "roku",
|
"domain": "roku",
|
||||||
"name": "Roku",
|
"name": "Roku",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/roku",
|
"documentation": "https://www.home-assistant.io/integrations/roku",
|
||||||
"requirements": ["rokuecp==0.13.2"],
|
"requirements": ["rokuecp==0.14.0"],
|
||||||
"homekit": {
|
"homekit": {
|
||||||
"models": ["3810X", "4660X", "7820X", "C105X", "C135X"]
|
"models": ["3810X", "4660X", "7820X", "C105X", "C135X"]
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,9 +3,12 @@ from __future__ import annotations
|
||||||
|
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import logging
|
import logging
|
||||||
|
import mimetypes
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from rokuecp.helpers import guess_stream_format
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
import yarl
|
||||||
|
|
||||||
from homeassistant.components import media_source
|
from homeassistant.components import media_source
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
|
@ -18,7 +21,9 @@ from homeassistant.components.media_player.const import (
|
||||||
ATTR_MEDIA_EXTRA,
|
ATTR_MEDIA_EXTRA,
|
||||||
MEDIA_TYPE_APP,
|
MEDIA_TYPE_APP,
|
||||||
MEDIA_TYPE_CHANNEL,
|
MEDIA_TYPE_CHANNEL,
|
||||||
|
MEDIA_TYPE_MUSIC,
|
||||||
MEDIA_TYPE_URL,
|
MEDIA_TYPE_URL,
|
||||||
|
MEDIA_TYPE_VIDEO,
|
||||||
SUPPORT_BROWSE_MEDIA,
|
SUPPORT_BROWSE_MEDIA,
|
||||||
SUPPORT_NEXT_TRACK,
|
SUPPORT_NEXT_TRACK,
|
||||||
SUPPORT_PAUSE,
|
SUPPORT_PAUSE,
|
||||||
|
@ -49,10 +54,12 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from . import roku_exception_handler
|
from . import roku_exception_handler
|
||||||
from .browse_media import async_browse_media
|
from .browse_media import async_browse_media
|
||||||
from .const import (
|
from .const import (
|
||||||
|
ATTR_ARTIST_NAME,
|
||||||
ATTR_CONTENT_ID,
|
ATTR_CONTENT_ID,
|
||||||
ATTR_FORMAT,
|
ATTR_FORMAT,
|
||||||
ATTR_KEYWORD,
|
ATTR_KEYWORD,
|
||||||
ATTR_MEDIA_TYPE,
|
ATTR_MEDIA_TYPE,
|
||||||
|
ATTR_THUMBNAIL,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_SEARCH,
|
SERVICE_SEARCH,
|
||||||
)
|
)
|
||||||
|
@ -76,21 +83,36 @@ SUPPORT_ROKU = (
|
||||||
| SUPPORT_BROWSE_MEDIA
|
| SUPPORT_BROWSE_MEDIA
|
||||||
)
|
)
|
||||||
|
|
||||||
ATTRS_TO_LAUNCH_PARAMS = {
|
|
||||||
ATTR_CONTENT_ID: "contentID",
|
STREAM_FORMAT_TO_MEDIA_TYPE = {
|
||||||
ATTR_MEDIA_TYPE: "MediaType",
|
"dash": MEDIA_TYPE_VIDEO,
|
||||||
|
"hls": MEDIA_TYPE_VIDEO,
|
||||||
|
"ism": MEDIA_TYPE_VIDEO,
|
||||||
|
"m4a": MEDIA_TYPE_MUSIC,
|
||||||
|
"m4v": MEDIA_TYPE_VIDEO,
|
||||||
|
"mka": MEDIA_TYPE_MUSIC,
|
||||||
|
"mkv": MEDIA_TYPE_VIDEO,
|
||||||
|
"mks": MEDIA_TYPE_VIDEO,
|
||||||
|
"mp3": MEDIA_TYPE_MUSIC,
|
||||||
|
"mp4": MEDIA_TYPE_VIDEO,
|
||||||
}
|
}
|
||||||
|
|
||||||
PLAY_MEDIA_SUPPORTED_TYPES = (
|
ATTRS_TO_LAUNCH_PARAMS = {
|
||||||
MEDIA_TYPE_APP,
|
ATTR_CONTENT_ID: "contentID",
|
||||||
MEDIA_TYPE_CHANNEL,
|
ATTR_MEDIA_TYPE: "mediaType",
|
||||||
MEDIA_TYPE_URL,
|
}
|
||||||
FORMAT_CONTENT_TYPE[HLS_PROVIDER],
|
|
||||||
)
|
|
||||||
|
|
||||||
ATTRS_TO_PLAY_VIDEO_PARAMS = {
|
ATTRS_TO_PLAY_ON_ROKU_PARAMS = {
|
||||||
ATTR_NAME: "videoName",
|
ATTR_NAME: "videoName",
|
||||||
ATTR_FORMAT: "videoFormat",
|
ATTR_FORMAT: "videoFormat",
|
||||||
|
ATTR_THUMBNAIL: "k",
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRS_TO_PLAY_ON_ROKU_AUDIO_PARAMS = {
|
||||||
|
ATTR_NAME: "songName",
|
||||||
|
ATTR_FORMAT: "songFormat",
|
||||||
|
ATTR_ARTIST_NAME: "artistName",
|
||||||
|
ATTR_THUMBNAIL: "albumArtUrl",
|
||||||
}
|
}
|
||||||
|
|
||||||
SEARCH_SCHEMA = {vol.Required(ATTR_KEYWORD): str}
|
SEARCH_SCHEMA = {vol.Required(ATTR_KEYWORD): str}
|
||||||
|
@ -366,26 +388,68 @@ class RokuMediaPlayer(RokuEntity, MediaPlayerEntity):
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Play media from a URL or file, launch an application, or tune to a channel."""
|
"""Play media from a URL or file, launch an application, or tune to a channel."""
|
||||||
extra: dict[str, Any] = kwargs.get(ATTR_MEDIA_EXTRA) or {}
|
extra: dict[str, Any] = kwargs.get(ATTR_MEDIA_EXTRA) or {}
|
||||||
|
original_media_type: str = media_type
|
||||||
|
original_media_id: str = media_id
|
||||||
|
mime_type: str | None = None
|
||||||
|
stream_name: str | None = None
|
||||||
|
stream_format: str | None = extra.get(ATTR_FORMAT)
|
||||||
|
|
||||||
# Handle media_source
|
# Handle media_source
|
||||||
if media_source.is_media_source_id(media_id):
|
if media_source.is_media_source_id(media_id):
|
||||||
sourced_media = await media_source.async_resolve_media(self.hass, media_id)
|
sourced_media = await media_source.async_resolve_media(self.hass, media_id)
|
||||||
media_type = MEDIA_TYPE_URL
|
media_type = MEDIA_TYPE_URL
|
||||||
media_id = sourced_media.url
|
media_id = sourced_media.url
|
||||||
|
mime_type = sourced_media.mime_type
|
||||||
|
stream_name = original_media_id
|
||||||
|
stream_format = guess_stream_format(media_id, mime_type)
|
||||||
|
|
||||||
# If media ID is a relative URL, we serve it from HA.
|
# If media ID is a relative URL, we serve it from HA.
|
||||||
media_id = async_process_play_media_url(self.hass, media_id)
|
media_id = async_process_play_media_url(self.hass, media_id)
|
||||||
|
|
||||||
if media_type not in PLAY_MEDIA_SUPPORTED_TYPES:
|
if media_type == FORMAT_CONTENT_TYPE[HLS_PROVIDER]:
|
||||||
|
media_type = MEDIA_TYPE_VIDEO
|
||||||
|
mime_type = FORMAT_CONTENT_TYPE[HLS_PROVIDER]
|
||||||
|
stream_name = "Camera Stream"
|
||||||
|
stream_format = "hls"
|
||||||
|
|
||||||
|
if media_type in (MEDIA_TYPE_MUSIC, MEDIA_TYPE_URL, MEDIA_TYPE_VIDEO):
|
||||||
|
parsed = yarl.URL(media_id)
|
||||||
|
|
||||||
|
if mime_type is None:
|
||||||
|
mime_type, _ = mimetypes.guess_type(parsed.path)
|
||||||
|
|
||||||
|
if stream_format is None:
|
||||||
|
stream_format = guess_stream_format(media_id, mime_type)
|
||||||
|
|
||||||
|
if extra.get(ATTR_FORMAT) is None:
|
||||||
|
extra[ATTR_FORMAT] = stream_format
|
||||||
|
|
||||||
|
if extra[ATTR_FORMAT] not in STREAM_FORMAT_TO_MEDIA_TYPE:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Invalid media type %s. Only %s, %s, %s, and camera HLS streams are supported",
|
"Media type %s is not supported with format %s (mime: %s)",
|
||||||
media_type,
|
original_media_type,
|
||||||
MEDIA_TYPE_APP,
|
extra[ATTR_FORMAT],
|
||||||
MEDIA_TYPE_CHANNEL,
|
mime_type,
|
||||||
MEDIA_TYPE_URL,
|
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if (
|
||||||
|
media_type == MEDIA_TYPE_URL
|
||||||
|
and STREAM_FORMAT_TO_MEDIA_TYPE[extra[ATTR_FORMAT]] == MEDIA_TYPE_MUSIC
|
||||||
|
):
|
||||||
|
media_type = MEDIA_TYPE_MUSIC
|
||||||
|
|
||||||
|
if media_type == MEDIA_TYPE_MUSIC and "tts_proxy" in media_id:
|
||||||
|
stream_name = "Text to Speech"
|
||||||
|
elif stream_name is None:
|
||||||
|
if stream_format == "ism":
|
||||||
|
stream_name = parsed.parts[-2]
|
||||||
|
else:
|
||||||
|
stream_name = parsed.name
|
||||||
|
|
||||||
|
if extra.get(ATTR_NAME) is None:
|
||||||
|
extra[ATTR_NAME] = stream_name
|
||||||
|
|
||||||
if media_type == MEDIA_TYPE_APP:
|
if media_type == MEDIA_TYPE_APP:
|
||||||
params = {
|
params = {
|
||||||
param: extra[attr]
|
param: extra[attr]
|
||||||
|
@ -396,20 +460,30 @@ class RokuMediaPlayer(RokuEntity, MediaPlayerEntity):
|
||||||
await self.coordinator.roku.launch(media_id, params)
|
await self.coordinator.roku.launch(media_id, params)
|
||||||
elif media_type == MEDIA_TYPE_CHANNEL:
|
elif media_type == MEDIA_TYPE_CHANNEL:
|
||||||
await self.coordinator.roku.tune(media_id)
|
await self.coordinator.roku.tune(media_id)
|
||||||
elif media_type == MEDIA_TYPE_URL:
|
elif media_type == MEDIA_TYPE_MUSIC:
|
||||||
|
if extra.get(ATTR_ARTIST_NAME) is None:
|
||||||
|
extra[ATTR_ARTIST_NAME] = "Home Assistant"
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
param: extra[attr]
|
param: extra[attr]
|
||||||
for (attr, param) in ATTRS_TO_PLAY_VIDEO_PARAMS.items()
|
for (attr, param) in ATTRS_TO_PLAY_ON_ROKU_AUDIO_PARAMS.items()
|
||||||
|
if attr in extra
|
||||||
|
}
|
||||||
|
|
||||||
|
params = {"t": "a", **params}
|
||||||
|
|
||||||
|
await self.coordinator.roku.play_on_roku(media_id, params)
|
||||||
|
elif media_type in (MEDIA_TYPE_URL, MEDIA_TYPE_VIDEO):
|
||||||
|
params = {
|
||||||
|
param: extra[attr]
|
||||||
|
for (attr, param) in ATTRS_TO_PLAY_ON_ROKU_PARAMS.items()
|
||||||
if attr in extra
|
if attr in extra
|
||||||
}
|
}
|
||||||
|
|
||||||
await self.coordinator.roku.play_on_roku(media_id, params)
|
await self.coordinator.roku.play_on_roku(media_id, params)
|
||||||
elif media_type == FORMAT_CONTENT_TYPE[HLS_PROVIDER]:
|
else:
|
||||||
params = {
|
_LOGGER.error("Media type %s is not supported", original_media_type)
|
||||||
"MediaType": "hls",
|
return
|
||||||
}
|
|
||||||
|
|
||||||
await self.coordinator.roku.play_on_roku(media_id, params)
|
|
||||||
|
|
||||||
await self.coordinator.async_request_refresh()
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
|
|
|
@ -2111,7 +2111,7 @@ rjpl==0.3.6
|
||||||
rocketchat-API==0.6.1
|
rocketchat-API==0.6.1
|
||||||
|
|
||||||
# homeassistant.components.roku
|
# homeassistant.components.roku
|
||||||
rokuecp==0.13.2
|
rokuecp==0.14.0
|
||||||
|
|
||||||
# homeassistant.components.roomba
|
# homeassistant.components.roomba
|
||||||
roombapy==1.6.5
|
roombapy==1.6.5
|
||||||
|
|
|
@ -1306,7 +1306,7 @@ rflink==0.0.62
|
||||||
ring_doorbell==0.7.2
|
ring_doorbell==0.7.2
|
||||||
|
|
||||||
# homeassistant.components.roku
|
# homeassistant.components.roku
|
||||||
rokuecp==0.13.2
|
rokuecp==0.14.0
|
||||||
|
|
||||||
# homeassistant.components.roomba
|
# homeassistant.components.roomba
|
||||||
roombapy==1.6.5
|
roombapy==1.6.5
|
||||||
|
|
|
@ -27,7 +27,9 @@ from homeassistant.components.media_player.const import (
|
||||||
MEDIA_TYPE_APPS,
|
MEDIA_TYPE_APPS,
|
||||||
MEDIA_TYPE_CHANNEL,
|
MEDIA_TYPE_CHANNEL,
|
||||||
MEDIA_TYPE_CHANNELS,
|
MEDIA_TYPE_CHANNELS,
|
||||||
|
MEDIA_TYPE_MUSIC,
|
||||||
MEDIA_TYPE_URL,
|
MEDIA_TYPE_URL,
|
||||||
|
MEDIA_TYPE_VIDEO,
|
||||||
SERVICE_PLAY_MEDIA,
|
SERVICE_PLAY_MEDIA,
|
||||||
SERVICE_SELECT_SOURCE,
|
SERVICE_SELECT_SOURCE,
|
||||||
SUPPORT_BROWSE_MEDIA,
|
SUPPORT_BROWSE_MEDIA,
|
||||||
|
@ -459,50 +461,7 @@ async def test_services(
|
||||||
"291097",
|
"291097",
|
||||||
{
|
{
|
||||||
"contentID": "8e06a8b7-d667-4e31-939d-f40a6dd78a88",
|
"contentID": "8e06a8b7-d667-4e31-939d-f40a6dd78a88",
|
||||||
"MediaType": "movie",
|
"mediaType": "movie",
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
|
||||||
MP_DOMAIN,
|
|
||||||
SERVICE_PLAY_MEDIA,
|
|
||||||
{
|
|
||||||
ATTR_ENTITY_ID: MAIN_ENTITY_ID,
|
|
||||||
ATTR_MEDIA_CONTENT_TYPE: MEDIA_TYPE_URL,
|
|
||||||
ATTR_MEDIA_CONTENT_ID: "https://awesome.tld/media.mp4",
|
|
||||||
ATTR_MEDIA_EXTRA: {
|
|
||||||
ATTR_NAME: "Sent from HA",
|
|
||||||
ATTR_FORMAT: "mp4",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert mock_roku.play_on_roku.call_count == 1
|
|
||||||
mock_roku.play_on_roku.assert_called_with(
|
|
||||||
"https://awesome.tld/media.mp4",
|
|
||||||
{
|
|
||||||
"videoName": "Sent from HA",
|
|
||||||
"videoFormat": "mp4",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
|
||||||
MP_DOMAIN,
|
|
||||||
SERVICE_PLAY_MEDIA,
|
|
||||||
{
|
|
||||||
ATTR_ENTITY_ID: MAIN_ENTITY_ID,
|
|
||||||
ATTR_MEDIA_CONTENT_TYPE: FORMAT_CONTENT_TYPE[HLS_PROVIDER],
|
|
||||||
ATTR_MEDIA_CONTENT_ID: "https://awesome.tld/api/hls/api_token/master_playlist.m3u8",
|
|
||||||
},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert mock_roku.play_on_roku.call_count == 2
|
|
||||||
mock_roku.play_on_roku.assert_called_with(
|
|
||||||
"https://awesome.tld/api/hls/api_token/master_playlist.m3u8",
|
|
||||||
{
|
|
||||||
"MediaType": "hls",
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -527,6 +486,158 @@ async def test_services(
|
||||||
mock_roku.launch.assert_called_with("12")
|
mock_roku.launch.assert_called_with("12")
|
||||||
|
|
||||||
|
|
||||||
|
async def test_services_play_media(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
init_integration: MockConfigEntry,
|
||||||
|
mock_roku: MagicMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test the media player services related to playing media."""
|
||||||
|
await hass.services.async_call(
|
||||||
|
MP_DOMAIN,
|
||||||
|
SERVICE_PLAY_MEDIA,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: MAIN_ENTITY_ID,
|
||||||
|
ATTR_MEDIA_CONTENT_TYPE: "blah",
|
||||||
|
ATTR_MEDIA_CONTENT_ID: "https://localhost/media.m4a",
|
||||||
|
ATTR_MEDIA_EXTRA: {
|
||||||
|
ATTR_NAME: "Test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert mock_roku.play_on_roku.call_count == 0
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
MP_DOMAIN,
|
||||||
|
SERVICE_PLAY_MEDIA,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: MAIN_ENTITY_ID,
|
||||||
|
ATTR_MEDIA_CONTENT_TYPE: MEDIA_TYPE_MUSIC,
|
||||||
|
ATTR_MEDIA_CONTENT_ID: "https://localhost/media.m4a",
|
||||||
|
ATTR_MEDIA_EXTRA: {ATTR_FORMAT: "blah"},
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert mock_roku.play_on_roku.call_count == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"content_type, content_id, resolved_name, resolved_format",
|
||||||
|
[
|
||||||
|
(MEDIA_TYPE_URL, "http://localhost/media.m4a", "media.m4a", "m4a"),
|
||||||
|
(MEDIA_TYPE_MUSIC, "http://localhost/media.m4a", "media.m4a", "m4a"),
|
||||||
|
(MEDIA_TYPE_MUSIC, "http://localhost/media.mka", "media.mka", "mka"),
|
||||||
|
(
|
||||||
|
MEDIA_TYPE_MUSIC,
|
||||||
|
"http://localhost/api/tts_proxy/generated.mp3",
|
||||||
|
"Text to Speech",
|
||||||
|
"mp3",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_services_play_media_audio(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
init_integration: MockConfigEntry,
|
||||||
|
mock_roku: MagicMock,
|
||||||
|
content_type: str,
|
||||||
|
content_id: str,
|
||||||
|
resolved_name: str,
|
||||||
|
resolved_format: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test the media player services related to playing media."""
|
||||||
|
await hass.services.async_call(
|
||||||
|
MP_DOMAIN,
|
||||||
|
SERVICE_PLAY_MEDIA,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: MAIN_ENTITY_ID,
|
||||||
|
ATTR_MEDIA_CONTENT_TYPE: content_type,
|
||||||
|
ATTR_MEDIA_CONTENT_ID: content_id,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_roku.play_on_roku.assert_called_once_with(
|
||||||
|
content_id,
|
||||||
|
{
|
||||||
|
"t": "a",
|
||||||
|
"songName": resolved_name,
|
||||||
|
"songFormat": resolved_format,
|
||||||
|
"artistName": "Home Assistant",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"content_type, content_id, resolved_name, resolved_format",
|
||||||
|
[
|
||||||
|
(MEDIA_TYPE_URL, "http://localhost/media.mp4", "media.mp4", "mp4"),
|
||||||
|
(MEDIA_TYPE_VIDEO, "http://localhost/media.m4v", "media.m4v", "mp4"),
|
||||||
|
(MEDIA_TYPE_VIDEO, "http://localhost/media.mov", "media.mov", "mp4"),
|
||||||
|
(MEDIA_TYPE_VIDEO, "http://localhost/media.mkv", "media.mkv", "mkv"),
|
||||||
|
(MEDIA_TYPE_VIDEO, "http://localhost/media.mks", "media.mks", "mks"),
|
||||||
|
(MEDIA_TYPE_VIDEO, "http://localhost/media.m3u8", "media.m3u8", "hls"),
|
||||||
|
(MEDIA_TYPE_VIDEO, "http://localhost/media.dash", "media.dash", "dash"),
|
||||||
|
(MEDIA_TYPE_VIDEO, "http://localhost/media.mpd", "media.mpd", "dash"),
|
||||||
|
(MEDIA_TYPE_VIDEO, "http://localhost/media.ism/manifest", "media.ism", "ism"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_services_play_media_video(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
init_integration: MockConfigEntry,
|
||||||
|
mock_roku: MagicMock,
|
||||||
|
content_type: str,
|
||||||
|
content_id: str,
|
||||||
|
resolved_name: str,
|
||||||
|
resolved_format: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test the media player services related to playing media."""
|
||||||
|
await hass.services.async_call(
|
||||||
|
MP_DOMAIN,
|
||||||
|
SERVICE_PLAY_MEDIA,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: MAIN_ENTITY_ID,
|
||||||
|
ATTR_MEDIA_CONTENT_TYPE: content_type,
|
||||||
|
ATTR_MEDIA_CONTENT_ID: content_id,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_roku.play_on_roku.assert_called_once_with(
|
||||||
|
content_id,
|
||||||
|
{
|
||||||
|
"videoName": resolved_name,
|
||||||
|
"videoFormat": resolved_format,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_services_camera_play_stream(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
init_integration: MockConfigEntry,
|
||||||
|
mock_roku: MagicMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test the media player services related to playing camera stream."""
|
||||||
|
await hass.services.async_call(
|
||||||
|
MP_DOMAIN,
|
||||||
|
SERVICE_PLAY_MEDIA,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: MAIN_ENTITY_ID,
|
||||||
|
ATTR_MEDIA_CONTENT_TYPE: FORMAT_CONTENT_TYPE[HLS_PROVIDER],
|
||||||
|
ATTR_MEDIA_CONTENT_ID: "https://awesome.tld/api/hls/api_token/master_playlist.m3u8",
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert mock_roku.play_on_roku.call_count == 1
|
||||||
|
mock_roku.play_on_roku.assert_called_with(
|
||||||
|
"https://awesome.tld/api/hls/api_token/master_playlist.m3u8",
|
||||||
|
{
|
||||||
|
"videoName": "Camera Stream",
|
||||||
|
"videoFormat": "hls",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_services_play_media_local_source(
|
async def test_services_play_media_local_source(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
init_integration: MockConfigEntry,
|
init_integration: MockConfigEntry,
|
||||||
|
@ -556,7 +667,11 @@ async def test_services_play_media_local_source(
|
||||||
assert mock_roku.play_on_roku.call_count == 1
|
assert mock_roku.play_on_roku.call_count == 1
|
||||||
assert mock_roku.play_on_roku.call_args
|
assert mock_roku.play_on_roku.call_args
|
||||||
call_args = mock_roku.play_on_roku.call_args.args
|
call_args = mock_roku.play_on_roku.call_args.args
|
||||||
assert "/media/local/Epic%20Sax%20Guy%2010%20Hours.mp4?authSig=" in call_args[0]
|
assert "/local/Epic%20Sax%20Guy%2010%20Hours.mp4?authSig=" in call_args[0]
|
||||||
|
assert call_args[1] == {
|
||||||
|
"videoFormat": "mp4",
|
||||||
|
"videoName": "media-source://media_source/local/Epic Sax Guy 10 Hours.mp4",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mock_device", ["roku/rokutv-7820x.json"], indirect=True)
|
@pytest.mark.parametrize("mock_device", ["roku/rokutv-7820x.json"], indirect=True)
|
||||||
|
|
Loading…
Add table
Reference in a new issue