Home Assistant Unofficial Reference 2024.12.1
media_browser.py
Go to the documentation of this file.
1 """Support to interface with the Roon API."""
2 
3 import logging
4 
5 from homeassistant.components.media_player import BrowseError, BrowseMedia, MediaClass
6 
7 
9  """Unknown media type."""
10 
11 
12 EXCLUDE_ITEMS = {
13  "Play Album",
14  "Play Artist",
15  "Play Playlist",
16  "Play Composer",
17  "Play Now",
18  "Play From Here",
19  "Queue",
20  "Start Radio",
21  "Add Next",
22  "Play Radio",
23  "Play Work",
24  "Settings",
25  "Search",
26  "Search Tidal",
27  "Search Qobuz",
28 }
29 
30 # Maximum number of items to pull back from the API
31 ITEM_LIMIT = 3000
32 
33 _LOGGER = logging.getLogger(__name__)
34 
35 
36 def browse_media(zone_id, roon_server, media_content_type=None, media_content_id=None):
37  """Implement the websocket media browsing helper."""
38  try:
39  _LOGGER.debug("browse_media: %s: %s", media_content_type, media_content_id)
40  if media_content_type in [None, "library"]:
41  return library_payload(roon_server, zone_id, media_content_id)
42 
43  except UnknownMediaType as err:
44  raise BrowseError(
45  f"Media not found: {media_content_type} / {media_content_id}"
46  ) from err
47 
48 
49 def item_payload(roon_server, item, list_image_id):
50  """Create response payload for a single media item."""
51 
52  title = item["title"]
53  if (subtitle := item.get("subtitle")) is None:
54  display_title = title
55  else:
56  display_title = f"{title} ({subtitle})"
57 
58  image_id = item.get("image_key") or list_image_id
59 
60  image = None
61  if image_id:
62  image = roon_server.roonapi.get_image(image_id)
63 
64  media_content_id = item["item_key"]
65  media_content_type = "library"
66 
67  hint = item.get("hint")
68  if hint == "list":
69  media_class = MediaClass.DIRECTORY
70  can_expand = True
71  elif hint == "action_list":
72  media_class = MediaClass.PLAYLIST
73  can_expand = False
74  elif hint == "action":
75  media_content_type = "track"
76  media_class = MediaClass.TRACK
77  can_expand = False
78  else:
79  # Roon API says to treat unknown as a list
80  media_class = MediaClass.DIRECTORY
81  can_expand = True
82  _LOGGER.warning("Unknown hint %s - %s", title, hint)
83 
84  payload = {
85  "title": display_title,
86  "media_class": media_class,
87  "media_content_id": media_content_id,
88  "media_content_type": media_content_type,
89  "can_play": True,
90  "can_expand": can_expand,
91  "thumbnail": image,
92  }
93 
94  return BrowseMedia(**payload)
95 
96 
97 def library_payload(roon_server, zone_id, media_content_id):
98  """Create response payload for the library."""
99 
100  opts = {
101  "hierarchy": "browse",
102  "zone_or_output_id": zone_id,
103  "count": ITEM_LIMIT,
104  }
105 
106  # Roon starts browsing for a zone where it left off - so start from the top unless otherwise specified
107  if media_content_id is None or media_content_id == "Explore":
108  opts["pop_all"] = True
109  content_id = "Explore"
110  else:
111  opts["item_key"] = media_content_id
112  content_id = media_content_id
113 
114  result_header = roon_server.roonapi.browse_browse(opts)
115  _LOGGER.debug("Result header %s", result_header)
116 
117  header = result_header["list"]
118  title = header.get("title")
119 
120  if (subtitle := header.get("subtitle")) is None:
121  list_title = title
122  else:
123  list_title = f"{title} ({subtitle})"
124 
125  total_count = header["count"]
126 
127  library_image_id = header.get("image_key")
128 
129  library_info = BrowseMedia(
130  title=list_title,
131  media_content_id=content_id,
132  media_content_type="library",
133  media_class=MediaClass.DIRECTORY,
134  can_play=False,
135  can_expand=True,
136  children=[],
137  )
138 
139  result_detail = roon_server.roonapi.browse_load(opts)
140  _LOGGER.debug("Result detail %s", result_detail)
141 
142  items = result_detail["items"]
143  count = len(items)
144 
145  if count < total_count:
146  _LOGGER.debug(
147  "Exceeded limit of %d, loaded %d/%d", ITEM_LIMIT, count, total_count
148  )
149 
150  for item in items:
151  if item.get("title") in EXCLUDE_ITEMS:
152  continue
153  entry = item_payload(roon_server, item, library_image_id)
154  library_info.children.append(entry)
155 
156  return library_info
def browse_media(zone_id, roon_server, media_content_type=None, media_content_id=None)
def library_payload(roon_server, zone_id, media_content_id)
def item_payload(roon_server, item, list_image_id)