Require passing target player when resolving media (#72593)
parent
b6575aa66b
commit
f76afffd5a
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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, {})
|
||||
|
|
Loading…
Reference in New Issue