async def library_payload(player): """Create response payload to describe contents of library.""" library_info = { "title": "Music Library", "media_class": MEDIA_CLASS_DIRECTORY, "media_content_id": "library", "media_content_type": "library", "can_play": False, "can_expand": True, "children": [], } for item in LIBRARY: media_class = CONTENT_TYPE_MEDIA_CLASS[item] result = await player.async_browse( MEDIA_TYPE_TO_SQUEEZEBOX[item], limit=1, ) if result is not None and result.get("items") is not None: library_info["children"].append( BrowseMedia( title=item, media_class=media_class["children"], media_content_id=item, media_content_type=item, can_play=True, can_expand=True, )) response = BrowseMedia(**library_info) return response
async def async_browse_media_applications(self, expanded): """Return application media objects.""" if expanded: children = [ BrowseMedia( title=application["label"], media_class=MEDIA_CLASS_APP, media_content_id=application_id, media_content_type=MEDIA_TYPE_APP, can_play=True, can_expand=False, thumbnail=self.get_browse_image_url(MEDIA_TYPE_APP, application_id, media_image_id=None), ) for application_id, application in self._tv.applications.items() ] else: children = None return BrowseMedia( title="Applications", media_class=MEDIA_CLASS_DIRECTORY, media_content_id="applications", media_content_type=MEDIA_TYPE_APPS, children_media_class=MEDIA_CLASS_APP, can_play=False, can_expand=True, children=children, )
async def async_browse_media(self, media_content_type=None, media_content_id=None): """Implement the websocket media browsing helper.""" if media_content_id not in (None, "root"): raise BrowseError( f"Media not found: {media_content_type} / {media_content_id}") presets = self._state.get_preset_details() radio = [ BrowseMedia( title=preset.name, media_class=MEDIA_CLASS_MUSIC, media_content_id=f"preset:{preset.index}", media_content_type=MEDIA_TYPE_MUSIC, can_play=True, can_expand=False, ) for preset in presets.values() ] root = BrowseMedia( title="Root", media_class=MEDIA_CLASS_DIRECTORY, media_content_id="root", media_content_type="library", can_play=False, can_expand=True, children=radio, ) return root
async def async_browse_media_channels(self, expanded): """Return channel media objects.""" if expanded: children = [ BrowseMedia( title=channel.get("name", f"Channel: {channel_id}"), media_class=MEDIA_CLASS_CHANNEL, media_content_id=f"alltv/{channel_id}", media_content_type=MEDIA_TYPE_CHANNEL, can_play=True, can_expand=False, ) for channel_id, channel in self._tv.channels.items() ] else: children = None return BrowseMedia( title="Channels", media_class=MEDIA_CLASS_DIRECTORY, media_content_id="channels", media_content_type=MEDIA_TYPE_CHANNELS, children_media_class=MEDIA_CLASS_CHANNEL, can_play=False, can_expand=True, children=children, )
async def browse_node(entity, 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(entity, 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(entity, json_item, title=title, info=info) return BrowseMedia(**payload, children=children)
def library_payload(): """ Create response payload to describe contents of a specific library. Used by async_browse_media. """ library_info = { "title": "Media Library", "media_class": MEDIA_CLASS_DIRECTORY, "media_content_id": "library", "media_content_type": "library", "can_play": False, "can_expand": True, "children": [], } for item in [{"name": n, "type": t} for t, n in LIBRARY_MAP.items()]: library_info["children"].append( item_payload({ "name": item["name"], "type": item["type"], "uri": item["type"] })) response = BrowseMedia(**library_info) response.children_media_class = MEDIA_CLASS_DIRECTORY return response
def library_payload(roon_server, zone_id, media_content_id): """Create response payload for the library.""" opts = { "hierarchy": "browse", "zone_or_output_id": zone_id, "count": ITEM_LIMIT, } # Roon starts browsing for a zone where it left off - so start from the top unless otherwise specified if media_content_id is None or media_content_id == "Explore": opts["pop_all"] = True content_id = "Explore" else: opts["item_key"] = media_content_id content_id = media_content_id result_header = roon_server.roonapi.browse_browse(opts) _LOGGER.debug("Result header %s", result_header) header = result_header["list"] title = header.get("title") subtitle = header.get("subtitle") if subtitle is None: list_title = title else: list_title = f"{title} ({subtitle})" total_count = header["count"] library_image_id = header.get("image_key") library_info = BrowseMedia( title=list_title, media_content_id=content_id, media_content_type="library", media_class=MEDIA_CLASS_DIRECTORY, can_play=False, can_expand=True, children=[], ) result_detail = roon_server.roonapi.browse_load(opts) _LOGGER.debug("Result detail %s", result_detail) items = result_detail["items"] count = len(items) if count < total_count: _LOGGER.debug("Exceeded limit of %d, loaded %d/%d", ITEM_LIMIT, count, total_count) for item in items: if item.get("title") in EXCLUDE_ITEMS: continue entry = item_payload(roon_server, item, library_image_id) library_info.children.append(entry) return library_info
def item_payload(item, coordinator, get_thumbnail_url=None): """ Create response payload for a single media item. Used by async_browse_media. """ thumbnail = None if "app_id" in item: media_content_type = MEDIA_TYPE_APP media_content_id = item["app_id"] if get_thumbnail_url: thumbnail = get_thumbnail_url(media_content_type, media_content_id) elif "channel_number" in item: media_content_type = MEDIA_TYPE_CHANNEL media_content_id = item["channel_number"] else: media_content_type = item["type"] media_content_id = "" title = item["title"] can_play = media_content_type in PLAYABLE_MEDIA_TYPES and media_content_id can_expand = media_content_type in EXPANDABLE_MEDIA_TYPES return BrowseMedia( title=title, media_class=CONTENT_TYPE_MEDIA_CLASS[media_content_type], media_content_type=media_content_type, media_content_id=media_content_id, can_play=can_play, can_expand=can_expand, thumbnail=thumbnail, )
def library_payload(media_library, get_thumbnail_url=None): """ Create response payload to describe contents of a specific library. Used by async_browse_media. """ if not media_library.browse_by_idstring( "tracks", "", max_items=1, ): raise BrowseError("Local library not found") children = [] for item in media_library.browse(): with suppress(UnknownMediaType): children.append(item_payload(item, get_thumbnail_url)) return BrowseMedia( title="Music Library", media_class=MEDIA_CLASS_DIRECTORY, media_content_id="library", media_content_type="library", can_play=False, can_expand=True, children=children, )
def item_payload(item): """Create response payload for a single media item.""" try: media_class = ITEM_TYPE_MEDIA_CLASS[item.type] except KeyError as err: _LOGGER.debug("Unknown type received: %s", item.type) raise UnknownMediaType from err payload = { "title": item.title, "media_class": media_class, "media_content_id": str(item.ratingKey), "media_content_type": item.type, "can_play": True, "can_expand": item.type in EXPANDABLES, } if hasattr(item, "thumbUrl"): entity.plex_server.thumbnail_cache.setdefault( 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)
def item_payload(item, get_thumbnail_url=None): """ Create response payload for a single media item. 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 content_id = get_content_id(item) thumbnail = None if getattr(item, "album_art_uri", None): thumbnail = get_thumbnail_url(media_class, content_id) return BrowseMedia( title=item.title, thumbnail=thumbnail, media_class=media_class, media_content_id=content_id, media_content_type=SONOS_TO_MEDIA_TYPES[media_type], can_play=can_play(item.item_class), can_expand=can_expand(item), )
def build_item_response(media_library, payload, get_thumbnail_url=None): """Create response payload for the provided media query.""" if payload["search_type"] == MEDIA_TYPE_ALBUM and payload[ "idstring"].startswith(("A:GENRE", "A:COMPOSER")): payload["idstring"] = "A:ALBUMARTIST/" + "/".join( payload["idstring"].split("/")[2:]) media = media_library.browse_by_idstring( MEDIA_TYPES_TO_SONOS[payload["search_type"]], payload["idstring"], full_album_art_uri=True, max_items=0, ) if media is None: return thumbnail = None title = None # Fetch album info for titles and thumbnails # Can't be extracted from track info if (payload["search_type"] == MEDIA_TYPE_ALBUM and media[0].item_class == "object.item.audioItem.musicTrack"): item = get_media(media_library, payload["idstring"], SONOS_ALBUM_ARTIST) title = getattr(item, "title", None) thumbnail = get_thumbnail_url(SONOS_ALBUM_ARTIST, payload["idstring"]) if not title: try: title = urllib.parse.unquote(payload["idstring"].split("/")[1]) except IndexError: title = LIBRARY_TITLES_MAPPING[payload["idstring"]] 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: with suppress(UnknownMediaType): children.append(item_payload(item, get_thumbnail_url)) return BrowseMedia( title=title, thumbnail=thumbnail, media_class=media_class, media_content_id=payload["idstring"], media_content_type=payload["search_type"], children=children, can_play=can_play(payload["search_type"]), can_expand=can_expand(payload["search_type"]), )
def _list_payload(item, children=None): return BrowseMedia( title=item["name"], media_class=MEDIA_CLASS_DIRECTORY, children_media_class=_item_to_children_media_class(item), media_content_type=MEDIA_TYPE_MUSIC, media_content_id=json.dumps(item), can_play=False, can_expand=True, )
def playlists_payload(): """Create response payload for all available playlists.""" playlists_info = {**PLAYLISTS_BROWSE_PAYLOAD, "children": []} for playlist in entity.plex_server.playlists(): try: playlists_info["children"].append(item_payload(playlist)) except UnknownMediaType: continue response = BrowseMedia(**playlists_info) response.children_media_class = MEDIA_CLASS_PLAYLIST return response
def special_library_payload(parent_payload, special_type): """Create response payload for special library folders.""" title = f"{special_type} ({parent_payload.title})" return BrowseMedia( title=title, media_class=parent_payload.media_class, media_content_id=f"{parent_payload.media_content_id}:{special_type}", media_content_type=parent_payload.media_content_type, can_play=False, can_expand=True, children_media_class=parent_payload.children_media_class, )
async def browse_top_level(media_library): """Browse the top-level of a Volumio media hierarchy.""" navigation = await media_library.browse() children = [_list_payload(item) for item in navigation["lists"]] return BrowseMedia( media_class=MEDIA_CLASS_DIRECTORY, media_content_id="library", media_content_type="library", title="Media Library", can_play=False, can_expand=True, children=children, )
def server_payload(plex_server): """Create response payload to describe libraries of the Plex server.""" server_info = BrowseMedia( title=plex_server.friendly_name, media_class=MEDIA_CLASS_DIRECTORY, media_content_id=plex_server.machine_identifier, media_content_type="server", can_play=False, can_expand=True, children_media_class=MEDIA_CLASS_DIRECTORY, ) server_info.children = [] server_info.children.append(special_library_payload( server_info, "On Deck")) server_info.children.append( special_library_payload(server_info, "Recently Added")) for library in plex_server.library.sections(): if library.type == "photo": continue server_info.children.append(library_section_payload(library)) server_info.children.append(BrowseMedia(**PLAYLISTS_BROWSE_PAYLOAD)) return server_info
async def async_browse_media_favorites(self, list_id, expanded): """Return channel media objects.""" if expanded: favorites = await self._tv.getFavoriteList(list_id) if favorites: def get_name(channel): channel_data = self._tv.channels.get(str(channel["ccid"])) if channel_data: return channel_data["name"] return f"Channel: {channel['ccid']}" children = [ BrowseMedia( title=get_name(channel), media_class=MEDIA_CLASS_CHANNEL, media_content_id=f"{list_id}/{channel['ccid']}", media_content_type=MEDIA_TYPE_CHANNEL, can_play=True, can_expand=False, ) for channel in favorites ] else: children = None else: children = None favorite = self._tv.favorite_lists[list_id] return BrowseMedia( title=favorite.get("name", f"Favorites {list_id}"), media_class=MEDIA_CLASS_DIRECTORY, media_content_id=f"favorites/{list_id}", media_content_type=MEDIA_TYPE_CHANNELS, children_media_class=MEDIA_CLASS_CHANNEL, can_play=False, can_expand=True, children=children, )
async def async_browse_media_root(self): """Return root media objects.""" return BrowseMedia( title="Library", media_class=MEDIA_CLASS_DIRECTORY, media_content_id="", media_content_type="", can_play=False, can_expand=True, children=[ await self.async_browse_media_channels(False), await self.async_browse_media_applications(False), await self.async_browse_media_favorite_lists(False), ], )
def library_section_payload(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( title=section.title, media_class=MEDIA_CLASS_DIRECTORY, media_content_id=str(section.key), media_content_type="library", can_play=False, can_expand=True, children_media_class=children_media_class, )
def item_payload(roon_server, item, list_image_id): """Create response payload for a single media item.""" title = item["title"] subtitle = item.get("subtitle") if subtitle is None: display_title = title else: display_title = f"{title} ({subtitle})" image_id = item.get("image_key") or list_image_id image = None if image_id: image = roon_server.roonapi.get_image(image_id) media_content_id = item["item_key"] media_content_type = "library" hint = item.get("hint") if hint == "list": media_class = MEDIA_CLASS_DIRECTORY can_expand = True elif hint == "action_list": media_class = MEDIA_CLASS_PLAYLIST can_expand = False elif hint == "action": media_content_type = "track" media_class = MEDIA_CLASS_TRACK can_expand = False else: # Roon API says to treat unknown as a list media_class = MEDIA_CLASS_DIRECTORY can_expand = True _LOGGER.warning("Unknown hint %s - %s", title, hint) payload = { "title": display_title, "media_class": media_class, "media_content_id": media_content_id, "media_content_type": media_content_type, "can_play": True, "can_expand": can_expand, "thumbnail": image, } return BrowseMedia(**payload)
def library_payload(coordinator, get_thumbnail_url=None): """ Create response payload to describe contents of a specific library. Used by async_browse_media. """ library_info = BrowseMedia( media_class=MEDIA_CLASS_DIRECTORY, media_content_id="library", media_content_type="library", title="Media Library", can_play=False, can_expand=True, children=[], ) library = { MEDIA_TYPE_APPS: "Apps", MEDIA_TYPE_CHANNELS: "Channels", } for item in [{ "title": name, "type": type_ } for type_, name in library.items()]: if (item["type"] == MEDIA_TYPE_CHANNELS and coordinator.data.info.device_type != "tv"): continue library_info.children.append( item_payload( { "title": item["title"], "type": item["type"] }, coordinator, get_thumbnail_url, )) 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 return library_info
def build_item_response(coordinator, payload, get_thumbnail_url=None): """Create response payload for the provided media query.""" search_id = payload["search_id"] search_type = payload["search_type"] thumbnail = None title = None media = None children_media_class = None if search_type == MEDIA_TYPE_APPS: title = "Apps" media = [{ "app_id": item.app_id, "title": item.name, "type": MEDIA_TYPE_APP } for item in coordinator.data.apps] children_media_class = MEDIA_CLASS_APP elif search_type == MEDIA_TYPE_CHANNELS: title = "Channels" media = [{ "channel_number": item.number, "title": item.name, "type": MEDIA_TYPE_CHANNEL, } for item in coordinator.data.channels] children_media_class = MEDIA_CLASS_CHANNEL if media is None: return None return BrowseMedia( media_class=CONTAINER_TYPES_SPECIFIC_MEDIA_CLASS.get( search_type, MEDIA_CLASS_DIRECTORY), media_content_id=search_id, media_content_type=search_type, title=title, can_play=search_type in PLAYABLE_MEDIA_TYPES and search_id, can_expand=True, children=[ item_payload(item, coordinator, get_thumbnail_url) for item in media ], children_media_class=children_media_class, thumbnail=thumbnail, )
def item_payload(item: InstalledPackage, images: dict[str, list[Image]]): """Create response payload for a single media item.""" thumbnail = None image = _find_media_image(images.get(item.one_store_product_id, [])) if image is not None: thumbnail = image.uri if thumbnail[0] == "/": thumbnail = f"https:{thumbnail}" return BrowseMedia( media_class=TYPE_MAP[item.content_type]["class"], media_content_id=item.one_store_product_id, media_content_type=TYPE_MAP[item.content_type]["type"], title=item.name, can_play=True, can_expand=False, thumbnail=thumbnail, )
async def async_browse_media_favorite_lists(self, expanded): """Return favorite media objects.""" if self._tv.favorite_lists and expanded: children = [ await self.async_browse_media_favorites(list_id, False) for list_id in self._tv.favorite_lists ] else: children = None return BrowseMedia( title="Favorites", media_class=MEDIA_CLASS_DIRECTORY, media_content_id="favorite_lists", media_content_type=MEDIA_TYPE_CHANNELS, children_media_class=MEDIA_CLASS_CHANNEL, can_play=False, can_expand=True, children=children, )
def item_payload(item): """ Create response payload for a single media item. Used by async_browse_media. """ try: media_type = item["type"] media_id = item["uri"] except KeyError as err: _LOGGER.debug("Missing type or URI for media item: %s", item) raise MissingMediaInformation from err try: media_class = CONTENT_TYPE_MEDIA_CLASS[media_type] except KeyError as err: _LOGGER.debug("Unknown media type received: %s", media_type) raise UnknownMediaType from err can_expand = media_type not in [ MEDIA_TYPE_TRACK, MEDIA_TYPE_EPISODE, ] payload = { "title": item.get("name"), "media_class": media_class["parent"], "children_media_class": media_class["children"], "media_content_id": media_id, "media_content_type": media_type, "can_play": media_type in PLAYABLE_MEDIA_TYPES, "can_expand": can_expand, } if "images" in item: payload["thumbnail"] = fetch_image_url(item) elif MEDIA_TYPE_ALBUM in item: payload["thumbnail"] = fetch_image_url(item[MEDIA_TYPE_ALBUM]) return BrowseMedia(**payload)
async def library_payload(): """ Create response payload to describe contents of a specific library. Used by async_browse_media. """ library_info = BrowseMedia( media_class=MEDIA_CLASS_DIRECTORY, media_content_id="library", media_content_type="library", title="Media Library", can_play=False, can_expand=True, children=[], ) library = { "library_music": "Music", MEDIA_TYPE_MOVIE: "Movies", MEDIA_TYPE_TVSHOW: "TV shows", MEDIA_TYPE_CHANNEL: "Channels", } library_info.children = await asyncio.gather( *[ item_payload( { "label": item["label"], "type": item["type"], "uri": item["type"], }, ) for item in [ {"label": name, "type": type_} for type_, name in library.items() ] ] ) return library_info
async def build_item_response(media_library, payload, get_thumbnail_url=None): """Create response payload for the provided media query.""" search_id = payload["search_id"] search_type = payload["search_type"] _, title, media = await get_media_info(media_library, search_id, search_type) thumbnail = await get_thumbnail_url(search_type, search_id) if media is None: return None children = await asyncio.gather( *[item_payload(item, get_thumbnail_url) for item in media] ) if search_type in (MEDIA_TYPE_TVSHOW, MEDIA_TYPE_MOVIE) and search_id == "": children.sort(key=lambda x: x.title.replace("The ", "", 1), reverse=False) response = BrowseMedia( media_class=CONTAINER_TYPES_SPECIFIC_MEDIA_CLASS.get( search_type, MEDIA_CLASS_DIRECTORY ), media_content_id=search_id, media_content_type=search_type, title=title, can_play=search_type in PLAYABLE_MEDIA_TYPES and search_id, can_expand=True, children=children, thumbnail=thumbnail, ) if search_type == "library_music": response.children_media_class = MEDIA_CLASS_MUSIC else: response.calculate_children_class() return response
async def build_item_response(entity, player, payload): """Create response payload for search described by payload.""" internal_request = is_internal_request(entity.opp) 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"]: item_id = str(item["id"]) item_thumbnail = None artwork_track_id = item.get("artwork_track_id") if artwork_track_id: if internal_request: item_thumbnail = player.generate_image_url_from_track_id( artwork_track_id) else: 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, )
def browse_media( # noqa: C901 entity, is_internal, media_content_type=None, media_content_id=None): """Implement the websocket media browsing helper.""" def item_payload(item): """Create response payload for a single media item.""" try: media_class = ITEM_TYPE_MEDIA_CLASS[item.type] except KeyError as err: _LOGGER.debug("Unknown type received: %s", item.type) raise UnknownMediaType from err payload = { "title": item.title, "media_class": media_class, "media_content_id": str(item.ratingKey), "media_content_type": item.type, "can_play": True, "can_expand": item.type in EXPANDABLES, } if hasattr(item, "thumbUrl"): entity.plex_server.thumbnail_cache.setdefault( 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) def library_payload(library_id): """Create response payload to describe contents of a specific library.""" library = entity.plex_server.library.sectionByID(library_id) library_info = library_section_payload(library) library_info.children = [] library_info.children.append( special_library_payload(library_info, "On Deck")) library_info.children.append( special_library_payload(library_info, "Recently Added")) for item in library.all(): try: library_info.children.append(item_payload(item)) except UnknownMediaType: continue return library_info def playlists_payload(): """Create response payload for all available playlists.""" playlists_info = {**PLAYLISTS_BROWSE_PAYLOAD, "children": []} for playlist in entity.plex_server.playlists(): try: playlists_info["children"].append(item_payload(playlist)) except UnknownMediaType: continue response = BrowseMedia(**playlists_info) response.children_media_class = MEDIA_CLASS_PLAYLIST return response def build_item_response(payload): """Create response payload for the provided media query.""" media = entity.plex_server.lookup_media(**payload) if media is None: return None try: media_info = item_payload(media) except UnknownMediaType: return None if media_info.can_expand: media_info.children = [] for item in media: try: media_info.children.append(item_payload(item)) except UnknownMediaType: continue return media_info if media_content_id and ":" in media_content_id: media_content_id, special_folder = media_content_id.split(":") else: 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 media_content_type == "server": library_or_section = entity.plex_server.library children_media_class = MEDIA_CLASS_DIRECTORY title = entity.plex_server.friendly_name elif media_content_type == "library": library_or_section = entity.plex_server.library.sectionByID( int(media_content_id)) title = library_or_section.title try: children_media_class = ITEM_TYPE_MEDIA_CLASS[ library_or_section.TYPE] except KeyError as err: raise BrowseError( f"Unknown type received: {library_or_section.TYPE}" ) from err else: raise BrowseError( f"Media not found: {media_content_type} / {media_content_id}") payload = { "title": title, "media_class": MEDIA_CLASS_DIRECTORY, "media_content_id": f"{media_content_id}:{special_folder}", "media_content_type": media_content_type, "can_play": False, "can_expand": True, "children": [], "children_media_class": children_media_class, } method = SPECIAL_METHODS[special_folder] items = getattr(library_or_section, method)() for item in items: try: payload["children"].append(item_payload(item)) except UnknownMediaType: continue return BrowseMedia(**payload) try: if media_content_type in ["server", None]: return server_payload(entity.plex_server) if media_content_type == "library": return library_payload(int(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": return playlists_payload() payload = { "media_type": DOMAIN, "plex_key": int(media_content_id), } response = build_item_response(payload) if response is None: raise BrowseError( f"Media not found: {media_content_type} / {media_content_id}") return response