Require passing target player when resolving media (#72593)

pull/72597/head
Erik Montnemery 2022-05-27 17:40:55 +02:00 committed by GitHub
parent b6575aa66b
commit f76afffd5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 46 additions and 15 deletions

View File

@ -18,10 +18,11 @@ from homeassistant.components.media_player.browse_media import (
)
from homeassistant.components.websocket_api import ActiveConnection
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.frame import report
from homeassistant.helpers.integration_platform import (
async_process_integration_platforms,
)
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.typing import UNDEFINED, ConfigType, UndefinedType
from homeassistant.loader import bind_hass
from . import local_source
@ -80,15 +81,15 @@ async def _process_media_source_platform(
@callback
def _get_media_item(
hass: HomeAssistant, media_content_id: str | None
hass: HomeAssistant, media_content_id: str | None, target_media_player: str | None
) -> MediaSourceItem:
"""Return media item."""
if media_content_id:
item = MediaSourceItem.from_uri(hass, media_content_id)
item = MediaSourceItem.from_uri(hass, media_content_id, target_media_player)
else:
# We default to our own domain if its only one registered
domain = None if len(hass.data[DOMAIN]) > 1 else DOMAIN
return MediaSourceItem(hass, domain, "")
return MediaSourceItem(hass, domain, "", target_media_player)
if item.domain is not None and item.domain not in hass.data[DOMAIN]:
raise ValueError("Unknown media source")
@ -108,7 +109,7 @@ async def async_browse_media(
raise BrowseError("Media Source not loaded")
try:
item = await _get_media_item(hass, media_content_id).async_browse()
item = await _get_media_item(hass, media_content_id, None).async_browse()
except ValueError as err:
raise BrowseError(str(err)) from err
@ -124,13 +125,21 @@ async def async_browse_media(
@bind_hass
async def async_resolve_media(hass: HomeAssistant, media_content_id: str) -> PlayMedia:
async def async_resolve_media(
hass: HomeAssistant,
media_content_id: str,
target_media_player: str | None | UndefinedType = UNDEFINED,
) -> PlayMedia:
"""Get info to play media."""
if DOMAIN not in hass.data:
raise Unresolvable("Media Source not loaded")
if target_media_player is UNDEFINED:
report("calls media_source.async_resolve_media without passing an entity_id")
target_media_player = None
try:
item = _get_media_item(hass, media_content_id)
item = _get_media_item(hass, media_content_id, target_media_player)
except ValueError as err:
raise Unresolvable(str(err)) from err

View File

@ -264,7 +264,7 @@ class UploadMediaView(http.HomeAssistantView):
raise web.HTTPBadRequest() from err
try:
item = MediaSourceItem.from_uri(self.hass, data["media_content_id"])
item = MediaSourceItem.from_uri(self.hass, data["media_content_id"], None)
except ValueError as err:
LOGGER.error("Received invalid upload data: %s", err)
raise web.HTTPBadRequest() from err
@ -328,7 +328,7 @@ async def websocket_remove_media(
) -> None:
"""Remove media."""
try:
item = MediaSourceItem.from_uri(hass, msg["media_content_id"])
item = MediaSourceItem.from_uri(hass, msg["media_content_id"], None)
except ValueError as err:
connection.send_error(msg["id"], websocket_api.ERR_INVALID_FORMAT, str(err))
return

View File

@ -50,6 +50,7 @@ class MediaSourceItem:
hass: HomeAssistant
domain: str | None
identifier: str
target_media_player: str | None
async def async_browse(self) -> BrowseMediaSource:
"""Browse this item."""
@ -94,7 +95,9 @@ class MediaSourceItem:
return cast(MediaSource, self.hass.data[DOMAIN][self.domain])
@classmethod
def from_uri(cls, hass: HomeAssistant, uri: str) -> MediaSourceItem:
def from_uri(
cls, hass: HomeAssistant, uri: str, target_media_player: str | None
) -> MediaSourceItem:
"""Create an item from a uri."""
if not (match := URI_SCHEME_REGEX.match(uri)):
raise ValueError("Invalid media source URI")
@ -102,7 +105,7 @@ class MediaSourceItem:
domain = match.group("domain")
identifier = match.group("identifier")
return cls(hass, domain, identifier)
return cls(hass, domain, identifier, target_media_player)
class MediaSource(ABC):

View File

@ -49,7 +49,7 @@ async def test_get_media_source(hass: HomeAssistant) -> None:
async def test_resolve_media_unconfigured(hass: HomeAssistant) -> None:
"""Test resolve_media without any devices being configured."""
source = DmsMediaSource(hass)
item = MediaSourceItem(hass, DOMAIN, "source_id/media_id")
item = MediaSourceItem(hass, DOMAIN, "source_id/media_id", None)
with pytest.raises(Unresolvable, match="No sources have been configured"):
await source.async_resolve_media(item)
@ -116,11 +116,11 @@ async def test_resolve_media_success(
async def test_browse_media_unconfigured(hass: HomeAssistant) -> None:
"""Test browse_media without any devices being configured."""
source = DmsMediaSource(hass)
item = MediaSourceItem(hass, DOMAIN, "source_id/media_id")
item = MediaSourceItem(hass, DOMAIN, "source_id/media_id", None)
with pytest.raises(BrowseError, match="No sources have been configured"):
await source.async_browse_media(item)
item = MediaSourceItem(hass, DOMAIN, "")
item = MediaSourceItem(hass, DOMAIN, "", None)
with pytest.raises(BrowseError, match="No sources have been configured"):
await source.async_browse_media(item)
@ -239,7 +239,7 @@ async def test_browse_media_source_id(
dms_device_mock.async_browse_metadata.side_effect = UpnpError
# Browse by source_id
item = MediaSourceItem(hass, DOMAIN, f"{MOCK_SOURCE_ID}/:media-item-id")
item = MediaSourceItem(hass, DOMAIN, f"{MOCK_SOURCE_ID}/:media-item-id", None)
dms_source = DmsMediaSource(hass)
with pytest.raises(BrowseError):
await dms_source.async_browse_media(item)

View File

@ -109,6 +109,25 @@ async def test_async_resolve_media(hass):
assert media.mime_type == "audio/mpeg"
@patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set())
async def test_async_resolve_media_no_entity(hass, caplog):
"""Test browse media."""
assert await async_setup_component(hass, media_source.DOMAIN, {})
await hass.async_block_till_done()
media = await media_source.async_resolve_media(
hass,
media_source.generate_media_source_id(media_source.DOMAIN, "local/test.mp3"),
)
assert isinstance(media, media_source.models.PlayMedia)
assert media.url == "/media/local/test.mp3"
assert media.mime_type == "audio/mpeg"
assert (
"calls media_source.async_resolve_media without passing an entity_id"
in caplog.text
)
async def test_async_unresolve_media(hass):
"""Test browse media."""
assert await async_setup_component(hass, media_source.DOMAIN, {})