Exemple #1
0
async def async_browse_media(
    hass: HomeAssistant,
    media_content_id: str | None,
    *,
    content_filter: Callable[[BrowseMedia], bool] | None = None,
) -> BrowseMediaSource:
    """Return media player browse media results."""
    if DOMAIN not in hass.data:
        raise BrowseError("Media Source not loaded")

    try:
        item = await _get_media_item(hass, media_content_id).async_browse()
    except ValueError as err:
        raise BrowseError("Not a media source item") from err

    if content_filter is None or item.children is None:
        return item

    old_count = len(item.children)
    item.children = [
        child for child in item.children
        if child.can_expand or content_filter(child)
    ]
    item.not_shown = old_count - len(item.children)
    return item
async def browse_node(media_library, media_content_type, media_content_id):
    """Browse a node of a Volumio media hierarchy."""
    json_item = json.loads(media_content_id)
    navigation = await media_library.browse(json_item["uri"])
    if "lists" not in navigation:
        raise BrowseError(
            f"Media not found: {media_content_type} / {media_content_id}")

    # we only use the first list since the second one could include all tracks
    first_list = navigation["lists"][0]
    children = [
        _item_payload(media_library, item, parent_item=json_item)
        for item in first_list["items"]
    ]
    info = navigation.get("info")
    title = first_list.get("title")
    if not title:
        if info:
            title = f"{info.get('album')} ({info.get('artist')})"
        else:
            title = "Media Library"

    payload = _raw_item_payload(media_library,
                                json_item,
                                title=title,
                                info=info)
    return BrowseMedia(**payload, children=children)
Exemple #3
0
async def async_browse_media(
    hass: HomeAssistant,
    media_content_type: str | None,
    media_content_id: str | None,
    *,
    can_play_artist: bool = True,
) -> BrowseMedia:
    """Browse Spotify media."""
    parsed_url = None
    info = None

    # Check if caller is requesting the root nodes
    if media_content_type is None and media_content_id is None:
        children = []
        for config_entry_id, info in hass.data[DOMAIN].items():
            config_entry = hass.config_entries.async_get_entry(config_entry_id)
            assert config_entry is not None
            children.append(
                BrowseMedia(
                    title=config_entry.title,
                    media_class=MEDIA_CLASS_APP,
                    media_content_id=f"{MEDIA_PLAYER_PREFIX}{config_entry_id}",
                    media_content_type=f"{MEDIA_PLAYER_PREFIX}library",
                    thumbnail=
                    "https://brands.home-assistant.io/_/spotify/logo.png",
                    can_play=False,
                    can_expand=True,
                ))
        return BrowseMedia(
            title="Spotify",
            media_class=MEDIA_CLASS_APP,
            media_content_id=MEDIA_PLAYER_PREFIX,
            media_content_type="spotify",
            thumbnail="https://brands.home-assistant.io/_/spotify/logo.png",
            can_play=False,
            can_expand=True,
            children=children,
        )

    if media_content_id is None or not media_content_id.startswith(
            MEDIA_PLAYER_PREFIX):
        raise BrowseError("Invalid Spotify URL specified")

    # Check for config entry specifier, and extract Spotify URI
    parsed_url = yarl.URL(media_content_id)
    if (info := hass.data[DOMAIN].get(parsed_url.host)) is None:
        raise BrowseError("Invalid Spotify account specified")
Exemple #4
0
async def async_browse_media(hass,
                             media_content_type,
                             media_content_id,
                             *,
                             can_play_artist=True):
    """Browse Spotify media."""
    if not (info := next(iter(hass.data[DOMAIN].values()), None)):
        raise BrowseError("No Spotify accounts available")
Exemple #5
0
async def test_websocket_browse_media(hass, hass_ws_client):
    """Test browse media websocket."""
    assert await async_setup_component(hass, media_source.DOMAIN, {})
    await hass.async_block_till_done()

    client = await hass_ws_client(hass)

    media = media_source.models.BrowseMediaSource(
        domain=media_source.DOMAIN,
        identifier="/media",
        title="Local Media",
        media_class=MEDIA_CLASS_DIRECTORY,
        media_content_type="listing",
        can_play=False,
        can_expand=True,
    )

    with patch(
        "homeassistant.components.media_source.async_browse_media",
        return_value=media,
    ):
        await client.send_json(
            {
                "id": 1,
                "type": "media_source/browse_media",
            }
        )

        msg = await client.receive_json()

    assert msg["success"]
    assert msg["id"] == 1
    assert media.as_dict() == msg["result"]

    with patch(
        "homeassistant.components.media_source.async_browse_media",
        side_effect=BrowseError("test"),
    ):
        await client.send_json(
            {
                "id": 2,
                "type": "media_source/browse_media",
                "media_content_id": "invalid",
            }
        )

        msg = await client.receive_json()

    assert not msg["success"]
    assert msg["error"]["code"] == "browse_media_failed"
    assert msg["error"]["message"] == "test"
Exemple #6
0
async def build_item_response(player, payload):
    """Create response payload for search described by payload."""
    search_id = payload["search_id"]
    search_type = payload["search_type"]

    media_class = CONTENT_TYPE_MEDIA_CLASS[search_type]

    if search_id and search_id != search_type:
        browse_id = (SQUEEZEBOX_ID_BY_TYPE[search_type], search_id)
    else:
        browse_id = None

    result = await player.async_browse(
        MEDIA_TYPE_TO_SQUEEZEBOX[search_type],
        limit=BROWSE_LIMIT,
        browse_id=browse_id,
    )

    children = None

    if result is not None and result.get("items"):
        item_type = CONTENT_TYPE_TO_CHILD_TYPE[search_type]
        child_media_class = CONTENT_TYPE_MEDIA_CLASS[item_type]

        children = []
        for item in result["items"]:
            children.append(
                BrowseMedia(
                    title=item["title"],
                    media_class=child_media_class["item"],
                    media_content_id=str(item["id"]),
                    media_content_type=item_type,
                    can_play=True,
                    can_expand=child_media_class["children"] is not None,
                    thumbnail=item.get("image_url"),
                ))

    if children is None:
        raise BrowseError(f"Media not found: {search_type} / {search_id}")

    return BrowseMedia(
        title=result.get("title"),
        media_class=media_class["item"],
        children_media_class=media_class["children"],
        media_content_id=search_id,
        media_content_type=search_type,
        can_play=True,
        children=children,
        can_expand=True,
    )
 async def async_browse_media(
         self, media_content_type: Optional[str],
         media_content_id: Optional[str]) -> "BrowseMedia":
     builder = None
     if media_content_type in [None, "library"]:
         builder = self._browse_media_library
     elif media_content_type == "bouquet":
         builder = self._browse_media_bouquet
     response = None
     if builder:
         response = await self.hass.async_add_executor_job(
             builder, media_content_type, media_content_id)
     if response is None:
         raise BrowseError(
             f"Media not found: {media_content_type} / {media_content_id}")
     return response
async def async_browse_media(hass, media_content_type, media_content_id, platform=None):
    """Browse Plex media."""
    plex_server = next(iter(hass.data[PLEX_DOMAIN][SERVERS].values()), None)
    if not plex_server:
        raise BrowseError("No Plex servers available")
    is_internal = is_internal_request(hass)
    return await hass.async_add_executor_job(
        partial(
            browse_media,
            hass,
            is_internal,
            media_content_type,
            media_content_id,
            platform=platform,
        )
    )
Exemple #9
0
async def async_browse_media(
    hass: HomeAssistant,
    media_content_id: str | None,
    *,
    content_filter: Callable[[BrowseMedia], bool] | None = None,
) -> BrowseMediaSource:
    """Return media player browse media results."""
    if DOMAIN not in hass.data:
        raise BrowseError("Media Source not loaded")

    item = await _get_media_item(hass, media_content_id).async_browse()

    if content_filter is None or item.children is None:
        return item

    item.children = [
        child for child in item.children if child.can_expand or content_filter(child)
    ]
    return item
Exemple #10
0
async def async_browse_media_internal(
    hass: HomeAssistant,
    spotify: Spotify,
    session: OAuth2Session,
    current_user: dict[str, Any],
    media_content_type: str | None,
    media_content_id: str | None,
    *,
    can_play_artist: bool = True,
) -> BrowseMedia:
    """Browse spotify media."""
    if media_content_type in (None, f"{MEDIA_PLAYER_PREFIX}library"):
        return await hass.async_add_executor_job(
            partial(library_payload, can_play_artist=can_play_artist))

    if not session.valid_token:
        await session.async_ensure_token_valid()
        await hass.async_add_executor_job(spotify.set_auth,
                                          session.token["access_token"])

    # Strip prefix
    if media_content_type:
        media_content_type = media_content_type[len(MEDIA_PLAYER_PREFIX):]

    payload = {
        "media_content_type": media_content_type,
        "media_content_id": media_content_id,
    }
    response = await hass.async_add_executor_job(
        partial(
            build_item_response,
            spotify,
            current_user,
            payload,
            can_play_artist=can_play_artist,
        ))
    if response is None:
        raise BrowseError(
            f"Media not found: {media_content_type} / {media_content_id}")
    return response
Exemple #11
0
                    item_thumbnail = entity.get_browse_image_url(
                        item_type, item_id, artwork_track_id)

            children.append(
                BrowseMedia(
                    title=item["title"],
                    media_class=child_media_class["item"],
                    media_content_id=item_id,
                    media_content_type=item_type,
                    can_play=True,
                    can_expand=child_media_class["children"] is not None,
                    thumbnail=item_thumbnail,
                ))

    if children is None:
        raise BrowseError(f"Media not found: {search_type} / {search_id}")

    return BrowseMedia(
        title=result.get("title"),
        media_class=media_class["item"],
        children_media_class=media_class["children"],
        media_content_id=search_id,
        media_content_type=search_type,
        can_play=True,
        children=children,
        can_expand=True,
    )


async def library_payload(hass, player):
    """Create response payload to describe contents of library."""
Exemple #12
0
    async def async_browse_media(self, media_content_type=None, media_content_id=None):
        """Implement the websocket media browsing helper."""
        _LOGGER.debug("Meural device %s: Browsing media. Media_content_type is %s, media_content_id is %s", self.name, media_content_type, media_content_id)
        if media_content_id in (None, "") and media_content_type in (None, ""):
            response = BrowseMedia(
                title="Meural Canvas",
                media_class=MEDIA_CLASS_DIRECTORY,
                media_content_id="",
                media_content_type="",
                can_play=False,
                can_expand=True,
                children=[BrowseMedia(
                    title="Media Source",
                    media_class=MEDIA_CLASS_DIRECTORY,
                    media_content_id="",
                    media_content_type="localmediasource",
                    can_play=False,
                    can_expand=True),
                BrowseMedia(
                    title="Meural Playlists",
                    media_class=MEDIA_CLASS_DIRECTORY,
                    media_content_id="",
                    media_content_type="meuralplaylists",
                    can_play=False,
                    can_expand=True),
                ]
            )
            return response

        elif media_source.is_media_source_id(media_content_id) or media_content_type=="localmediasource":
            kwargs = {}
            if MAJOR_VERSION > 2022 or (MAJOR_VERSION == 2022 and MINOR_VERSION >= 2):
                kwargs['content_filter'] = lambda item: item.media_content_type in ('image/jpg', 'image/png', 'image/jpeg')

            response = await media_source.async_browse_media(self.hass, media_content_id, **kwargs)
            return response

        elif media_content_type=="meuralplaylists":
            response = BrowseMedia(
                title="Meural Playlists",
                media_class=MEDIA_CLASS_DIRECTORY,
                media_content_id="",
                media_content_type="",
                can_play=False,
                can_expand=True,
                children=[])

            device_galleries = await self.meural.get_device_galleries(self.meural_device_id)
            _LOGGER.info("Meural device %s: Browsing media. Getting %d device galleries from Meural server", self.name, len(device_galleries))
            user_galleries = await self.meural.get_user_galleries()
            _LOGGER.info("Meural device %s: Browsing media. Getting %d user galleries from Meural server", self.name, len(user_galleries))
            [device_galleries.append(x) for x in user_galleries if x not in device_galleries]
            self._remote_galleries = device_galleries
            _LOGGER.info("Meural device %s: Browsing media. Has %d unique remote galleries on Meural server" % (self.name, len(self._remote_galleries)))

            for g in self._galleries:

                thumb=next((h["cover"] for h in self._remote_galleries if h["id"] == int(g["id"])), None)
                if thumb == None and (int(g["id"])>4):
                    _LOGGER.debug("Meural device %s: Browsing media. Gallery %s misses thumbnail, getting gallery items", self.name, g["id"])
                    album_items = await self.local_meural.send_get_items_by_gallery(g["id"])
                    _LOGGER.info("Meural device %s: Browsing media. Replacing missing thumbnail of gallery %s with first gallery item image. Getting information from Meural server for item %s", self.name, g["id"], album_items[0]["id"])
                    first_item = await self.meural.get_item(album_items[0]["id"])
                    thumb = first_item["image"]
                _LOGGER.debug("Meural device %s: Browsing media. Thumbnail image for gallery %s is %s", self.name, g["id"], thumb)

                response.children.append(BrowseMedia(
                    title=g["name"],
                    media_class=MEDIA_TYPE_PLAYLIST,
                    media_content_id=g["id"],
                    media_content_type=MEDIA_TYPE_PLAYLIST,
                    can_play=True,
                    can_expand=False,
                    thumbnail=thumb,
                    )
                )
            return response

        else:
            _LOGGER.error("Meural device %s: Browsing media. Media not found, media_content_type is %s, media_content_id is %s", self.name, media_content_type, media_content_id)
            raise BrowseError(
                f"Media not found: {media_content_type} / {media_content_id}"
            )
Exemple #13
0
async def async_browse_media(
    hass: HomeAssistant,
    media_content_type: str,
    media_content_id: str,
    cast_type: str,
) -> BrowseMedia | None:
    """Browse media."""
    if media_content_type != DOMAIN:
        return None

    try:
        get_url(hass, require_ssl=True, prefer_external=True)
    except NoURLAvailableError as err:
        raise BrowseError(NO_URL_AVAILABLE_ERROR) from err

    # List dashboards.
    if not media_content_id:
        children = [
            BrowseMedia(
                title="Default",
                media_class=MEDIA_CLASS_APP,
                media_content_id=DEFAULT_DASHBOARD,
                media_content_type=DOMAIN,
                thumbnail=
                "https://brands.home-assistant.io/_/lovelace/logo.png",
                can_play=True,
                can_expand=False,
            )
        ]
        for url_path in hass.data[DOMAIN]["dashboards"]:
            if url_path is None:
                continue

            info = await _get_dashboard_info(hass, url_path)
            children.append(_item_from_info(info))

        root = (await
                async_get_media_browser_root_object(hass,
                                                    CAST_TYPE_CHROMECAST))[0]
        root.children = children
        return root

    try:
        info = await _get_dashboard_info(hass, media_content_id)
    except ValueError as err:
        raise BrowseError(f"Dashboard {media_content_id} not found") from err

    children = []

    for view in info["views"]:
        children.append(
            BrowseMedia(
                title=view["title"],
                media_class=MEDIA_CLASS_APP,
                media_content_id=f'{info["url_path"]}/{view["path"]}',
                media_content_type=DOMAIN,
                thumbnail=
                "https://brands.home-assistant.io/_/lovelace/logo.png",
                can_play=True,
                can_expand=False,
            ))

    root = _item_from_info(info)
    root.children = children
    return root