1 """Support for media browsing."""
3 from __future__
import annotations
8 from pysqueezebox
import Player
31 MEDIA_TYPE_TO_SQUEEZEBOX = {
32 "Favorites":
"favorites",
36 "Playlists":
"playlists",
38 "New Music":
"new music",
39 MediaType.ALBUM:
"album",
40 MediaType.ARTIST:
"artist",
41 MediaType.TRACK:
"title",
42 MediaType.PLAYLIST:
"playlist",
43 MediaType.GENRE:
"genre",
46 SQUEEZEBOX_ID_BY_TYPE = {
47 MediaType.ALBUM:
"album_id",
48 MediaType.ARTIST:
"artist_id",
49 MediaType.TRACK:
"track_id",
50 MediaType.PLAYLIST:
"playlist_id",
51 MediaType.GENRE:
"genre_id",
52 "Favorites":
"item_id",
55 CONTENT_TYPE_MEDIA_CLASS: dict[str | MediaType, dict[str, MediaClass |
None]] = {
56 "Favorites": {
"item": MediaClass.DIRECTORY,
"children": MediaClass.TRACK},
57 "Artists": {
"item": MediaClass.DIRECTORY,
"children": MediaClass.ARTIST},
58 "Albums": {
"item": MediaClass.DIRECTORY,
"children": MediaClass.ALBUM},
59 "Tracks": {
"item": MediaClass.DIRECTORY,
"children": MediaClass.TRACK},
60 "Playlists": {
"item": MediaClass.DIRECTORY,
"children": MediaClass.PLAYLIST},
61 "Genres": {
"item": MediaClass.DIRECTORY,
"children": MediaClass.GENRE},
62 "New Music": {
"item": MediaClass.DIRECTORY,
"children": MediaClass.ALBUM},
63 MediaType.ALBUM: {
"item": MediaClass.ALBUM,
"children": MediaClass.TRACK},
64 MediaType.ARTIST: {
"item": MediaClass.ARTIST,
"children": MediaClass.ALBUM},
65 MediaType.TRACK: {
"item": MediaClass.TRACK,
"children":
None},
66 MediaType.GENRE: {
"item": MediaClass.GENRE,
"children": MediaClass.ARTIST},
67 MediaType.PLAYLIST: {
"item": MediaClass.PLAYLIST,
"children": MediaClass.TRACK},
70 CONTENT_TYPE_TO_CHILD_TYPE = {
71 MediaType.ALBUM: MediaType.TRACK,
72 MediaType.PLAYLIST: MediaType.PLAYLIST,
73 MediaType.ARTIST: MediaType.ALBUM,
74 MediaType.GENRE: MediaType.ARTIST,
75 "Artists": MediaType.ARTIST,
76 "Albums": MediaType.ALBUM,
77 "Tracks": MediaType.TRACK,
78 "Playlists": MediaType.PLAYLIST,
79 "Genres": MediaType.GENRE,
81 "New Music": MediaType.ALBUM,
88 entity: MediaPlayerEntity, player: Player, payload: dict[str, str |
None]
90 """Create response payload for search described by payload."""
94 search_id = payload[
"search_id"]
95 search_type = payload[
"search_type"]
97 search_type
is not None
99 media_class = CONTENT_TYPE_MEDIA_CLASS[search_type]
103 if search_id
and search_id != search_type:
104 browse_id = (SQUEEZEBOX_ID_BY_TYPE[search_type], search_id)
108 result = await player.async_browse(
109 MEDIA_TYPE_TO_SQUEEZEBOX[search_type],
114 if result
is not None and result.get(
"items"):
115 item_type = CONTENT_TYPE_TO_CHILD_TYPE[search_type]
118 for item
in result[
"items"]:
119 item_id =
str(item[
"id"])
120 item_thumbnail: str |
None =
None
122 child_item_type: MediaType | str = item_type
123 child_media_class = CONTENT_TYPE_MEDIA_CLASS[item_type]
124 can_expand = child_media_class[
"children"]
is not None
127 if search_type ==
"Favorites":
128 if "album_id" in item:
129 item_id =
str(item[
"album_id"])
130 child_item_type = MediaType.ALBUM
131 child_media_class = CONTENT_TYPE_MEDIA_CLASS[MediaType.ALBUM]
134 elif item[
"hasitems"]:
135 child_item_type =
"Favorites"
136 child_media_class = CONTENT_TYPE_MEDIA_CLASS[
"Favorites"]
140 child_item_type =
"Favorites"
141 child_media_class = CONTENT_TYPE_MEDIA_CLASS[MediaType.TRACK]
145 if artwork_track_id := item.get(
"artwork_track_id"):
147 item_thumbnail = player.generate_image_url_from_track_id(
150 elif item_type
is not None:
151 item_thumbnail = entity.get_browse_image_url(
152 item_type, item_id, artwork_track_id
155 item_thumbnail = item.get(
"image_url")
157 assert child_media_class[
"item"]
is not None
161 media_class=child_media_class[
"item"],
162 media_content_id=item_id,
163 media_content_type=child_item_type,
165 can_expand=can_expand,
166 thumbnail=item_thumbnail,
171 raise BrowseError(f
"Media not found: {search_type} / {search_id}")
173 assert media_class[
"item"]
is not None
175 search_id = search_type
177 title=result.get(
"title"),
178 media_class=media_class[
"item"],
179 children_media_class=media_class[
"children"],
180 media_content_id=search_id,
181 media_content_type=search_type,
182 can_play=search_type !=
"Favorites",
189 """Create response payload to describe contents of library."""
190 library_info: dict[str, Any] = {
191 "title":
"Music Library",
192 "media_class": MediaClass.DIRECTORY,
193 "media_content_id":
"library",
194 "media_content_type":
"library",
201 media_class = CONTENT_TYPE_MEDIA_CLASS[item]
203 result = await player.async_browse(
204 MEDIA_TYPE_TO_SQUEEZEBOX[item],
207 if result
is not None and result.get(
"items")
is not None:
208 assert media_class[
"children"]
is not None
209 library_info[
"children"].append(
212 media_class=media_class[
"children"],
213 media_content_id=item,
214 media_content_type=item,
215 can_play=item !=
"Favorites",
221 browse = await media_source.async_browse_media(
222 hass,
None, content_filter=media_source_content_filter
225 if browse.domain
is None:
226 library_info[
"children"].extend(browse.children)
228 library_info[
"children"].append(browse)
234 """Content filter for media sources."""
235 return item.media_content_type.startswith(
"audio/")
239 """Generate playlist from browsing payload."""
240 media_type = payload[
"search_type"]
241 media_id = payload[
"search_id"]
243 if media_type
not in SQUEEZEBOX_ID_BY_TYPE:
244 raise BrowseError(f
"Media type not supported: {media_type}")
246 browse_id = (SQUEEZEBOX_ID_BY_TYPE[media_type], media_id)
247 result = await player.async_browse(
248 "titles", limit=BROWSE_LIMIT, browse_id=browse_id
250 if result
and "items" in result:
251 items: list = result[
"items"]
253 raise BrowseError(f
"Media not found: {media_type} / {media_id}")
bool is_internal_request(HomeAssistant hass)