Add children media class (#39902)

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
Martin Hjelmare 2020-09-11 13:08:13 +02:00 committed by GitHub
parent e208aac834
commit e96fed20c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 181 additions and 67 deletions

View File

@ -29,8 +29,15 @@ PLAYABLE_MEDIA_TYPES = [
MEDIA_TYPE_TRACK, MEDIA_TYPE_TRACK,
] ]
CONTENT_TYPE_MEDIA_CLASS = { CONTAINER_TYPES_SPECIFIC_MEDIA_CLASS = {
"library_music": MEDIA_CLASS_MUSIC, MEDIA_TYPE_ALBUM: MEDIA_CLASS_ALBUM,
MEDIA_TYPE_ARTIST: MEDIA_CLASS_ARTIST,
MEDIA_TYPE_PLAYLIST: MEDIA_CLASS_PLAYLIST,
MEDIA_TYPE_SEASON: MEDIA_CLASS_SEASON,
MEDIA_TYPE_TVSHOW: MEDIA_CLASS_TV_SHOW,
}
CHILD_TYPE_MEDIA_CLASS = {
MEDIA_TYPE_SEASON: MEDIA_CLASS_SEASON, MEDIA_TYPE_SEASON: MEDIA_CLASS_SEASON,
MEDIA_TYPE_ALBUM: MEDIA_CLASS_ALBUM, MEDIA_TYPE_ALBUM: MEDIA_CLASS_ALBUM,
MEDIA_TYPE_ARTIST: MEDIA_CLASS_ARTIST, MEDIA_TYPE_ARTIST: MEDIA_CLASS_ARTIST,
@ -151,8 +158,10 @@ async def build_item_response(media_library, payload):
except UnknownMediaType: except UnknownMediaType:
pass pass
return BrowseMedia( response = BrowseMedia(
media_class=CONTENT_TYPE_MEDIA_CLASS[search_type], media_class=CONTAINER_TYPES_SPECIFIC_MEDIA_CLASS.get(
search_type, MEDIA_CLASS_DIRECTORY
),
media_content_id=search_id, media_content_id=search_id,
media_content_type=search_type, media_content_type=search_type,
title=title, title=title,
@ -162,6 +171,13 @@ async def build_item_response(media_library, payload):
thumbnail=thumbnail, thumbnail=thumbnail,
) )
if search_type == "library_music":
response.children_media_class = MEDIA_CLASS_MUSIC
else:
response.calculate_children_class()
return response
def item_payload(item, media_library): def item_payload(item, media_library):
""" """
@ -170,11 +186,12 @@ def item_payload(item, media_library):
Used by async_browse_media. Used by async_browse_media.
""" """
title = item["label"] title = item["label"]
thumbnail = item.get("thumbnail") thumbnail = item.get("thumbnail")
if thumbnail: if thumbnail:
thumbnail = media_library.thumbnail_url(thumbnail) thumbnail = media_library.thumbnail_url(thumbnail)
media_class = None
if "songid" in item: if "songid" in item:
media_content_type = MEDIA_TYPE_TRACK media_content_type = MEDIA_TYPE_TRACK
media_content_id = f"{item['songid']}" media_content_id = f"{item['songid']}"
@ -213,13 +230,15 @@ def item_payload(item, media_library):
else: else:
# this case is for the top folder of each type # this case is for the top folder of each type
# possible content types: album, artist, movie, library_music, tvshow # possible content types: album, artist, movie, library_music, tvshow
media_class = MEDIA_CLASS_DIRECTORY
media_content_type = item["type"] media_content_type = item["type"]
media_content_id = "" media_content_id = ""
can_play = False can_play = False
can_expand = True can_expand = True
if media_class is None:
try: try:
media_class = CONTENT_TYPE_MEDIA_CLASS[media_content_type] media_class = CHILD_TYPE_MEDIA_CLASS[media_content_type]
except KeyError as err: except KeyError as err:
_LOGGER.debug("Unknown media type received: %s", media_content_type) _LOGGER.debug("Unknown media type received: %s", media_content_type)
raise UnknownMediaType from err raise UnknownMediaType from err

View File

@ -85,6 +85,7 @@ from .const import (
ATTR_SOUND_MODE, ATTR_SOUND_MODE,
ATTR_SOUND_MODE_LIST, ATTR_SOUND_MODE_LIST,
DOMAIN, DOMAIN,
MEDIA_CLASS_DIRECTORY,
SERVICE_CLEAR_PLAYLIST, SERVICE_CLEAR_PLAYLIST,
SERVICE_PLAY_MEDIA, SERVICE_PLAY_MEDIA,
SERVICE_SELECT_SOUND_MODE, SERVICE_SELECT_SOUND_MODE,
@ -816,24 +817,10 @@ class MediaPlayerEntity(Entity):
media_content_type: Optional[str] = None, media_content_type: Optional[str] = None,
media_content_id: Optional[str] = None, media_content_id: Optional[str] = None,
) -> "BrowseMedia": ) -> "BrowseMedia":
""" """Return a BrowseMedia instance.
Return a payload for the "media_player/browse_media" websocket command.
Payload should follow this format: The BrowseMedia instance will be used by the
{ "media_player/browse_media" websocket command.
"title": str - Title of the item
"media_class": str - Media class
"media_content_type": str - see below
"media_content_id": str - see below
- Can be passed back in to browse further
- Can be used as-is with media_player.play_media service
"can_play": bool - If item is playable
"can_expand": bool - If item contains other media
"thumbnail": str (Optional) - URL to image thumbnail for item
"children": list (Optional) - [{<item_with_keys_above>}, ...]
}
Note: Children should omit the children key.
""" """
raise NotImplementedError() raise NotImplementedError()
@ -1054,6 +1041,7 @@ class BrowseMedia:
can_play: bool, can_play: bool,
can_expand: bool, can_expand: bool,
children: Optional[List["BrowseMedia"]] = None, children: Optional[List["BrowseMedia"]] = None,
children_media_class: Optional[str] = None,
thumbnail: Optional[str] = None, thumbnail: Optional[str] = None,
): ):
"""Initialize browse media item.""" """Initialize browse media item."""
@ -1064,10 +1052,14 @@ class BrowseMedia:
self.can_play = can_play self.can_play = can_play
self.can_expand = can_expand self.can_expand = can_expand
self.children = children self.children = children
self.children_media_class = children_media_class
self.thumbnail = thumbnail self.thumbnail = thumbnail
def as_dict(self, *, parent: bool = True) -> dict: def as_dict(self, *, parent: bool = True) -> dict:
"""Convert Media class to browse media dictionary.""" """Convert Media class to browse media dictionary."""
if self.children_media_class is None:
self.calculate_children_class()
response = { response = {
"title": self.title, "title": self.title,
"media_class": self.media_class, "media_class": self.media_class,
@ -1075,6 +1067,7 @@ class BrowseMedia:
"media_content_id": self.media_content_id, "media_content_id": self.media_content_id,
"can_play": self.can_play, "can_play": self.can_play,
"can_expand": self.can_expand, "can_expand": self.can_expand,
"children_media_class": self.children_media_class,
"thumbnail": self.thumbnail, "thumbnail": self.thumbnail,
} }
@ -1089,3 +1082,14 @@ class BrowseMedia:
response["children"] = [] response["children"] = []
return response return response
def calculate_children_class(self) -> None:
"""Count the children media classes and calculate the correct class."""
if self.children is None or len(self.children) == 0:
return
self.children_media_class = MEDIA_CLASS_DIRECTORY
proposed_class = self.children[0].media_class
if all(child.media_class == proposed_class for child in self.children):
self.children_media_class = proposed_class

View File

@ -6,6 +6,7 @@ from typing import List, Optional, Tuple
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_CHANNEL, MEDIA_CLASS_CHANNEL,
MEDIA_CLASS_DIRECTORY,
MEDIA_TYPE_CHANNEL, MEDIA_TYPE_CHANNEL,
MEDIA_TYPE_CHANNELS, MEDIA_TYPE_CHANNELS,
) )
@ -53,11 +54,12 @@ class MediaSourceItem:
base = BrowseMediaSource( base = BrowseMediaSource(
domain=None, domain=None,
identifier=None, identifier=None,
media_class=MEDIA_CLASS_CHANNEL, media_class=MEDIA_CLASS_DIRECTORY,
media_content_type=MEDIA_TYPE_CHANNELS, media_content_type=MEDIA_TYPE_CHANNELS,
title="Media Sources", title="Media Sources",
can_play=False, can_play=False,
can_expand=True, can_expand=True,
children_media_class=MEDIA_CLASS_CHANNEL,
) )
base.children = [ base.children = [
BrowseMediaSource( BrowseMediaSource(

View File

@ -5,6 +5,7 @@ import re
from typing import Optional, Tuple from typing import Optional, Tuple
from homeassistant.components.media_player.const import ( from homeassistant.components.media_player.const import (
MEDIA_CLASS_DIRECTORY,
MEDIA_CLASS_VIDEO, MEDIA_CLASS_VIDEO,
MEDIA_TYPE_VIDEO, MEDIA_TYPE_VIDEO,
) )
@ -91,10 +92,12 @@ class NetatmoSource(MediaSource):
else: else:
path = f"{source}/{camera_id}" path = f"{source}/{camera_id}"
media_class = MEDIA_CLASS_DIRECTORY if event_id is None else MEDIA_CLASS_VIDEO
media = BrowseMediaSource( media = BrowseMediaSource(
domain=DOMAIN, domain=DOMAIN,
identifier=path, identifier=path,
media_class=MEDIA_CLASS_VIDEO, media_class=media_class,
media_content_type=MEDIA_TYPE_VIDEO, media_content_type=MEDIA_TYPE_VIDEO,
title=title, title=title,
can_play=bool( can_play=bool(

View File

@ -12,6 +12,7 @@ from homeassistant.components.media_player import (
) )
from homeassistant.components.media_player.const import ( from homeassistant.components.media_player.const import (
MEDIA_CLASS_CHANNEL, MEDIA_CLASS_CHANNEL,
MEDIA_CLASS_DIRECTORY,
MEDIA_TYPE_CHANNEL, MEDIA_TYPE_CHANNEL,
MEDIA_TYPE_CHANNELS, MEDIA_TYPE_CHANNELS,
SUPPORT_BROWSE_MEDIA, SUPPORT_BROWSE_MEDIA,
@ -289,7 +290,7 @@ class PhilipsTVMediaPlayer(MediaPlayerEntity):
return BrowseMedia( return BrowseMedia(
title="Channels", title="Channels",
media_class=MEDIA_CLASS_CHANNEL, media_class=MEDIA_CLASS_DIRECTORY,
media_content_id="", media_content_id="",
media_content_type=MEDIA_TYPE_CHANNELS, media_content_type=MEDIA_TYPE_CHANNELS,
can_play=False, can_play=False,

View File

@ -26,7 +26,7 @@ class UnknownMediaType(BrowseError):
EXPANDABLES = ["album", "artist", "playlist", "season", "show"] EXPANDABLES = ["album", "artist", "playlist", "season", "show"]
PLAYLISTS_BROWSE_PAYLOAD = { PLAYLISTS_BROWSE_PAYLOAD = {
"title": "Playlists", "title": "Playlists",
"media_class": MEDIA_CLASS_PLAYLIST, "media_class": MEDIA_CLASS_DIRECTORY,
"media_content_id": "all", "media_content_id": "all",
"media_content_type": "playlists", "media_content_type": "playlists",
"can_play": False, "can_play": False,
@ -94,10 +94,21 @@ def browse_media(
if special_folder: if special_folder:
if media_content_type == "server": if media_content_type == "server":
library_or_section = plex_server.library library_or_section = plex_server.library
children_media_class = MEDIA_CLASS_DIRECTORY
title = plex_server.friendly_name title = plex_server.friendly_name
elif media_content_type == "library": elif media_content_type == "library":
library_or_section = plex_server.library.sectionByID(media_content_id) library_or_section = plex_server.library.sectionByID(media_content_id)
title = library_or_section.title title = library_or_section.title
try:
children_media_class = ITEM_TYPE_MEDIA_CLASS[library_or_section.TYPE]
except KeyError as err:
raise BrowseError(
f"Media not found: {media_content_type} / {media_content_id}"
) from err
else:
raise BrowseError(
f"Media not found: {media_content_type} / {media_content_id}"
)
payload = { payload = {
"title": title, "title": title,
@ -107,6 +118,7 @@ def browse_media(
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"children": [], "children": [],
"children_media_class": children_media_class,
} }
method = SPECIAL_METHODS[special_folder] method = SPECIAL_METHODS[special_folder]
@ -116,14 +128,21 @@ def browse_media(
payload["children"].append(item_payload(item)) payload["children"].append(item_payload(item))
except UnknownMediaType: except UnknownMediaType:
continue continue
return BrowseMedia(**payload) return BrowseMedia(**payload)
try:
if media_content_type in ["server", None]: if media_content_type in ["server", None]:
return server_payload(plex_server) return server_payload(plex_server)
if media_content_type == "library": if media_content_type == "library":
return library_payload(plex_server, media_content_id) return library_payload(plex_server, media_content_id)
except UnknownMediaType as err:
raise BrowseError(
f"Media not found: {media_content_type} / {media_content_id}"
) from err
if media_content_type == "playlists": if media_content_type == "playlists":
return playlists_payload(plex_server) return playlists_payload(plex_server)
@ -160,6 +179,11 @@ def item_payload(item):
def library_section_payload(section): def library_section_payload(section):
"""Create response payload for a single library section.""" """Create response payload for a single library section."""
try:
children_media_class = ITEM_TYPE_MEDIA_CLASS[section.TYPE]
except KeyError as err:
_LOGGER.debug("Unknown type received: %s", section.TYPE)
raise UnknownMediaType from err
return BrowseMedia( return BrowseMedia(
title=section.title, title=section.title,
media_class=MEDIA_CLASS_DIRECTORY, media_class=MEDIA_CLASS_DIRECTORY,
@ -167,6 +191,7 @@ def library_section_payload(section):
media_content_type="library", media_content_type="library",
can_play=False, can_play=False,
can_expand=True, can_expand=True,
children_media_class=children_media_class,
) )
@ -194,6 +219,7 @@ def server_payload(plex_server):
can_expand=True, can_expand=True,
) )
server_info.children = [] server_info.children = []
server_info.children_media_class = MEDIA_CLASS_DIRECTORY
server_info.children.append(special_library_payload(server_info, "On Deck")) server_info.children.append(special_library_payload(server_info, "On Deck"))
server_info.children.append(special_library_payload(server_info, "Recently Added")) server_info.children.append(special_library_payload(server_info, "Recently Added"))
for library in plex_server.library.sections(): for library in plex_server.library.sections():
@ -229,4 +255,6 @@ def playlists_payload(plex_server):
playlists_info["children"].append(item_payload(playlist)) playlists_info["children"].append(item_payload(playlist))
except UnknownMediaType: except UnknownMediaType:
continue continue
return BrowseMedia(**playlists_info) response = BrowseMedia(**playlists_info)
response.children_media_class = MEDIA_CLASS_PLAYLIST
return response

View File

@ -13,9 +13,9 @@ from homeassistant.components.media_player.const import (
CONTENT_TYPE_MEDIA_CLASS = { CONTENT_TYPE_MEDIA_CLASS = {
MEDIA_TYPE_APP: MEDIA_CLASS_APP, MEDIA_TYPE_APP: MEDIA_CLASS_APP,
MEDIA_TYPE_APPS: MEDIA_CLASS_APP, MEDIA_TYPE_APPS: MEDIA_CLASS_DIRECTORY,
MEDIA_TYPE_CHANNEL: MEDIA_CLASS_CHANNEL, MEDIA_TYPE_CHANNEL: MEDIA_CLASS_CHANNEL,
MEDIA_TYPE_CHANNELS: MEDIA_CLASS_CHANNEL, MEDIA_TYPE_CHANNELS: MEDIA_CLASS_DIRECTORY,
} }
PLAYABLE_MEDIA_TYPES = [ PLAYABLE_MEDIA_TYPES = [
@ -59,7 +59,7 @@ def build_item_response(coordinator, payload):
return None return None
return BrowseMedia( return BrowseMedia(
media_class=CONTENT_TYPE_MEDIA_CLASS[search_type], media_class=MEDIA_CLASS_DIRECTORY,
media_content_id=search_id, media_content_id=search_id,
media_content_type=search_type, media_content_type=search_type,
title=title, title=title,
@ -139,4 +139,16 @@ def library_payload(coordinator):
) )
) )
if all(
child.media_content_type == MEDIA_TYPE_APPS for child in library_info.children
):
library_info.children_media_class = MEDIA_CLASS_APP
elif all(
child.media_content_type == MEDIA_TYPE_CHANNELS
for child in library_info.children
):
library_info.children_media_class = MEDIA_CLASS_CHANNEL
else:
library_info.children_media_class = MEDIA_CLASS_DIRECTORY
return library_info return library_info

View File

@ -222,6 +222,10 @@ ATTR_STATUS_LIGHT = "status_light"
UNAVAILABLE_VALUES = {"", "NOT_IMPLEMENTED", None} UNAVAILABLE_VALUES = {"", "NOT_IMPLEMENTED", None}
class UnknownMediaType(BrowseError):
"""Unknown media type."""
class SonosData: class SonosData:
"""Storage class for platform global data.""" """Storage class for platform global data."""
@ -1487,7 +1491,20 @@ def build_item_response(media_library, payload):
except IndexError: except IndexError:
title = LIBRARY_TITLES_MAPPING[payload["idstring"]] title = LIBRARY_TITLES_MAPPING[payload["idstring"]]
media_class = SONOS_TO_MEDIA_CLASSES[MEDIA_TYPES_TO_SONOS[payload["search_type"]]] try:
media_class = SONOS_TO_MEDIA_CLASSES[
MEDIA_TYPES_TO_SONOS[payload["search_type"]]
]
except KeyError:
_LOGGER.debug("Unknown media type received %s", payload["search_type"])
return None
children = []
for item in media:
try:
children.append(item_payload(item))
except UnknownMediaType:
pass
return BrowseMedia( return BrowseMedia(
title=title, title=title,
@ -1495,7 +1512,7 @@ def build_item_response(media_library, payload):
media_class=media_class, media_class=media_class,
media_content_id=payload["idstring"], media_content_id=payload["idstring"],
media_content_type=payload["search_type"], media_content_type=payload["search_type"],
children=[item_payload(item) for item in media], children=children,
can_play=can_play(payload["search_type"]), can_play=can_play(payload["search_type"]),
can_expand=can_expand(payload["search_type"]), can_expand=can_expand(payload["search_type"]),
) )
@ -1507,12 +1524,18 @@ def item_payload(item):
Used by async_browse_media. Used by async_browse_media.
""" """
media_type = get_media_type(item)
try:
media_class = SONOS_TO_MEDIA_CLASSES[media_type]
except KeyError as err:
_LOGGER.debug("Unknown media type received %s", media_type)
raise UnknownMediaType from err
return BrowseMedia( return BrowseMedia(
title=item.title, title=item.title,
thumbnail=getattr(item, "album_art_uri", None), thumbnail=getattr(item, "album_art_uri", None),
media_class=SONOS_TO_MEDIA_CLASSES[get_media_type(item)], media_class=media_class,
media_content_id=get_content_id(item), media_content_id=get_content_id(item),
media_content_type=SONOS_TO_MEDIA_TYPES[get_media_type(item)], media_content_type=SONOS_TO_MEDIA_TYPES[media_type],
can_play=can_play(item.item_class), can_play=can_play(item.item_class),
can_expand=can_expand(item), can_expand=can_expand(item),
) )
@ -1524,6 +1547,13 @@ def library_payload(media_library):
Used by async_browse_media. Used by async_browse_media.
""" """
children = []
for item in media_library.browse():
try:
children.append(item_payload(item))
except UnknownMediaType:
pass
return BrowseMedia( return BrowseMedia(
title="Music Library", title="Music Library",
media_class=MEDIA_CLASS_DIRECTORY, media_class=MEDIA_CLASS_DIRECTORY,
@ -1531,7 +1561,7 @@ def library_payload(media_library):
media_content_type="library", media_content_type="library",
can_play=False, can_play=False,
can_expand=True, can_expand=True,
children=[item_payload(item) for item in media_library.browse()], children=children,
) )

View File

@ -105,18 +105,18 @@ LIBRARY_MAP = {
} }
CONTENT_TYPE_MEDIA_CLASS = { CONTENT_TYPE_MEDIA_CLASS = {
"current_user_playlists": MEDIA_CLASS_PLAYLIST, "current_user_playlists": MEDIA_CLASS_DIRECTORY,
"current_user_followed_artists": MEDIA_CLASS_ARTIST, "current_user_followed_artists": MEDIA_CLASS_DIRECTORY,
"current_user_saved_albums": MEDIA_CLASS_ALBUM, "current_user_saved_albums": MEDIA_CLASS_DIRECTORY,
"current_user_saved_tracks": MEDIA_CLASS_TRACK, "current_user_saved_tracks": MEDIA_CLASS_DIRECTORY,
"current_user_saved_shows": MEDIA_CLASS_PODCAST, "current_user_saved_shows": MEDIA_CLASS_DIRECTORY,
"current_user_recently_played": MEDIA_CLASS_TRACK, "current_user_recently_played": MEDIA_CLASS_DIRECTORY,
"current_user_top_artists": MEDIA_CLASS_ARTIST, "current_user_top_artists": MEDIA_CLASS_DIRECTORY,
"current_user_top_tracks": MEDIA_CLASS_TRACK, "current_user_top_tracks": MEDIA_CLASS_DIRECTORY,
"featured_playlists": MEDIA_CLASS_PLAYLIST, "featured_playlists": MEDIA_CLASS_DIRECTORY,
"categories": MEDIA_CLASS_GENRE, "categories": MEDIA_CLASS_DIRECTORY,
"category_playlists": MEDIA_CLASS_PLAYLIST, "category_playlists": MEDIA_CLASS_DIRECTORY,
"new_releases": MEDIA_CLASS_ALBUM, "new_releases": MEDIA_CLASS_DIRECTORY,
MEDIA_TYPE_PLAYLIST: MEDIA_CLASS_PLAYLIST, MEDIA_TYPE_PLAYLIST: MEDIA_CLASS_PLAYLIST,
MEDIA_TYPE_ALBUM: MEDIA_CLASS_ALBUM, MEDIA_TYPE_ALBUM: MEDIA_CLASS_ALBUM,
MEDIA_TYPE_ARTIST: MEDIA_CLASS_ARTIST, MEDIA_TYPE_ARTIST: MEDIA_CLASS_ARTIST,
@ -567,6 +567,7 @@ def build_item_response(spotify, user, payload):
can_expand=True, can_expand=True,
) )
) )
media_item.children_media_class = MEDIA_CLASS_GENRE
return media_item return media_item
if title is None: if title is None:
@ -575,7 +576,7 @@ def build_item_response(spotify, user, payload):
else: else:
title = LIBRARY_MAP.get(payload["media_content_id"]) title = LIBRARY_MAP.get(payload["media_content_id"])
response = { params = {
"title": title, "title": title,
"media_class": media_class, "media_class": media_class,
"media_content_id": media_content_id, "media_content_id": media_content_id,
@ -586,16 +587,16 @@ def build_item_response(spotify, user, payload):
} }
for item in items: for item in items:
try: try:
response["children"].append(item_payload(item)) params["children"].append(item_payload(item))
except (MissingMediaInformation, UnknownMediaType): except (MissingMediaInformation, UnknownMediaType):
continue continue
if "images" in media: if "images" in media:
response["thumbnail"] = fetch_image_url(media) params["thumbnail"] = fetch_image_url(media)
elif image: elif image:
response["thumbnail"] = image params["thumbnail"] = image
return BrowseMedia(**response) return BrowseMedia(**params)
def item_payload(item): def item_payload(item):
@ -624,17 +625,13 @@ def item_payload(item):
payload = { payload = {
"title": item.get("name"), "title": item.get("name"),
"media_class": media_class,
"media_content_id": media_id, "media_content_id": media_id,
"media_content_type": media_type, "media_content_type": media_type,
"can_play": media_type in PLAYABLE_MEDIA_TYPES, "can_play": media_type in PLAYABLE_MEDIA_TYPES,
"can_expand": can_expand, "can_expand": can_expand,
} }
payload = {
**payload,
"media_class": media_class,
}
if "images" in item: if "images" in item:
payload["thumbnail"] = fetch_image_url(item) payload["thumbnail"] = fetch_image_url(item)
elif MEDIA_TYPE_ALBUM in item: elif MEDIA_TYPE_ALBUM in item:
@ -665,7 +662,9 @@ def library_payload():
{"name": item["name"], "type": item["type"], "uri": item["type"]} {"name": item["name"], "type": item["type"], "uri": item["type"]}
) )
) )
return BrowseMedia(**library_info) response = BrowseMedia(**library_info)
response.children_media_class = MEDIA_CLASS_DIRECTORY
return response
def fetch_image_url(item, key="images"): def fetch_image_url(item, key="images"):

View File

@ -17,6 +17,7 @@ async def test_browse_media_as_dict():
title="media/", title="media/",
can_play=False, can_play=False,
can_expand=True, can_expand=True,
children_media_class=MEDIA_CLASS_MUSIC,
) )
base.children = [ base.children = [
models.BrowseMediaSource( models.BrowseMediaSource(
@ -37,6 +38,7 @@ async def test_browse_media_as_dict():
assert item["media_content_id"] == f"{const.URI_SCHEME}{const.DOMAIN}/media" assert item["media_content_id"] == f"{const.URI_SCHEME}{const.DOMAIN}/media"
assert not item["can_play"] assert not item["can_play"]
assert item["can_expand"] assert item["can_expand"]
assert item["children_media_class"] == MEDIA_CLASS_MUSIC
assert len(item["children"]) == 1 assert len(item["children"]) == 1
assert item["children"][0]["title"] == "test.mp3" assert item["children"][0]["title"] == "test.mp3"
assert item["children"][0]["media_class"] == MEDIA_CLASS_MUSIC assert item["children"][0]["media_class"] == MEDIA_CLASS_MUSIC
@ -62,6 +64,7 @@ async def test_browse_media_parent_no_children():
assert not item["can_play"] assert not item["can_play"]
assert item["can_expand"] assert item["can_expand"]
assert len(item["children"]) == 0 assert len(item["children"]) == 0
assert item["children_media_class"] is None
async def test_media_source_default_name(): async def test_media_source_default_name():

View File

@ -409,6 +409,11 @@ class MockPlexLibrarySection:
if self.title == "Photos": if self.title == "Photos":
return "photo" return "photo"
@property
def TYPE(self):
"""Return the library type."""
return self.type
@property @property
def key(self): def key(self):
"""Mock the key identifier property.""" """Mock the key identifier property."""

View File

@ -16,6 +16,9 @@ from homeassistant.components.media_player.const import (
ATTR_MEDIA_TITLE, ATTR_MEDIA_TITLE,
ATTR_MEDIA_VOLUME_MUTED, ATTR_MEDIA_VOLUME_MUTED,
DOMAIN as MP_DOMAIN, DOMAIN as MP_DOMAIN,
MEDIA_CLASS_APP,
MEDIA_CLASS_CHANNEL,
MEDIA_CLASS_DIRECTORY,
MEDIA_TYPE_APP, MEDIA_TYPE_APP,
MEDIA_TYPE_APPS, MEDIA_TYPE_APPS,
MEDIA_TYPE_CHANNEL, MEDIA_TYPE_CHANNEL,
@ -499,6 +502,7 @@ async def test_media_browse(hass, aioclient_mock, hass_ws_client):
assert msg["result"] assert msg["result"]
assert msg["result"]["title"] == "Media Library" assert msg["result"]["title"] == "Media Library"
assert msg["result"]["media_class"] == MEDIA_CLASS_DIRECTORY
assert msg["result"]["media_content_type"] == "library" assert msg["result"]["media_content_type"] == "library"
assert msg["result"]["can_expand"] assert msg["result"]["can_expand"]
assert not msg["result"]["can_play"] assert not msg["result"]["can_play"]
@ -523,10 +527,12 @@ async def test_media_browse(hass, aioclient_mock, hass_ws_client):
assert msg["result"] assert msg["result"]
assert msg["result"]["title"] == "Apps" assert msg["result"]["title"] == "Apps"
assert msg["result"]["media_class"] == MEDIA_CLASS_DIRECTORY
assert msg["result"]["media_content_type"] == MEDIA_TYPE_APPS assert msg["result"]["media_content_type"] == MEDIA_TYPE_APPS
assert msg["result"]["can_expand"] assert msg["result"]["can_expand"]
assert not msg["result"]["can_play"] assert not msg["result"]["can_play"]
assert len(msg["result"]["children"]) == 11 assert len(msg["result"]["children"]) == 11
assert msg["result"]["children_media_class"] == MEDIA_CLASS_APP
assert msg["result"]["children"][0]["title"] == "Satellite TV" assert msg["result"]["children"][0]["title"] == "Satellite TV"
assert msg["result"]["children"][0]["media_content_type"] == MEDIA_TYPE_APP assert msg["result"]["children"][0]["media_content_type"] == MEDIA_TYPE_APP
@ -565,10 +571,12 @@ async def test_media_browse(hass, aioclient_mock, hass_ws_client):
assert msg["result"] assert msg["result"]
assert msg["result"]["title"] == "Channels" assert msg["result"]["title"] == "Channels"
assert msg["result"]["media_class"] == MEDIA_CLASS_DIRECTORY
assert msg["result"]["media_content_type"] == MEDIA_TYPE_CHANNELS assert msg["result"]["media_content_type"] == MEDIA_TYPE_CHANNELS
assert msg["result"]["can_expand"] assert msg["result"]["can_expand"]
assert not msg["result"]["can_play"] assert not msg["result"]["can_play"]
assert len(msg["result"]["children"]) == 2 assert len(msg["result"]["children"]) == 2
assert msg["result"]["children_media_class"] == MEDIA_CLASS_CHANNEL
assert msg["result"]["children"][0]["title"] == "WhatsOn" assert msg["result"]["children"][0]["title"] == "WhatsOn"
assert msg["result"]["children"][0]["media_content_type"] == MEDIA_TYPE_CHANNEL assert msg["result"]["children"][0]["media_content_type"] == MEDIA_TYPE_CHANNEL