Home Assistant Unofficial Reference 2024.12.1
browse_media.py
Go to the documentation of this file.
1 """Support for media browsing."""
2 
3 import logging
4 
5 from afsapi import AFSAPI, FSApiException, OutOfRangeException, Preset
6 
8  BrowseError,
9  BrowseMedia,
10  MediaClass,
11  MediaType,
12 )
13 
14 from .const import MEDIA_CONTENT_ID_CHANNELS, MEDIA_CONTENT_ID_PRESET
15 
16 TOP_LEVEL_DIRECTORIES = {
17  MEDIA_CONTENT_ID_CHANNELS: "Channels",
18  MEDIA_CONTENT_ID_PRESET: "Presets",
19 }
20 
21 FSAPI_ITEM_TYPE_TO_MEDIA_CLASS = {
22  0: MediaClass.DIRECTORY,
23  1: MediaClass.CHANNEL,
24  2: MediaClass.CHANNEL,
25 }
26 
27 _LOGGER = logging.getLogger(__name__)
28 
29 
30 def _item_preset_payload(preset: Preset, player_mode: str) -> BrowseMedia:
31  """Create response payload for a single media item.
32 
33  Used by async_browse_media.
34  """
35  return BrowseMedia(
36  title=preset.name,
37  media_class=MediaClass.CHANNEL,
38  media_content_type=MediaType.CHANNEL,
39  # We add 1 to the preset key to keep it in sync with the numbering shown
40  # on the interface of the device
41  media_content_id=f"{player_mode}/{MEDIA_CONTENT_ID_PRESET}/{int(preset.key)+1}",
42  can_play=True,
43  can_expand=False,
44  )
45 
46 
48  key, item: dict[str, str], player_mode: str, parent_keys: list[str]
49 ) -> BrowseMedia:
50  """Create response payload for a single media item.
51 
52  Used by async_browse_media.
53  """
54  assert "label" in item or "name" in item
55  assert "type" in item
56 
57  title = item.get("label") or item.get("name") or "Unknown"
58  title = title.strip()
59 
60  media_content_id = "/".join(
61  [player_mode, MEDIA_CONTENT_ID_CHANNELS, *parent_keys, key]
62  )
63  media_class = (
64  FSAPI_ITEM_TYPE_TO_MEDIA_CLASS.get(int(item["type"])) or MediaClass.CHANNEL
65  )
66 
67  return BrowseMedia(
68  title=title,
69  media_class=media_class,
70  media_content_type=MediaClass.CHANNEL,
71  media_content_id=media_content_id,
72  can_play=(media_class != MediaClass.DIRECTORY),
73  can_expand=(media_class == MediaClass.DIRECTORY),
74  )
75 
76 
77 async def browse_top_level(current_mode, afsapi: AFSAPI):
78  """Create response payload to describe contents of a specific library.
79 
80  Used by async_browse_media.
81  """
82 
83  children = [
84  BrowseMedia(
85  title=name,
86  media_class=MediaClass.DIRECTORY,
87  media_content_type=MediaType.CHANNELS,
88  media_content_id=(
89  f"{current_mode or 'unknown'}/{top_level_media_content_id}"
90  ),
91  can_play=False,
92  can_expand=True,
93  )
94  for top_level_media_content_id, name in TOP_LEVEL_DIRECTORIES.items()
95  ]
96 
97  return BrowseMedia(
98  media_class=MediaClass.DIRECTORY,
99  media_content_id="library",
100  media_content_type=MediaType.CHANNELS,
101  title="Media Library",
102  can_play=False,
103  can_expand=True,
104  children=children,
105  children_media_class=MediaClass.DIRECTORY,
106  )
107 
108 
109 async def browse_node(
110  afsapi: AFSAPI,
111  media_content_type,
112  media_content_id,
113 ):
114  """List the contents of a navigation directory (or preset list)."""
115 
116  player_mode, browse_type, *parent_keys = media_content_id.split("/")
117 
118  title = TOP_LEVEL_DIRECTORIES.get(browse_type, "Unknown")
119 
120  children = []
121  try:
122  if browse_type == MEDIA_CONTENT_ID_PRESET:
123  # Return the presets
124 
125  children = [
126  _item_preset_payload(preset, player_mode=player_mode)
127  for preset in await afsapi.get_presets()
128  ]
129 
130  else:
131  # Browse to correct folder
132  await afsapi.nav_select_folder_via_path(parent_keys)
133 
134  # Return items in this folder
135  children = [
136  _item_payload(key, item, player_mode, parent_keys=parent_keys)
137  async for key, item in await afsapi.nav_list()
138  ]
139  except OutOfRangeException as err:
140  raise BrowseError("The requested item is out of range") from err
141  except FSApiException as err:
142  raise BrowseError(str(err)) from err
143 
144  return BrowseMedia(
145  title=title,
146  media_content_id=media_content_id,
147  media_content_type=MediaType.CHANNELS,
148  media_class=MediaClass.DIRECTORY,
149  can_play=False,
150  can_expand=True,
151  children=children,
152  children_media_class=MediaType.CHANNEL,
153  )
BrowseMedia _item_preset_payload(Preset preset, str player_mode)
Definition: browse_media.py:30
def browse_node(AFSAPI afsapi, media_content_type, media_content_id)
def browse_top_level(current_mode, AFSAPI afsapi)
Definition: browse_media.py:77
BrowseMedia _item_payload(key, dict[str, str] item, str player_mode, list[str] parent_keys)
Definition: browse_media.py:49