1 """Support for media browsing."""
3 from __future__
import annotations
8 from jellyfin_apiclient_python
import JellyfinClient
18 from .client_wrapper
import get_artwork_url
23 SUPPORTED_COLLECTION_TYPES,
26 CONTAINER_TYPES_SPECIFIC_MEDIA_CLASS: dict[str, str] = {
27 MediaType.MUSIC: MediaClass.MUSIC,
28 MediaType.SEASON: MediaClass.SEASON,
29 MediaType.TVSHOW: MediaClass.TV_SHOW,
30 "boxset": MediaClass.DIRECTORY,
31 "collection": MediaClass.DIRECTORY,
32 "library": MediaClass.DIRECTORY,
35 PLAYABLE_MEDIA_TYPES = [
44 client: JellyfinClient,
48 """Create response payload for a single media item."""
52 media_content_id = item[
"Id"]
53 media_content_type = CONTENT_TYPE_MAP.get(item[
"Type"], MEDIA_TYPE_NONE)
57 media_content_id=media_content_id,
58 media_content_type=media_content_type,
59 media_class=MEDIA_CLASS_MAP.get(item[
"Type"], MediaClass.DIRECTORY),
60 can_play=bool(media_content_type
in PLAYABLE_MEDIA_TYPES
and media_content_id),
61 can_expand=bool(item.get(
"IsFolder")),
62 children_media_class=
None,
68 hass: HomeAssistant, client: JellyfinClient, user_id: str
70 """Create response payload for root folder."""
71 folders = await hass.async_add_executor_job(client.jellyfin.get_media_folders)
75 for folder
in folders[
"Items"]
76 if folder[
"CollectionType"]
in SUPPORTED_COLLECTION_TYPES
81 media_content_type=
"root",
82 media_class=MediaClass.DIRECTORY,
83 children_media_class=MediaClass.DIRECTORY,
93 client: JellyfinClient,
95 media_content_type: str |
None,
96 media_content_id: str,
98 """Create response payload for the provided media query."""
100 hass, client, user_id, media_content_type, media_content_id
103 if title
is None or media
is None:
104 raise BrowseError(f
"Media not found: {media_content_type} / {media_content_id}")
106 children = await asyncio.gather(
107 *(
item_payload(hass, client, user_id, media_item)
for media_item
in media)
110 response = BrowseMedia(
111 media_class=CONTAINER_TYPES_SPECIFIC_MEDIA_CLASS.get(
112 str(media_content_type), MediaClass.DIRECTORY
114 media_content_id=media_content_id,
115 media_content_type=
str(media_content_type),
117 can_play=bool(media_content_type
in PLAYABLE_MEDIA_TYPES
and media_content_id),
123 response.calculate_children_class()
128 def fetch_item(client: JellyfinClient, item_id: str) -> dict[str, Any] |
None:
129 """Fetch item from Jellyfin server."""
130 result = client.jellyfin.get_item(item_id)
135 item: dict[str, Any] = result
140 client: JellyfinClient,
141 params: dict[str, Any],
142 ) -> list[dict[str, Any]] |
None:
143 """Fetch items from Jellyfin server."""
144 result = client.jellyfin.user_items(params=params)
146 if not result
or "Items" not in result
or len(result[
"Items"]) < 1:
149 items: list[dict[str, Any]] = result[
"Items"]
154 if not item.get(
"IsFolder")
155 or (item.get(
"IsFolder")
and item.get(
"ChildCount", 1) > 0)
161 client: JellyfinClient,
163 media_content_type: str |
None,
164 media_content_id: str,
165 ) -> tuple[str |
None, list[dict[str, Any]] |
None, str |
None]:
166 """Fetch media info."""
167 thumbnail: str |
None =
None
168 title: str |
None =
None
169 media: list[dict[str, Any]] |
None =
None
171 item = await hass.async_add_executor_job(fetch_item, client, media_content_id)
174 return None,
None,
None
179 if item.get(
"IsFolder"):
180 media = await hass.async_add_executor_job(
181 fetch_items, client, {
"parentId": media_content_id,
"fields":
"childCount"}
184 if not media
or len(media) == 0:
187 return title, media, thumbnail
str|None get_artwork_url(JellyfinClient client, dict[str, Any] item, int max_width=600)