From fec8c2ab822c824837cbe7dc640ab064c4248822 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 14 Feb 2022 08:04:19 -0800 Subject: [PATCH] Add support for MJPEG cameras to camera media source (#66499) --- .../components/camera/media_source.py | 21 ++++++++--- tests/components/camera/test_media_source.py | 35 ++++++++++++++----- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/camera/media_source.py b/homeassistant/components/camera/media_source.py index 841a9365320..b0161a58251 100644 --- a/homeassistant/components/camera/media_source.py +++ b/homeassistant/components/camera/media_source.py @@ -47,8 +47,18 @@ class CameraMediaSource(MediaSource): if not camera: raise Unresolvable(f"Could not resolve media item: {item.identifier}") + stream_type = camera.frontend_stream_type + + if stream_type is None: + return PlayMedia( + f"/api/camera_proxy_stream/{camera.entity_id}", camera.content_type + ) + if camera.frontend_stream_type != STREAM_TYPE_HLS: - raise Unresolvable("Camera does not support HLS streaming.") + raise Unresolvable("Camera does not support MJPEG or HLS streaming.") + + if "stream" not in self.hass.config.components: + raise Unresolvable("Stream integration not loaded") try: url = await _async_stream_endpoint_url(self.hass, camera, HLS_PROVIDER) @@ -65,16 +75,19 @@ class CameraMediaSource(MediaSource): if item.identifier: raise BrowseError("Unknown item") - if "stream" not in self.hass.config.components: - raise BrowseError("Stream integration is not loaded") + supported_stream_types: list[str | None] = [None] + + if "stream" in self.hass.config.components: + supported_stream_types.append(STREAM_TYPE_HLS) # Root. List cameras. component: EntityComponent = self.hass.data[DOMAIN] children = [] for camera in component.entities: camera = cast(Camera, camera) + stream_type = camera.frontend_stream_type - if camera.frontend_stream_type != STREAM_TYPE_HLS: + if stream_type not in supported_stream_types: continue children.append( diff --git a/tests/components/camera/test_media_source.py b/tests/components/camera/test_media_source.py index d5d65296e65..3a3558419e5 100644 --- a/tests/components/camera/test_media_source.py +++ b/tests/components/camera/test_media_source.py @@ -15,17 +15,17 @@ async def setup_media_source(hass): assert await async_setup_component(hass, "media_source", {}) -@pytest.fixture(autouse=True) -async def mock_stream(hass): - """Mock stream.""" - hass.config.components.add("stream") - - async def test_browsing(hass, mock_camera_hls): """Test browsing camera media source.""" item = await media_source.async_browse_media(hass, "media-source://camera") assert item is not None assert item.title == "Camera" + assert len(item.children) == 0 + + # Adding stream enables HLS camera + hass.config.components.add("stream") + + item = await media_source.async_browse_media(hass, "media-source://camera") assert len(item.children) == 2 @@ -39,6 +39,9 @@ async def test_browsing_filter_non_hls(hass, mock_camera_web_rtc): async def test_resolving(hass, mock_camera_hls): """Test resolving.""" + # Adding stream enables HLS camera + hass.config.components.add("stream") + with patch( "homeassistant.components.camera.media_source._async_stream_endpoint_url", return_value="http://example.com/stream", @@ -53,20 +56,34 @@ async def test_resolving(hass, mock_camera_hls): async def test_resolving_errors(hass, mock_camera_hls): """Test resolving.""" - with pytest.raises(media_source.Unresolvable): + + with pytest.raises(media_source.Unresolvable) as exc_info: + await media_source.async_resolve_media( + hass, "media-source://camera/camera.demo_camera" + ) + assert str(exc_info.value) == "Stream integration not loaded" + + hass.config.components.add("stream") + + with pytest.raises(media_source.Unresolvable) as exc_info: await media_source.async_resolve_media( hass, "media-source://camera/camera.non_existing" ) + assert str(exc_info.value) == "Could not resolve media item: camera.non_existing" - with pytest.raises(media_source.Unresolvable), patch( + with pytest.raises(media_source.Unresolvable) as exc_info, patch( "homeassistant.components.camera.Camera.frontend_stream_type", new_callable=PropertyMock(return_value=STREAM_TYPE_WEB_RTC), ): await media_source.async_resolve_media( hass, "media-source://camera/camera.demo_camera" ) + assert str(exc_info.value) == "Camera does not support MJPEG or HLS streaming." - with pytest.raises(media_source.Unresolvable): + with pytest.raises(media_source.Unresolvable) as exc_info: await media_source.async_resolve_media( hass, "media-source://camera/camera.demo_camera" ) + assert ( + str(exc_info.value) == "camera.demo_camera does not support play stream service" + )