Support browsing multiple Spotify accounts (#66256)

* Support browsing multiple Spotify accounts

* Fix rebase mistakes

* Address review comments

* Return root spotify node with config entries as children

* Add util to get spotify URI for media browser URL

* Only support browsing spotify with config entry specified
This commit is contained in:
Erik Montnemery 2022-02-14 13:59:11 +01:00 committed by GitHub
parent 370832f527
commit dbd26c7faf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 6 deletions

View File

@ -30,7 +30,11 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
from . import config_flow
from .browse_media import async_browse_media
from .const import DOMAIN, LOGGER, SPOTIFY_SCOPES
from .util import is_spotify_media_type, resolve_spotify_media_type
from .util import (
is_spotify_media_type,
resolve_spotify_media_type,
spotify_uri_from_media_browser_url,
)
CONFIG_SCHEMA = vol.Schema(
{
@ -50,6 +54,7 @@ PLATFORMS = [Platform.MEDIA_PLAYER]
__all__ = [
"async_browse_media",
"DOMAIN",
"spotify_uri_from_media_browser_url",
"is_spotify_media_type",
"resolve_spotify_media_type",
]

View File

@ -6,11 +6,13 @@ import logging
from typing import Any
from spotipy import Spotify
import yarl
from homeassistant.backports.enum import StrEnum
from homeassistant.components.media_player import BrowseError, BrowseMedia
from homeassistant.components.media_player.const import (
MEDIA_CLASS_ALBUM,
MEDIA_CLASS_APP,
MEDIA_CLASS_ARTIST,
MEDIA_CLASS_DIRECTORY,
MEDIA_CLASS_EPISODE,
@ -137,15 +139,53 @@ class UnknownMediaType(BrowseError):
async def async_browse_media(
hass: HomeAssistant,
media_content_type: str,
media_content_id: str,
media_content_type: str | None,
media_content_id: str | None,
*,
can_play_artist: bool = True,
) -> BrowseMedia:
"""Browse Spotify media."""
if not (info := next(iter(hass.data[DOMAIN].values()), None)):
raise BrowseError("No Spotify accounts available")
return await async_browse_media_internal(
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")
media_content_id = parsed_url.name
result = await async_browse_media_internal(
hass,
info.client,
info.session,
@ -155,6 +195,13 @@ async def async_browse_media(
can_play_artist=can_play_artist,
)
# Build new URLs with config entry specifyers
result.media_content_id = str(parsed_url.with_name(result.media_content_id))
if result.children:
for child in result.children:
child.media_content_id = str(parsed_url.with_name(child.media_content_id))
return result
async def async_browse_media_internal(
hass: HomeAssistant,

View File

@ -3,6 +3,8 @@ from __future__ import annotations
from typing import Any
import yarl
from .const import MEDIA_PLAYER_PREFIX
@ -22,3 +24,11 @@ def fetch_image_url(item: dict[str, Any], key="images") -> str | None:
return item.get(key, [])[0].get("url")
except IndexError:
return None
def spotify_uri_from_media_browser_url(media_content_id: str) -> str:
"""Extract spotify URI from media browser URL."""
if media_content_id and media_content_id.startswith(MEDIA_PLAYER_PREFIX):
parsed_url = yarl.URL(media_content_id)
media_content_id = parsed_url.name
return media_content_id