Support Plex in Sonos media browser (#64951)

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
jjlawren 2022-01-26 12:40:47 -06:00 committed by GitHub
parent e5b0031103
commit e17f1ea577
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 118 additions and 79 deletions

View File

@ -13,7 +13,7 @@ from plexwebsocket import (
) )
import requests.exceptions import requests.exceptions
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN from homeassistant.components.media_player import DOMAIN as MP_DOMAIN, BrowseError
from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.const import CONF_URL, CONF_VERIFY_SSL, EVENT_HOMEASSISTANT_STOP from homeassistant.const import CONF_URL, CONF_VERIFY_SSL, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
@ -25,6 +25,7 @@ from homeassistant.helpers.dispatcher import (
async_dispatcher_connect, async_dispatcher_connect,
async_dispatcher_send, async_dispatcher_send,
) )
from homeassistant.helpers.network import is_internal_request
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from .const import ( from .const import (
@ -39,16 +40,41 @@ from .const import (
PLEX_SERVER_CONFIG, PLEX_SERVER_CONFIG,
PLEX_UPDATE_LIBRARY_SIGNAL, PLEX_UPDATE_LIBRARY_SIGNAL,
PLEX_UPDATE_PLATFORMS_SIGNAL, PLEX_UPDATE_PLATFORMS_SIGNAL,
PLEX_URI_SCHEME,
SERVERS, SERVERS,
WEBSOCKETS, WEBSOCKETS,
) )
from .errors import ShouldUpdateConfigEntry from .errors import ShouldUpdateConfigEntry
from .media_browser import browse_media
from .server import PlexServer from .server import PlexServer
from .services import async_setup_services from .services import async_setup_services
_LOGGER = logging.getLogger(__package__) _LOGGER = logging.getLogger(__package__)
def is_plex_media_id(media_content_id):
"""Return whether the media_content_id is a valid Plex media_id."""
return media_content_id and media_content_id.startswith(PLEX_URI_SCHEME)
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,
plex_server,
is_internal,
media_content_type,
media_content_id,
platform=platform,
)
)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Plex component.""" """Set up the Plex component."""
hass.data.setdefault( hass.data.setdefault(

View File

@ -16,7 +16,7 @@ from homeassistant.components.media_player.const import (
) )
from homeassistant.components.media_player.errors import BrowseError from homeassistant.components.media_player.errors import BrowseError
from .const import DOMAIN from .const import DOMAIN, PLEX_URI_SCHEME
from .helpers import pretty_title from .helpers import pretty_title
@ -29,7 +29,7 @@ EXPANDABLES = ["album", "artist", "playlist", "season", "show"]
PLAYLISTS_BROWSE_PAYLOAD = { PLAYLISTS_BROWSE_PAYLOAD = {
"title": "Playlists", "title": "Playlists",
"media_class": MEDIA_CLASS_DIRECTORY, "media_class": MEDIA_CLASS_DIRECTORY,
"media_content_id": "all", "media_content_id": PLEX_URI_SCHEME + "all",
"media_content_type": "playlists", "media_content_type": "playlists",
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
@ -53,7 +53,7 @@ _LOGGER = logging.getLogger(__name__)
def browse_media( # noqa: C901 def browse_media( # noqa: C901
entity, is_internal, media_content_type=None, media_content_id=None plex_server, is_internal, media_content_type, media_content_id, *, platform=None
): ):
"""Implement the websocket media browsing helper.""" """Implement the websocket media browsing helper."""
@ -67,28 +67,19 @@ def browse_media( # noqa: C901
payload = { payload = {
"title": pretty_title(item, short_name), "title": pretty_title(item, short_name),
"media_class": media_class, "media_class": media_class,
"media_content_id": str(item.ratingKey), "media_content_id": PLEX_URI_SCHEME + str(item.ratingKey),
"media_content_type": item.type, "media_content_type": item.type,
"can_play": True, "can_play": True,
"can_expand": item.type in EXPANDABLES, "can_expand": item.type in EXPANDABLES,
} }
if hasattr(item, "thumbUrl"): if hasattr(item, "thumbUrl"):
entity.plex_server.thumbnail_cache.setdefault( payload["thumbnail"] = item.thumbUrl
str(item.ratingKey), item.thumbUrl
)
if is_internal:
thumbnail = item.thumbUrl
else:
thumbnail = entity.get_browse_image_url(item.type, item.ratingKey)
payload["thumbnail"] = thumbnail
return BrowseMedia(**payload) return BrowseMedia(**payload)
def library_payload(library_id): def library_payload(library_id):
"""Create response payload to describe contents of a specific library.""" """Create response payload to describe contents of a specific library."""
library = entity.plex_server.library.sectionByID(library_id) library = plex_server.library.sectionByID(library_id)
library_info = library_section_payload(library) library_info = library_section_payload(library)
library_info.children = [special_library_payload(library_info, "Recommended")] library_info.children = [special_library_payload(library_info, "Recommended")]
for item in library.all(): for item in library.all():
@ -98,10 +89,12 @@ def browse_media( # noqa: C901
continue continue
return library_info return library_info
def playlists_payload(): def playlists_payload(platform):
"""Create response payload for all available playlists.""" """Create response payload for all available playlists."""
playlists_info = {**PLAYLISTS_BROWSE_PAYLOAD, "children": []} playlists_info = {**PLAYLISTS_BROWSE_PAYLOAD, "children": []}
for playlist in entity.plex_server.playlists(): for playlist in plex_server.playlists():
if playlist.playlistType != "audio" and platform == "sonos":
continue
try: try:
playlists_info["children"].append(item_payload(playlist)) playlists_info["children"].append(item_payload(playlist))
except UnknownMediaType: except UnknownMediaType:
@ -112,7 +105,7 @@ def browse_media( # noqa: C901
def build_item_response(payload): def build_item_response(payload):
"""Create response payload for the provided media query.""" """Create response payload for the provided media query."""
media = entity.plex_server.lookup_media(**payload) media = plex_server.lookup_media(**payload)
if media is None: if media is None:
return None return None
@ -123,7 +116,7 @@ def browse_media( # noqa: C901
return None return None
if media_info.can_expand: if media_info.can_expand:
media_info.children = [] media_info.children = []
if media.TYPE == "artist": if media.TYPE == "artist" and platform != "sonos":
if (station := media.station()) is not None: if (station := media.station()) is not None:
media_info.children.append(station_payload(station)) media_info.children.append(station_payload(station))
for item in media: for item in media:
@ -133,18 +126,22 @@ def browse_media( # noqa: C901
continue continue
return media_info return media_info
if media_content_id:
assert media_content_id.startswith(PLEX_URI_SCHEME)
media_content_id = media_content_id[len(PLEX_URI_SCHEME) :]
if media_content_id and media_content_id.startswith(HUB_PREFIX): if media_content_id and media_content_id.startswith(HUB_PREFIX):
media_content_id = media_content_id[len(HUB_PREFIX) :] media_content_id = media_content_id[len(HUB_PREFIX) :]
location, hub_identifier = media_content_id.split(":") location, hub_identifier = media_content_id.split(":")
if location == "server": if location == "server":
hub = next( hub = next(
x x
for x in entity.plex_server.library.hubs() for x in plex_server.library.hubs()
if x.hubIdentifier == hub_identifier if x.hubIdentifier == hub_identifier
) )
media_content_id = f"{HUB_PREFIX}server:{hub.hubIdentifier}" media_content_id = f"{HUB_PREFIX}server:{hub.hubIdentifier}"
else: else:
library_section = entity.plex_server.library.sectionByID(int(location)) library_section = plex_server.library.sectionByID(int(location))
hub = next( hub = next(
x for x in library_section.hubs() if x.hubIdentifier == hub_identifier x for x in library_section.hubs() if x.hubIdentifier == hub_identifier
) )
@ -156,7 +153,7 @@ def browse_media( # noqa: C901
payload = { payload = {
"title": hub.title, "title": hub.title,
"media_class": MEDIA_CLASS_DIRECTORY, "media_class": MEDIA_CLASS_DIRECTORY,
"media_content_id": media_content_id, "media_content_id": PLEX_URI_SCHEME + media_content_id,
"media_content_type": hub.type, "media_content_type": hub.type,
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
@ -165,6 +162,8 @@ def browse_media( # noqa: C901
} }
for item in hub.items: for item in hub.items:
if hub.type == "station": if hub.type == "station":
if platform == "sonos":
continue
payload["children"].append(station_payload(item)) payload["children"].append(station_payload(item))
else: else:
payload["children"].append(item_payload(item)) payload["children"].append(item_payload(item))
@ -175,24 +174,13 @@ def browse_media( # noqa: C901
else: else:
special_folder = None special_folder = None
if (
media_content_type
and media_content_type == "server"
and media_content_id != entity.plex_server.machine_identifier
):
raise BrowseError(
f"Plex server with ID '{media_content_id}' is not associated with {entity.entity_id}"
)
if special_folder: if special_folder:
if media_content_type == "server": if media_content_type == "server":
library_or_section = entity.plex_server.library library_or_section = plex_server.library
children_media_class = MEDIA_CLASS_DIRECTORY children_media_class = MEDIA_CLASS_DIRECTORY
title = entity.plex_server.friendly_name title = plex_server.friendly_name
elif media_content_type == "library": elif media_content_type == "library":
library_or_section = entity.plex_server.library.sectionByID( library_or_section = plex_server.library.sectionByID(int(media_content_id))
int(media_content_id)
)
title = library_or_section.title title = library_or_section.title
try: try:
children_media_class = ITEM_TYPE_MEDIA_CLASS[library_or_section.TYPE] children_media_class = ITEM_TYPE_MEDIA_CLASS[library_or_section.TYPE]
@ -208,7 +196,8 @@ def browse_media( # noqa: C901
payload = { payload = {
"title": title, "title": title,
"media_class": MEDIA_CLASS_DIRECTORY, "media_class": MEDIA_CLASS_DIRECTORY,
"media_content_id": f"{media_content_id}:{special_folder}", "media_content_id": PLEX_URI_SCHEME
+ f"{media_content_id}:{special_folder}",
"media_content_type": media_content_type, "media_content_type": media_content_type,
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
@ -226,7 +215,7 @@ def browse_media( # noqa: C901
try: try:
if media_content_type in ("server", None): if media_content_type in ("server", None):
return server_payload(entity.plex_server) return server_payload(plex_server, platform)
if media_content_type == "library": if media_content_type == "library":
return library_payload(int(media_content_id)) return library_payload(int(media_content_id))
@ -237,7 +226,7 @@ def browse_media( # noqa: C901
) from err ) from err
if media_content_type == "playlists": if media_content_type == "playlists":
return playlists_payload() return playlists_payload(platform)
payload = { payload = {
"media_type": DOMAIN, "media_type": DOMAIN,
@ -259,7 +248,7 @@ def library_section_payload(section):
return BrowseMedia( return BrowseMedia(
title=section.title, title=section.title,
media_class=MEDIA_CLASS_DIRECTORY, media_class=MEDIA_CLASS_DIRECTORY,
media_content_id=str(section.key), media_content_id=PLEX_URI_SCHEME + str(section.key),
media_content_type="library", media_content_type="library",
can_play=False, can_play=False,
can_expand=True, can_expand=True,
@ -281,21 +270,25 @@ def special_library_payload(parent_payload, special_type):
) )
def server_payload(plex_server): def server_payload(plex_server, platform):
"""Create response payload to describe libraries of the Plex server.""" """Create response payload to describe libraries of the Plex server."""
server_info = BrowseMedia( server_info = BrowseMedia(
title=plex_server.friendly_name, title=plex_server.friendly_name,
media_class=MEDIA_CLASS_DIRECTORY, media_class=MEDIA_CLASS_DIRECTORY,
media_content_id=plex_server.machine_identifier, media_content_id=PLEX_URI_SCHEME + plex_server.machine_identifier,
media_content_type="server", media_content_type="server",
can_play=False, can_play=False,
can_expand=True, can_expand=True,
children=[],
children_media_class=MEDIA_CLASS_DIRECTORY, children_media_class=MEDIA_CLASS_DIRECTORY,
) )
server_info.children = [special_library_payload(server_info, "Recommended")] if platform != "sonos":
server_info.children.append(special_library_payload(server_info, "Recommended"))
for library in plex_server.library.sections(): for library in plex_server.library.sections():
if library.type == "photo": if library.type == "photo":
continue continue
if library.type != "artist" and platform == "sonos":
continue
server_info.children.append(library_section_payload(library)) server_info.children.append(library_section_payload(library))
server_info.children.append(BrowseMedia(**PLAYLISTS_BROWSE_PAYLOAD)) server_info.children.append(BrowseMedia(**PLAYLISTS_BROWSE_PAYLOAD))
return server_info return server_info
@ -310,7 +303,7 @@ def hub_payload(hub):
payload = { payload = {
"title": hub.title, "title": hub.title,
"media_class": MEDIA_CLASS_DIRECTORY, "media_class": MEDIA_CLASS_DIRECTORY,
"media_content_id": media_content_id, "media_content_id": PLEX_URI_SCHEME + media_content_id,
"media_content_type": hub.type, "media_content_type": hub.type,
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
@ -323,7 +316,7 @@ def station_payload(station):
return BrowseMedia( return BrowseMedia(
title=station.title, title=station.title,
media_class=ITEM_TYPE_MEDIA_CLASS[station.type], media_class=ITEM_TYPE_MEDIA_CLASS[station.type],
media_content_id=station.key, media_content_id=PLEX_URI_SCHEME + station.key,
media_content_type="station", media_content_type="station",
can_play=True, can_play=True,
can_expand=False, can_expand=False,

View File

@ -46,6 +46,7 @@ from .const import (
PLEX_UPDATE_MEDIA_PLAYER_SESSION_SIGNAL, PLEX_UPDATE_MEDIA_PLAYER_SESSION_SIGNAL,
PLEX_UPDATE_MEDIA_PLAYER_SIGNAL, PLEX_UPDATE_MEDIA_PLAYER_SIGNAL,
PLEX_UPDATE_SENSOR_SIGNAL, PLEX_UPDATE_SENSOR_SIGNAL,
PLEX_URI_SCHEME,
SERVERS, SERVERS,
TRANSIENT_DEVICE_MODELS, TRANSIENT_DEVICE_MODELS,
) )
@ -486,6 +487,9 @@ class PlexMediaPlayer(MediaPlayerEntity):
"Plex integration configured without a token, playback may fail" "Plex integration configured without a token, playback may fail"
) )
if media_id.startswith(PLEX_URI_SCHEME):
media_id = media_id[len(PLEX_URI_SCHEME) :]
if media_type == "station": if media_type == "station":
playqueue = self.plex_server.create_station_playqueue(media_id) playqueue = self.plex_server.create_station_playqueue(media_id)
try: try:
@ -576,7 +580,7 @@ class PlexMediaPlayer(MediaPlayerEntity):
is_internal = is_internal_request(self.hass) is_internal = is_internal_request(self.hass)
return await self.hass.async_add_executor_job( return await self.hass.async_add_executor_job(
browse_media, browse_media,
self, self.plex_server,
is_internal, is_internal,
media_content_type, media_content_type,
media_content_id, media_content_id,

View File

@ -7,7 +7,7 @@ from functools import partial
import logging import logging
from urllib.parse import quote_plus, unquote from urllib.parse import quote_plus, unquote
from homeassistant.components import media_source, spotify from homeassistant.components import media_source, plex, spotify
from homeassistant.components.media_player import BrowseMedia from homeassistant.components.media_player import BrowseMedia
from homeassistant.components.media_player.const import ( from homeassistant.components.media_player.const import (
MEDIA_CLASS_DIRECTORY, MEDIA_CLASS_DIRECTORY,
@ -18,6 +18,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.network import is_internal_request from homeassistant.helpers.network import is_internal_request
from .const import ( from .const import (
DOMAIN,
EXPANDABLE_MEDIA_TYPES, EXPANDABLE_MEDIA_TYPES,
LIBRARY_TITLES_MAPPING, LIBRARY_TITLES_MAPPING,
MEDIA_TYPES_TO_SONOS, MEDIA_TYPES_TO_SONOS,
@ -90,6 +91,14 @@ async def async_browse_media(
hass, media_content_id, content_filter=media_source_filter hass, media_content_id, content_filter=media_source_filter
) )
if plex.is_plex_media_id(media_content_id):
return await plex.async_browse_media(
hass, media_content_type, media_content_id, platform=DOMAIN
)
if media_content_type == "plex":
return await plex.async_browse_media(hass, None, None, platform=DOMAIN)
if spotify.is_spotify_media_type(media_content_type): if spotify.is_spotify_media_type(media_content_type):
return await spotify.async_browse_media( return await spotify.async_browse_media(
hass, media_content_type, media_content_id, can_play_artist=False hass, media_content_type, media_content_id, can_play_artist=False
@ -256,6 +265,19 @@ async def root_payload(
) )
) )
if "plex" in hass.config.components:
children.append(
BrowseMedia(
title="Plex",
media_class=MEDIA_CLASS_DIRECTORY,
media_content_id="",
media_content_type="plex",
thumbnail="https://brands.home-assistant.io/_/plex/logo.png",
can_play=False,
can_expand=True,
)
)
if "spotify" in hass.config.components: if "spotify" in hass.config.components:
children.append( children.append(
BrowseMedia( BrowseMedia(
@ -263,7 +285,7 @@ async def root_payload(
media_class=MEDIA_CLASS_DIRECTORY, media_class=MEDIA_CLASS_DIRECTORY,
media_content_id="", media_content_id="",
media_content_type="spotify", media_content_type="spotify",
thumbnail="https://brands.home-assistant.io/spotify/logo.png", thumbnail="https://brands.home-assistant.io/_/spotify/logo.png",
can_play=False, can_play=False,
can_expand=True, can_expand=True,
) )

View File

@ -546,7 +546,10 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
plex_plugin = self.speaker.plex_plugin plex_plugin = self.speaker.plex_plugin
media_id = media_id[len(PLEX_URI_SCHEME) :] media_id = media_id[len(PLEX_URI_SCHEME) :]
payload = json.loads(media_id) payload = json.loads(media_id)
shuffle = payload.pop("shuffle", None) if isinstance(payload, dict):
shuffle = payload.pop("shuffle", False)
else:
shuffle = False
media = lookup_plex_media(self.hass, media_type, json.dumps(payload)) media = lookup_plex_media(self.hass, media_type, json.dumps(payload))
if not kwargs.get(ATTR_MEDIA_ENQUEUE): if not kwargs.get(ATTR_MEDIA_ENQUEUE):
soco.clear_queue() soco.clear_queue()

View File

@ -6,7 +6,7 @@ from homeassistant.components.media_player.const import (
ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_ID,
ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_CONTENT_TYPE,
) )
from homeassistant.components.plex.const import CONF_SERVER_IDENTIFIER from homeassistant.components.plex.const import CONF_SERVER_IDENTIFIER, PLEX_URI_SCHEME
from homeassistant.components.websocket_api.const import ERR_UNKNOWN_ERROR, TYPE_RESULT from homeassistant.components.websocket_api.const import ERR_UNKNOWN_ERROR, TYPE_RESULT
from .const import DEFAULT_DATA from .const import DEFAULT_DATA
@ -120,23 +120,6 @@ async def test_browse_media(
media_players = hass.states.async_entity_ids("media_player") media_players = hass.states.async_entity_ids("media_player")
msg_id = 1 msg_id = 1
# Browse base of non-existent Plex server
await websocket_client.send_json(
{
"id": msg_id,
"type": "media_player/browse_media",
"entity_id": media_players[0],
ATTR_MEDIA_CONTENT_TYPE: "server",
ATTR_MEDIA_CONTENT_ID: "this server does not exist",
}
)
msg = await websocket_client.receive_json()
assert msg["id"] == msg_id
assert msg["type"] == TYPE_RESULT
assert not msg["success"]
assert msg["error"]["code"] == ERR_UNKNOWN_ERROR
# Browse base of Plex server # Browse base of Plex server
msg_id += 1 msg_id += 1
await websocket_client.send_json( await websocket_client.send_json(
@ -153,7 +136,10 @@ async def test_browse_media(
assert msg["success"] assert msg["success"]
result = msg["result"] result = msg["result"]
assert result[ATTR_MEDIA_CONTENT_TYPE] == "server" assert result[ATTR_MEDIA_CONTENT_TYPE] == "server"
assert result[ATTR_MEDIA_CONTENT_ID] == DEFAULT_DATA[CONF_SERVER_IDENTIFIER] assert (
result[ATTR_MEDIA_CONTENT_ID]
== PLEX_URI_SCHEME + DEFAULT_DATA[CONF_SERVER_IDENTIFIER]
)
# Library Sections + Recommended + Playlists # Library Sections + Recommended + Playlists
assert len(result["children"]) == len(mock_plex_server.library.sections()) + 2 assert len(result["children"]) == len(mock_plex_server.library.sections()) + 2
@ -175,7 +161,8 @@ async def test_browse_media(
"type": "media_player/browse_media", "type": "media_player/browse_media",
"entity_id": media_players[0], "entity_id": media_players[0],
ATTR_MEDIA_CONTENT_TYPE: "server", ATTR_MEDIA_CONTENT_TYPE: "server",
ATTR_MEDIA_CONTENT_ID: f"{DEFAULT_DATA[CONF_SERVER_IDENTIFIER]}:{special_keys[0]}", ATTR_MEDIA_CONTENT_ID: PLEX_URI_SCHEME
+ f"{DEFAULT_DATA[CONF_SERVER_IDENTIFIER]}:{special_keys[0]}",
} }
) )
@ -187,7 +174,7 @@ async def test_browse_media(
assert result[ATTR_MEDIA_CONTENT_TYPE] == "server" assert result[ATTR_MEDIA_CONTENT_TYPE] == "server"
assert ( assert (
result[ATTR_MEDIA_CONTENT_ID] result[ATTR_MEDIA_CONTENT_ID]
== f"{DEFAULT_DATA[CONF_SERVER_IDENTIFIER]}:{special_keys[0]}" == PLEX_URI_SCHEME + f"{DEFAULT_DATA[CONF_SERVER_IDENTIFIER]}:{special_keys[0]}"
) )
assert len(result["children"]) == 4 # Hardcoded in fixture assert len(result["children"]) == 4 # Hardcoded in fixture
assert result["children"][0]["media_content_type"] == "mixed" assert result["children"][0]["media_content_type"] == "mixed"
@ -228,7 +215,8 @@ async def test_browse_media(
"type": "media_player/browse_media", "type": "media_player/browse_media",
"entity_id": media_players[0], "entity_id": media_players[0],
ATTR_MEDIA_CONTENT_TYPE: "library", ATTR_MEDIA_CONTENT_TYPE: "library",
ATTR_MEDIA_CONTENT_ID: f"{library_section_id}:{special_keys[0]}", ATTR_MEDIA_CONTENT_ID: PLEX_URI_SCHEME
+ f"{library_section_id}:{special_keys[0]}",
} }
) )
@ -238,7 +226,10 @@ async def test_browse_media(
assert msg["success"] assert msg["success"]
result = msg["result"] result = msg["result"]
assert result[ATTR_MEDIA_CONTENT_TYPE] == "library" assert result[ATTR_MEDIA_CONTENT_TYPE] == "library"
assert result[ATTR_MEDIA_CONTENT_ID] == f"{library_section_id}:{special_keys[0]}" assert (
result[ATTR_MEDIA_CONTENT_ID]
== PLEX_URI_SCHEME + f"{library_section_id}:{special_keys[0]}"
)
assert len(result["children"]) == 1 assert len(result["children"]) == 1
# Browse into a library radio station hub # Browse into a library radio station hub
@ -280,7 +271,7 @@ async def test_browse_media(
assert msg["success"] assert msg["success"]
result = msg["result"] result = msg["result"]
assert result[ATTR_MEDIA_CONTENT_TYPE] == "library" assert result[ATTR_MEDIA_CONTENT_TYPE] == "library"
result_id = int(result[ATTR_MEDIA_CONTENT_ID]) result_id = int(result[ATTR_MEDIA_CONTENT_ID][len(PLEX_URI_SCHEME) :])
# All items in section + Hubs # All items in section + Hubs
assert ( assert (
len(result["children"]) len(result["children"])
@ -314,7 +305,7 @@ async def test_browse_media(
assert msg["success"] assert msg["success"]
result = msg["result"] result = msg["result"]
assert result[ATTR_MEDIA_CONTENT_TYPE] == "show" assert result[ATTR_MEDIA_CONTENT_TYPE] == "show"
result_id = int(result[ATTR_MEDIA_CONTENT_ID]) result_id = int(result[ATTR_MEDIA_CONTENT_ID][len(PLEX_URI_SCHEME) :])
assert result["title"] == mock_plex_server.fetch_item(result_id).title assert result["title"] == mock_plex_server.fetch_item(result_id).title
assert result["children"][0]["title"] == f"{mock_season.title} ({mock_season.year})" assert result["children"][0]["title"] == f"{mock_season.title} ({mock_season.year})"
@ -344,7 +335,7 @@ async def test_browse_media(
assert msg["success"] assert msg["success"]
result = msg["result"] result = msg["result"]
assert result[ATTR_MEDIA_CONTENT_TYPE] == "season" assert result[ATTR_MEDIA_CONTENT_TYPE] == "season"
result_id = int(result[ATTR_MEDIA_CONTENT_ID]) result_id = int(result[ATTR_MEDIA_CONTENT_ID][len(PLEX_URI_SCHEME) :])
assert ( assert (
result["title"] result["title"]
== f"{mock_season.parentTitle} - {mock_season.title} ({mock_season.year})" == f"{mock_season.parentTitle} - {mock_season.title} ({mock_season.year})"
@ -369,7 +360,7 @@ async def test_browse_media(
assert msg["success"] assert msg["success"]
result = msg["result"] result = msg["result"]
result_id = int(result[ATTR_MEDIA_CONTENT_ID]) result_id = int(result[ATTR_MEDIA_CONTENT_ID][len(PLEX_URI_SCHEME) :])
assert result[ATTR_MEDIA_CONTENT_TYPE] == "library" assert result[ATTR_MEDIA_CONTENT_TYPE] == "library"
assert result["title"] == "Music" assert result["title"] == "Music"
@ -399,7 +390,7 @@ async def test_browse_media(
assert mock_fetch.called assert mock_fetch.called
assert msg["success"] assert msg["success"]
result = msg["result"] result = msg["result"]
result_id = int(result[ATTR_MEDIA_CONTENT_ID]) result_id = int(result[ATTR_MEDIA_CONTENT_ID][len(PLEX_URI_SCHEME) :])
assert result[ATTR_MEDIA_CONTENT_TYPE] == "artist" assert result[ATTR_MEDIA_CONTENT_TYPE] == "artist"
assert result["title"] == mock_artist.title assert result["title"] == mock_artist.title
assert result["children"][0]["title"] == "Radio Station" assert result["children"][0]["title"] == "Radio Station"
@ -420,7 +411,7 @@ async def test_browse_media(
assert msg["success"] assert msg["success"]
result = msg["result"] result = msg["result"]
result_id = int(result[ATTR_MEDIA_CONTENT_ID]) result_id = int(result[ATTR_MEDIA_CONTENT_ID][len(PLEX_URI_SCHEME) :])
assert result[ATTR_MEDIA_CONTENT_TYPE] == "album" assert result[ATTR_MEDIA_CONTENT_TYPE] == "album"
assert ( assert (
result["title"] result["title"]