Ejemplo n.º 1
0
    async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
        """Return a streamable URL and associated mime type for a UniFi Protect event.

        Accepted identifier format are

        * {nvr_id}:event:{event_id} - MP4 video clip for specific event
        * {nvr_id}:eventthumb:{event_id} - Thumbnail JPEG for specific event
        """

        parts = item.identifier.split(":")
        if len(parts) != 3 or parts[1] not in ("event", "eventthumb"):
            return _bad_identifier_media(item.identifier)

        thumbnail_only = parts[1] == "eventthumb"
        try:
            data = self.data_sources[parts[0]]
        except (KeyError, IndexError) as err:
            return _bad_identifier_media(item.identifier, err)

        event = data.api.bootstrap.events.get(parts[2])
        if event is None:
            try:
                event = await data.api.get_event(parts[2])
            except NvrError as err:
                return _bad_identifier_media(item.identifier, err)
            else:
                # cache the event for later
                data.api.bootstrap.events[event.id] = event

        nvr = data.api.bootstrap.nvr
        if thumbnail_only:
            return PlayMedia(async_generate_thumbnail_url(event.id, nvr.id),
                             "image/jpeg")
        return PlayMedia(async_generate_event_video_url(event), "video/mp4")
Ejemplo n.º 2
0
async def test_async_resolve_media_success(hass: HomeAssistant) -> None:
    """Test successful resolve media."""

    client = create_mock_motioneye_client()

    config = await setup_mock_motioneye_config_entry(hass, client=client)

    device_registry = dr.async_get(hass)
    device = device_registry.async_get_or_create(
        config_entry_id=config.entry_id,
        identifiers={TEST_CAMERA_DEVICE_IDENTIFIER},
    )

    # Test successful resolve for a movie.
    client.get_movie_url = Mock(return_value="http://movie-url")
    media = await media_source.async_resolve_media(
        hass,
        (f"{const.URI_SCHEME}{DOMAIN}"
         f"/{TEST_CONFIG_ENTRY_ID}#{device.id}#movies#/foo.mp4"),
        None,
    )
    assert media == PlayMedia(url="http://movie-url", mime_type="video/mp4")
    assert client.get_movie_url.call_args == call(TEST_CAMERA_ID, "/foo.mp4")

    # Test successful resolve for an image.
    client.get_image_url = Mock(return_value="http://image-url")
    media = await media_source.async_resolve_media(
        hass,
        (f"{const.URI_SCHEME}{DOMAIN}"
         f"/{TEST_CONFIG_ENTRY_ID}#{device.id}#images#/foo.jpg"),
        None,
    )
    assert media == PlayMedia(url="http://image-url", mime_type="image/jpeg")
    assert client.get_image_url.call_args == call(TEST_CAMERA_ID, "/foo.jpg")
 async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
     """Resolve media to a url."""
     media = await async_parse_uri(item.identifier)
     for mass_instance in self.hass.data[DOMAIN].values():
         if mass_instance.server_id != media["mass_server_id"]:
             continue
         if media["media_type"] in ["track", "radio"]:
             url = f"{mass_instance.base_url}/stream_media/"
             url += f'{media["media_type"]}/{media["provider"]}/{media["item_id"]}'
             return PlayMedia(url, "audio/flac")
         else:
             return PlayMedia(item.identifier, "application/musicassistant")
     raise BrowseError("Invalid Music Assistance instance")
Ejemplo n.º 4
0
    async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
        """Resolve media to a url."""
        parsed = URL(item.identifier)
        if "message" not in parsed.query:
            raise Unresolvable("No message specified.")

        options = dict(parsed.query)
        kwargs: dict[str, Any] = {
            "engine": parsed.name,
            "message": options.pop("message"),
            "language": options.pop("language", None),
            "options": options,
        }
        if "cache" in options:
            kwargs["cache"] = options.pop("cache") == "true"

        manager: SpeechManager = self.hass.data[DOMAIN]

        try:
            url = await manager.async_get_url_path(**kwargs)
        except HomeAssistantError as err:
            raise Unresolvable(str(err)) from err

        mime_type = mimetypes.guess_type(url)[0] or "audio/mpeg"

        if manager.base_url and manager.base_url != get_url(self.hass):
            url = f"{manager.base_url}{url}"

        return PlayMedia(url, mime_type)
Ejemplo n.º 5
0
    async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
        """Resolve media to a url."""
        parsed = URL(item.identifier)
        if "message" not in parsed.query:
            raise Unresolvable("No message specified.")

        options = dict(parsed.query)
        kwargs = {
            "engine": parsed.name,
            "message": options.pop("message"),
            "language": options.pop("language", None),
            "options": options,
        }

        manager: SpeechManager = self.hass.data[DOMAIN]

        try:
            url = await manager.async_get_url_path(**kwargs
                                                   )  # type: ignore[arg-type]
        except HomeAssistantError as err:
            raise Unresolvable(str(err)) from err

        mime_type = mimetypes.guess_type(url)[0] or "audio/mpeg"

        return PlayMedia(url, mime_type)
Ejemplo n.º 6
0
class RadioMediaSource(MediaSource):
    """Provide Radio stations as media sources."""

    name = "Radio Browser"

    def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
        """Initialize CameraMediaSource."""
        super().__init__(DOMAIN)
        self.hass = hass
        self.entry = entry

    @property
    def radios(self) -> RadioBrowser | None:
        """Return the radio browser."""
        return self.hass.data.get(DOMAIN)

    async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
        """Resolve selected Radio station to a streaming URL."""
        radios = self.radios

        if radios is None:
            raise Unresolvable("Radio Browser not initialized")

        station = await radios.station(uuid=item.identifier)
        if not station:
            raise Unresolvable("Radio station is no longer available")

        if not (mime_type := self._async_get_station_mime_type(station)):
            raise Unresolvable(
                "Could not determine stream type of radio station")

        # Register "click" with Radio Browser
        await radios.station_click(uuid=station.uuid)

        return PlayMedia(station.url, mime_type)
    async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
        """Resolve a media item to a playable item."""
        _, camera_id, event_id = async_parse_identifier(item)

        data: dict = self.hass.data[self.domain]
        entry: dict = data.get(camera_id) if camera_id else None
        base: ReolinkBase = entry.get(BASE) if entry else None
        if not base:
            raise BrowseError("Camera does not exist.")

        file = unquote_plus(event_id)
        if not file:
            raise BrowseError("Event does not exist.")
        _LOGGER.debug("file = %s", file)

        url = await base.api.get_vod_source(file)
        _LOGGER.debug("Load VOD %s", url)
        stream = create_stream(self.hass, url)
        stream.add_provider("hls", timeout=3600)
        url: str = stream.endpoint_url("hls")
        # the media browser seems to have a problem with the master_playlist
        # ( it does not load the referenced playlist ) so we will just
        # force the reference playlist instead, this seems to work
        # though technically wrong
        url = url.replace("master_", "")
        _LOGGER.debug("Proxy %s", url)
        return PlayMedia(url, MIME_TYPE)
Ejemplo n.º 8
0
    async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
        """Resolve media to a url."""
        config_id, device_id, kind, path = self._parse_identifier(
            item.identifier)

        if not config_id or not device_id or not kind or not path:
            raise Unresolvable(
                f"Incomplete media identifier specified: {item.identifier}")

        config = self._get_config_or_raise(config_id)
        device = self._get_device_or_raise(device_id)
        camera_id = self._get_camera_id_or_raise(config, device)
        self._verify_kind_or_raise(kind)
        path = self._get_path_or_raise(path)

        client = self.hass.data[DOMAIN][config.entry_id][CONF_CLIENT]
        try:
            if kind == "movies":
                url = client.get_movie_url(camera_id, path)
            else:
                url = client.get_image_url(camera_id, path)
        except MotionEyeClientPathError as exc:
            raise Unresolvable from exc

        return PlayMedia(url, MIME_TYPE_MAP[kind])
Ejemplo n.º 9
0
    async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
        """Return a streamable URL and associated mime type."""
        media_item = await self.hass.async_add_executor_job(
            self.api.get_item, item.identifier)

        stream_url = self._get_stream_url(media_item)
        mime_type = _media_mime_type(media_item)

        return PlayMedia(stream_url, mime_type)
Ejemplo n.º 10
0
async def test_async_resolve_media(frigate_client: AsyncMock,
                                   hass: HomeAssistant) -> None:
    """Test successful resolve media."""

    await setup_mock_frigate_config_entry(hass, client=frigate_client)

    # Test resolving a clip.
    media = await media_source.async_resolve_media(
        hass,
        f"{const.URI_SCHEME}{DOMAIN}/{TEST_FRIGATE_INSTANCE_ID}/event/clips/camera/CLIP-FOO",
    )
    assert media == PlayMedia(
        url=
        f"/api/frigate/{TEST_FRIGATE_INSTANCE_ID}/vod/event/CLIP-FOO/index.m3u8",
        mime_type="application/x-mpegURL",
    )

    # Test resolving a recording.
    media = await media_source.async_resolve_media(
        hass,
        (f"{const.URI_SCHEME}{DOMAIN}/{TEST_FRIGATE_INSTANCE_ID}"
         "/recordings/2021-05/30/15/front_door/46.08.mp4"),
    )
    assert media == PlayMedia(
        url=
        (f"/api/frigate/{TEST_FRIGATE_INSTANCE_ID}/vod/2021-05/30/15/front_door/index.m3u8"
         ),
        mime_type="application/x-mpegURL",
    )

    # Test resolving a snapshot.
    media = await media_source.async_resolve_media(
        hass,
        (f"{const.URI_SCHEME}{DOMAIN}/{TEST_FRIGATE_INSTANCE_ID}"
         "/event/snapshots/camera/event_id"),
    )
    assert media == PlayMedia(
        url=f"/api/frigate/{TEST_FRIGATE_INSTANCE_ID}/snapshot/event_id",
        mime_type="image/jpg",
    )

    with pytest.raises(Unresolvable):
        media = await media_source.async_resolve_media(
            hass, f"{const.URI_SCHEME}{DOMAIN}/UNKNOWN")
Ejemplo n.º 11
0
    async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
        """Resolve a media item to a playable item."""
        autolog("<<<")

        if not item or not item.identifier:
            return None

        media_content_type, media_content_id = self.parse_mediasource_identifier(
            item.identifier)
        t = await self.jelly_cm.get_stream_url(media_content_id,
                                               media_content_type)
        return PlayMedia(t[0], t[1])
Ejemplo n.º 12
0
    async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
        """Resolve media to a url."""
        component: EntityComponent = self.hass.data[DOMAIN]
        camera = cast(Optional[Camera], component.get_entity(item.identifier))

        if not camera:
            raise Unresolvable(
                f"Could not resolve media item: {item.identifier}")

        if (stream_type := camera.frontend_stream_type) is None:
            return PlayMedia(f"/api/camera_proxy_stream/{camera.entity_id}",
                             camera.content_type)
Ejemplo n.º 13
0
 async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
     """Resolve media to a url."""
     identifier = Identifier.from_str(
         item.identifier,
         default_frigate_instance_id=self._get_default_frigate_instance_id(),
     )
     if identifier:
         server_path = identifier.get_integration_proxy_path()
         return PlayMedia(
             f"/api/frigate/{identifier.frigate_instance_id}/{server_path}",
             identifier.mime_type,
         )
     raise Unresolvable("Unknown identifier: %s" % item.identifier)
Ejemplo n.º 14
0
 async def async_resolve_media(
     self,
     item: MediaSourceItem,
 ) -> PlayMedia:
     """Resolve media to a url."""
     entry_id, path, mime_type = item.identifier.split("~~", 2)
     entry = self.hass.config_entries.async_get_entry(entry_id)
     if entry is None:
         raise ValueError("Invalid entry")
     path_split = path.split("/", 1)
     return PlayMedia(
         f"{_build_base_url(entry)}&base={path_split[0]}&path={path_split[1]}",
         mime_type,
     )
Ejemplo n.º 15
0
    async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
        """Resolve media to a url."""
        mass = self.get_mass()

        if mass is None:
            raise Unresolvable("MusicAssistant is not initialized")

        if item.target_media_player is None:
            # TODO: How to intercept a play request for the 'webbrowser' player
            # or at least hide our source for the webbrowser player ?
            raise Unresolvable("Playback not supported on the device.")

        # get/create mass player instance attached to this entity id
        player = await async_register_player_control(self.hass, mass,
                                                     item.target_media_player)
        if not player:
            return PlayMedia(item.identifier, MEDIA_TYPE_MUSIC)

        # send the mass library uri to the player(queue)
        await player.active_queue.play_media(item.identifier, passive=True)
        # tell the actual player to play the stream url
        content_type = player.active_queue.settings.stream_type.value
        return PlayMedia(player.active_queue.stream.url,
                         f"audio/{content_type}")
Ejemplo n.º 16
0
 async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
     """Resolve a media item to a playable item."""
     _, camera_id, event_id = async_parse_identifier(item)
     cache: typings.MediaSourceCacheEntry = self.cache[camera_id]
     event = cache["playback_events"][event_id]
     base: ReolinkBase = self.hass.data[DOMAIN][cache["entry_id"]][BASE]
     url = await base.api.get_vod_source(event["file"])
     _LOGGER.debug("Load VOD %s", url)
     stream = create_stream(self.hass, url)
     stream.add_provider("hls", timeout=600)
     url: str = stream.endpoint_url("hls")
     # the media browser seems to have a problem with the master_playlist
     # ( it does not load the referenced playlist ) so we will just
     # force the reference playlist instead, this seems to work
     # though technically wrong
     url = url.replace("master_", "")
     _LOGGER.debug("Proxy %s", url)
     return PlayMedia(url, MIME_TYPE)
Ejemplo n.º 17
0
class NestMediaSource(MediaSource):
    """Provide Nest Media Sources for Nest Cameras.

    The media source generates a directory tree of devices and media associated
    with events for each device (e.g. motion, person, etc). Each node in the
    tree has a unique MediaId.

    The lifecycle for event media is handled outside of NestMediaSource, and
    instead it just asks the device for all events it knows about.
    """

    name: str = MEDIA_SOURCE_TITLE

    def __init__(self, hass: HomeAssistant) -> None:
        """Initialize NestMediaSource."""
        super().__init__(DOMAIN)
        self.hass = hass

    async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
        """Resolve media identifier to a url."""
        media_id: MediaId | None = parse_media_id(item.identifier)
        if not media_id:
            raise Unresolvable("No identifier specified for MediaSourceItem")
        if not media_id.event_token:
            raise Unresolvable(
                "Identifier missing an event_token: %s" % item.identifier
            )
        devices = await self.devices()
        if not (device := devices.get(media_id.device_id)):
            raise Unresolvable(
                "Unable to find device with identifier: %s" % item.identifier
            )
        # Infer content type from the device, since it only supports one
        # snapshot type (either jpg or mp4 clip)
        content_type = EventImageType.IMAGE.content_type
        if CameraClipPreviewTrait.NAME in device.traits:
            content_type = EventImageType.CLIP_PREVIEW.content_type
        return PlayMedia(
            EVENT_MEDIA_API_URL_FORMAT.format(
                device_id=media_id.device_id, event_token=media_id.event_token
            ),
            content_type,
        )
Ejemplo n.º 18
0
class NestMediaSource(MediaSource):
    """Provide Nest Media Sources for Nest Cameras.

    The media source generates a directory tree of devices and media associated
    with events for each device (e.g. motion, person, etc). Each node in the
    tree has a unique MediaId.

    The lifecycle for event media is handled outside of NestMediaSource, and
    instead it just asks the device for all events it knows about.
    """

    name: str = MEDIA_SOURCE_TITLE

    def __init__(self, hass: HomeAssistant) -> None:
        """Initialize NestMediaSource."""
        super().__init__(DOMAIN)
        self.hass = hass

    async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
        """Resolve media identifier to a url."""
        media_id: MediaId | None = parse_media_id(item.identifier)
        if not media_id:
            raise Unresolvable("No identifier specified for MediaSourceItem")
        if not media_id.event_id:
            raise Unresolvable("Identifier missing an event_id: %s" % item.identifier)
        devices = await self.devices()
        if not (device := devices.get(media_id.device_id)):
            raise Unresolvable(
                "Unable to find device with identifier: %s" % item.identifier
            )
        events = await _get_events(device)
        if media_id.event_id not in events:
            raise Unresolvable(
                "Unable to find event with identifier: %s" % item.identifier
            )
        event = events[media_id.event_id]
        return PlayMedia(
            EVENT_MEDIA_API_URL_FORMAT.format(
                device_id=media_id.device_id, event_id=media_id.event_id
            ),
            event.event_image_type.content_type,
        )
Ejemplo n.º 19
0
    async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
        """Resolve media to a url."""
        config_id, device_id, kind, path = self._parse_identifier(item.identifier)

        if not config_id or not device_id or not kind or not path:
            raise Unresolvable(
                f"Incomplete media identifier specified: {item.identifier}"
            )

        config = self._get_config_or_raise(config_id)
        device = self._get_device_or_raise(device_id)
        self._verify_kind_or_raise(kind)

        url = get_media_url(
            self.hass.data[DOMAIN][config.entry_id][CONF_CLIENT],
            self._get_camera_id_or_raise(config, device),
            self._get_path_or_raise(path),
            kind == "images",
        )
        if not url:
            raise Unresolvable(f"Could not resolve media item: {item.identifier}")

        return PlayMedia(url, MIME_TYPE_MAP[kind])
Ejemplo n.º 20
0
class CameraMediaSource(MediaSource):
    """Provide camera feeds as media sources."""

    name: str = "Camera"

    def __init__(self, hass: HomeAssistant) -> None:
        """Initialize CameraMediaSource."""
        super().__init__(DOMAIN)
        self.hass = hass

    async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
        """Resolve media to a url."""
        component: EntityComponent = self.hass.data[DOMAIN]
        camera = cast(Optional[Camera], component.get_entity(item.identifier))

        if not camera:
            raise Unresolvable(
                f"Could not resolve media item: {item.identifier}")

        if (stream_type := camera.frontend_stream_type) is None:
            return PlayMedia(f"/api/camera_proxy_stream/{camera.entity_id}",
                             camera.content_type)

        if stream_type != STREAM_TYPE_HLS:
            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)
        except HomeAssistantError as err:
            raise Unresolvable(str(err)) from err

        return PlayMedia(url, FORMAT_CONTENT_TYPE[HLS_PROVIDER])
Ejemplo n.º 21
0
 async def async_resolve_media(self, item: MediaSourceItem) -> str:
     """Resolve media to a url."""
     iteminfo = self.async_parse_identifier(item)
     mime_type, _ = mimetypes.guess_type(str(iteminfo.path))
     return PlayMedia(f"/media/{item.identifier}", mime_type)
Ejemplo n.º 22
0
 async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
     """Resolve media to a url."""
     _, category, url = async_parse_identifier(item)
     kind = category.split("#", 1)[1]
     return PlayMedia(url, MIME_TYPE_MAP[kind])
Ejemplo n.º 23
0
 async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
     """Resolve media to a url."""
     _, camera_id, event_id = async_parse_identifier(item)
     url = self.events[camera_id][event_id]["media_url"]
     return PlayMedia(url, MIME_TYPE)
Ejemplo n.º 24
0
            # The device resolves to the most recent event if available
            if not (last_event_id := await _async_get_recent_event_id(
                    media_id, device)):
                raise Unresolvable(
                    "Unable to resolve recent event for device: %s" %
                    item.identifier)
            media_id = last_event_id

        # Infer content type from the device, since it only supports one
        # snapshot type (either jpg or mp4 clip)
        content_type = EventImageType.IMAGE.content_type
        if CameraClipPreviewTrait.NAME in device.traits:
            content_type = EventImageType.CLIP_PREVIEW.content_type
        return PlayMedia(
            EVENT_MEDIA_API_URL_FORMAT.format(
                device_id=media_id.device_id,
                event_token=media_id.event_token),
            content_type,
        )

    async def async_browse_media(self,
                                 item: MediaSourceItem) -> BrowseMediaSource:
        """Return media for the specified level of the directory tree.

        The top level is the root that contains devices. Inside each device are
        media for events for that device.
        """
        media_id: MediaId | None = parse_media_id(item.identifier)
        _LOGGER.debug("Browsing media for identifier=%s, media_id=%s",
                      item.identifier, media_id)
        devices = await self.devices()
        if media_id is None:
Ejemplo n.º 25
0
async def test_async_browse_media(hass):
    """Test browse media."""
    assert await async_setup_component(hass, DOMAIN, {})

    # Prepare cached Netatmo event date
    hass.data[DOMAIN] = {}
    hass.data[DOMAIN][DATA_EVENTS] = {
        "12:34:56:78:90:ab": {
            1599152672: {
                "id": "12345",
                "type": "person",
                "time": 1599152672,
                "camera_id": "12:34:56:78:90:ab",
                "snapshot": {
                    "url": "https://netatmocameraimage",
                },
                "video_id": "98765",
                "video_status": "available",
                "message": "<b>Paulus</b> seen",
                "media_url": "http:///files/high/index.m3u8",
            },
            1599152673: {
                "id": "12346",
                "type": "person",
                "time": 1599152673,
                "camera_id": "12:34:56:78:90:ab",
                "snapshot": {
                    "url": "https://netatmocameraimage",
                },
                "message": "<b>Tobias</b> seen",
            },
            1599152674: {
                "id":
                "12347",
                "type":
                "outdoor",
                "time":
                1599152674,
                "camera_id":
                "12:34:56:78:90:ac",
                "snapshot": {
                    "url": "https://netatmocameraimage",
                },
                "video_id":
                "98766",
                "video_status":
                "available",
                "event_list": [
                    {
                        "type": "vehicle",
                        "time": 1599152674,
                        "id": "12347-0",
                        "offset": 0,
                        "message": "Vehicle detected",
                        "snapshot": {
                            "url": "https://netatmocameraimage",
                        },
                    },
                    {
                        "type": "human",
                        "time": 1599152674,
                        "id": "12347-1",
                        "offset": 8,
                        "message": "Person detected",
                        "snapshot": {
                            "url": "https://netatmocameraimage",
                        },
                    },
                ],
                "media_url":
                "http:///files/high/index.m3u8",
            },
        }
    }

    hass.data[DOMAIN][DATA_CAMERAS] = {
        "12:34:56:78:90:ab": "MyCamera",
        "12:34:56:78:90:ac": "MyOutdoorCamera",
    }

    assert await async_setup_component(hass, const.DOMAIN, {})
    await hass.async_block_till_done()

    # Test camera not exists
    with pytest.raises(media_source.BrowseError) as excinfo:
        await media_source.async_browse_media(
            hass, f"{const.URI_SCHEME}{DOMAIN}/events/98:76:54:32:10:ff")
    assert str(excinfo.value) == "Camera does not exist."

    # Test browse event
    with pytest.raises(media_source.BrowseError) as excinfo:
        await media_source.async_browse_media(
            hass, f"{const.URI_SCHEME}{DOMAIN}/events/12:34:56:78:90:ab/12345")
    assert str(excinfo.value) == "Event does not exist."

    # Test invalid base
    with pytest.raises(media_source.BrowseError) as excinfo:
        await media_source.async_browse_media(
            hass, f"{const.URI_SCHEME}{DOMAIN}/invalid/base")
    assert str(excinfo.value) == "Unknown source directory."

    # Test successful listing
    media = await media_source.async_browse_media(
        hass, f"{const.URI_SCHEME}{DOMAIN}/events/")

    # Test successful events listing
    media = await media_source.async_browse_media(
        hass, f"{const.URI_SCHEME}{DOMAIN}/events/12:34:56:78:90:ab")

    # Test successful event listing
    media = await media_source.async_browse_media(
        hass,
        f"{const.URI_SCHEME}{DOMAIN}/events/12:34:56:78:90:ab/1599152672")
    assert media

    # Test successful event resolve
    media = await media_source.async_resolve_media(
        hass,
        f"{const.URI_SCHEME}{DOMAIN}/events/12:34:56:78:90:ab/1599152672")
    assert media == PlayMedia(url="http:///files/high/index.m3u8",
                              mime_type="application/x-mpegURL")
Ejemplo n.º 26
0
async def test_async_browse_media(hass):
    """Test browse media."""
    assert await async_setup_component(hass, DOMAIN, {})

    # Prepare cached Netatmo event date
    hass.data[DOMAIN] = {}
    hass.data[DOMAIN][DATA_EVENTS] = ast.literal_eval(
        load_fixture("netatmo/events.txt"))

    hass.data[DOMAIN][DATA_CAMERAS] = {
        "12:34:56:78:90:ab": "MyCamera",
        "12:34:56:78:90:ac": "MyOutdoorCamera",
    }

    assert await async_setup_component(hass, const.DOMAIN, {})
    await hass.async_block_till_done()

    # Test camera not exists
    with pytest.raises(media_source.BrowseError) as excinfo:
        await media_source.async_browse_media(
            hass, f"{const.URI_SCHEME}{DOMAIN}/events/98:76:54:32:10:ff")
    assert str(excinfo.value) == "Camera does not exist."

    # Test browse event
    with pytest.raises(media_source.BrowseError) as excinfo:
        await media_source.async_browse_media(
            hass, f"{const.URI_SCHEME}{DOMAIN}/events/12:34:56:78:90:ab/12345")
    assert str(excinfo.value) == "Event does not exist."

    # Test invalid base
    with pytest.raises(media_source.BrowseError) as excinfo:
        await media_source.async_browse_media(
            hass, f"{const.URI_SCHEME}{DOMAIN}/invalid/base")
    assert str(excinfo.value) == "Unknown source directory."

    # Test invalid base
    with pytest.raises(media_source.BrowseError) as excinfo:
        await media_source.async_browse_media(hass,
                                              f"{const.URI_SCHEME}{DOMAIN}/")
    assert str(excinfo.value) == "Not a media source item"

    # Test successful listing
    media = await media_source.async_browse_media(
        hass, f"{const.URI_SCHEME}{DOMAIN}/events")

    # Test successful listing
    media = await media_source.async_browse_media(
        hass, f"{const.URI_SCHEME}{DOMAIN}/events/")

    # Test successful events listing
    media = await media_source.async_browse_media(
        hass, f"{const.URI_SCHEME}{DOMAIN}/events/12:34:56:78:90:ab")

    # Test successful event listing
    media = await media_source.async_browse_media(
        hass,
        f"{const.URI_SCHEME}{DOMAIN}/events/12:34:56:78:90:ab/1599152672")
    assert media

    # Test successful event resolve
    media = await media_source.async_resolve_media(
        hass,
        f"{const.URI_SCHEME}{DOMAIN}/events/12:34:56:78:90:ab/1599152672")
    assert media == PlayMedia(url="http:///files/high/index.m3u8",
                              mime_type="application/x-mpegURL")
 async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
     """Resolve media to a url."""
     return PlayMedia(f"/api/frigate/{item.identifier}", MIME_TYPE)