Home Assistant Unofficial Reference 2024.12.1
media_source.py
Go to the documentation of this file.
1 """System Bridge Media Source Implementation."""
2 
3 from __future__ import annotations
4 
5 from systembridgemodels.media_directories import MediaDirectory
6 from systembridgemodels.media_files import MediaFile, MediaFiles
7 from systembridgemodels.media_get_files import MediaGetFiles
8 
9 from homeassistant.components.media_player import MediaClass
11  MEDIA_CLASS_MAP,
12  MEDIA_MIME_TYPES,
13  BrowseMediaSource,
14  MediaSource,
15  MediaSourceItem,
16  PlayMedia,
17 )
18 from homeassistant.config_entries import ConfigEntry
19 from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TOKEN
20 from homeassistant.core import HomeAssistant
21 
22 from .const import DOMAIN
23 from .coordinator import SystemBridgeDataUpdateCoordinator
24 
25 
26 async def async_get_media_source(hass: HomeAssistant) -> MediaSource:
27  """Set up SystemBridge media source."""
28  return SystemBridgeSource(hass)
29 
30 
32  """Provide System Bridge media files as a media source."""
33 
34  def __init__(
35  self,
36  hass: HomeAssistant,
37  ) -> None:
38  """Initialize source."""
39  super().__init__(DOMAIN)
40  self.namenamename = "System Bridge"
41  self.hass: HomeAssistant = hass
42 
44  self,
45  item: MediaSourceItem,
46  ) -> PlayMedia:
47  """Resolve media to a url."""
48  entry_id, path, mime_type = item.identifier.split("~~", 2)
49  entry = self.hass.config_entries.async_get_entry(entry_id)
50  if entry is None:
51  raise ValueError("Invalid entry")
52  path_split = path.split("/", 1)
53  return PlayMedia(
54  f"{_build_base_url(entry)}&base={path_split[0]}&path={path_split[1]}",
55  mime_type,
56  )
57 
58  async def async_browse_media(
59  self,
60  item: MediaSourceItem,
61  ) -> BrowseMediaSource:
62  """Return media."""
63  if not item.identifier:
64  return self._build_bridges_build_bridges()
65 
66  if "~~" not in item.identifier:
67  entry = self.hass.config_entries.async_get_entry(item.identifier)
68  if entry is None:
69  raise ValueError("Invalid entry")
70  coordinator: SystemBridgeDataUpdateCoordinator = self.hass.data[DOMAIN].get(
71  entry.entry_id
72  )
73  directories = await coordinator.websocket_client.get_directories()
74  return _build_root_paths(entry, directories)
75 
76  entry_id, path = item.identifier.split("~~", 1)
77  entry = self.hass.config_entries.async_get_entry(entry_id)
78  if entry is None:
79  raise ValueError("Invalid entry")
80 
81  coordinator = self.hass.data[DOMAIN].get(entry.entry_id)
82 
83  path_split = path.split("/", 1)
84 
85  files = await coordinator.websocket_client.get_files(
86  MediaGetFiles(
87  base=path_split[0],
88  path=path_split[1] if len(path_split) > 1 else None,
89  )
90  )
91 
92  return _build_media_items(entry, files, path, item.identifier)
93 
94  def _build_bridges(self) -> BrowseMediaSource:
95  """Build bridges for System Bridge media."""
96  children = [
98  domain=DOMAIN,
99  identifier=entry.entry_id,
100  media_class=MediaClass.DIRECTORY,
101  media_content_type="",
102  title=entry.title,
103  can_play=False,
104  can_expand=True,
105  children=[],
106  children_media_class=MediaClass.DIRECTORY,
107  )
108  for entry in self.hass.config_entries.async_entries(DOMAIN)
109  if entry.entry_id is not None
110  ]
111 
112  return BrowseMediaSource(
113  domain=DOMAIN,
114  identifier="",
115  media_class=MediaClass.DIRECTORY,
116  media_content_type="",
117  title=self.namenamename,
118  can_play=False,
119  can_expand=True,
120  children=children,
121  children_media_class=MediaClass.DIRECTORY,
122  )
123 
124 
126  entry: ConfigEntry,
127 ) -> str:
128  """Build base url for System Bridge media."""
129  return (
130  f"http://{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}"
131  f"/api/media/file/data?token={entry.data[CONF_TOKEN]}"
132  )
133 
134 
136  entry: ConfigEntry,
137  media_directories: list[MediaDirectory],
138 ) -> BrowseMediaSource:
139  """Build base categories for System Bridge media."""
140  return BrowseMediaSource(
141  domain=DOMAIN,
142  identifier="",
143  media_class=MediaClass.DIRECTORY,
144  media_content_type="",
145  title=entry.title,
146  can_play=False,
147  can_expand=True,
148  children=[
150  domain=DOMAIN,
151  identifier=f"{entry.entry_id}~~{directory.key}",
152  media_class=MediaClass.DIRECTORY,
153  media_content_type="",
154  title=f"{directory.key[:1].capitalize()}{directory.key[1:]}",
155  can_play=False,
156  can_expand=True,
157  children=[],
158  children_media_class=MediaClass.DIRECTORY,
159  )
160  for directory in media_directories
161  ],
162  children_media_class=MediaClass.DIRECTORY,
163  )
164 
165 
167  entry: ConfigEntry,
168  media_files: MediaFiles,
169  path: str,
170  identifier: str,
171 ) -> BrowseMediaSource:
172  """Fetch requested files."""
173  return BrowseMediaSource(
174  domain=DOMAIN,
175  identifier=identifier,
176  media_class=MediaClass.DIRECTORY,
177  media_content_type="",
178  title=f"{entry.title} - {path}",
179  can_play=False,
180  can_expand=True,
181  children=[
182  _build_media_item(identifier, file)
183  for file in media_files.files
184  if file.is_directory
185  or (
186  file.is_file
187  and file.mime_type is not None
188  and file.mime_type.startswith(MEDIA_MIME_TYPES)
189  )
190  ],
191  )
192 
193 
195  path: str,
196  media_file: MediaFile,
197 ) -> BrowseMediaSource:
198  """Build individual media item."""
199  ext = ""
200  if media_file.is_file and media_file.mime_type is not None:
201  ext = f"~~{media_file.mime_type}"
202 
203  if media_file.is_directory or media_file.mime_type is None:
204  media_class = MediaClass.DIRECTORY
205  else:
206  media_class = MEDIA_CLASS_MAP[media_file.mime_type.split("/", 1)[0]]
207 
208  return BrowseMediaSource(
209  domain=DOMAIN,
210  identifier=f"{path}/{media_file.name}{ext}",
211  media_class=media_class,
212  media_content_type=media_file.mime_type,
213  title=media_file.name,
214  can_play=media_file.is_file,
215  can_expand=media_file.is_directory,
216  )
BrowseMediaSource async_browse_media(self, MediaSourceItem item)
Definition: media_source.py:61
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
BrowseMediaSource _build_media_item(str path, MediaFile media_file)
MediaSource async_get_media_source(HomeAssistant hass)
Definition: media_source.py:26
BrowseMediaSource _build_root_paths(ConfigEntry entry, list[MediaDirectory] media_directories)
BrowseMediaSource _build_media_items(ConfigEntry entry, MediaFiles media_files, str path, str identifier)