Home Assistant Unofficial Reference 2024.12.1
media_source.py
Go to the documentation of this file.
1 """Implementation of DLNA DMS as a media source.
2 
3 URIs look like "media-source://dlna_dms/<source_id>/<media_identifier>"
4 
5 Media identifiers can look like:
6 * `/path/to/file`: slash-separated path through the Content Directory
7 * `:ObjectID`: colon followed by a server-assigned ID for an object
8 * `?query`: question mark followed by a query string to search for,
9  see [DLNA ContentDirectory SearchCriteria](http://www.upnp.org/specs/av/UPnP-av-ContentDirectory-v1-Service.pdf)
10  for the syntax.
11 """
12 
13 from __future__ import annotations
14 
15 from homeassistant.components.media_player import BrowseError, MediaClass, MediaType
17  BrowseMediaSource,
18  MediaSource,
19  MediaSourceItem,
20  Unresolvable,
21 )
22 from homeassistant.core import HomeAssistant
23 
24 from .const import DOMAIN, LOGGER, PATH_OBJECT_ID_FLAG, ROOT_OBJECT_ID, SOURCE_SEP
25 from .dms import DidlPlayMedia, get_domain_data
26 
27 
28 async def async_get_media_source(hass: HomeAssistant) -> DmsMediaSource:
29  """Set up DLNA DMS media source."""
30  LOGGER.debug("Setting up DLNA media sources")
31  return DmsMediaSource(hass)
32 
33 
34 class DmsMediaSource(MediaSource):
35  """Provide DLNA Media Servers as media sources."""
36 
37  name = "DLNA Servers"
38 
39  def __init__(self, hass: HomeAssistant) -> None:
40  """Initialize DLNA source."""
41  super().__init__(DOMAIN)
42 
43  self.hasshass = hass
44 
45  async def async_resolve_media(self, item: MediaSourceItem) -> DidlPlayMedia:
46  """Resolve a media item to a playable item."""
47  dms_data = get_domain_data(self.hasshass)
48  if not dms_data.sources:
49  raise Unresolvable("No sources have been configured")
50 
51  source_id, media_id = _parse_identifier(item)
52  if not source_id:
53  raise Unresolvable(f"No source ID in {item.identifier}")
54  if not media_id:
55  raise Unresolvable(f"No media ID in {item.identifier}")
56 
57  try:
58  source = dms_data.sources[source_id]
59  except KeyError as err:
60  raise Unresolvable(f"Unknown source ID: {source_id}") from err
61 
62  return await source.async_resolve_media(media_id)
63 
64  async def async_browse_media(self, item: MediaSourceItem) -> BrowseMediaSource:
65  """Browse media."""
66  dms_data = get_domain_data(self.hasshass)
67  if not dms_data.sources:
68  raise BrowseError("No sources have been configured")
69 
70  source_id, media_id = _parse_identifier(item)
71  LOGGER.debug("Browsing for %s / %s", source_id, media_id)
72 
73  if not source_id and len(dms_data.sources) > 1:
74  # Browsing the root of dlna_dms with more than one server, return
75  # all known servers.
76  base = BrowseMediaSource(
77  domain=DOMAIN,
78  identifier="",
79  media_class=MediaClass.DIRECTORY,
80  media_content_type=MediaType.CHANNELS,
81  title=self.namename,
82  can_play=False,
83  can_expand=True,
84  children_media_class=MediaClass.CHANNEL,
85  )
86 
87  base.children = [
88  BrowseMediaSource(
89  domain=DOMAIN,
90  identifier=f"{source_id}/{PATH_OBJECT_ID_FLAG}{ROOT_OBJECT_ID}",
91  media_class=MediaClass.CHANNEL,
92  media_content_type=MediaType.CHANNEL,
93  title=source.name,
94  can_play=False,
95  can_expand=True,
96  thumbnail=source.icon,
97  )
98  for source_id, source in dms_data.sources.items()
99  ]
100 
101  return base
102 
103  if not source_id:
104  # No source specified, default to the first registered
105  source_id = next(iter(dms_data.sources))
106 
107  try:
108  source = dms_data.sources[source_id]
109  except KeyError as err:
110  raise BrowseError(f"Unknown source ID: {source_id}") from err
111 
112  return await source.async_browse_media(media_id)
113 
114 
115 def _parse_identifier(item: MediaSourceItem) -> tuple[str | None, str | None]:
116  """Parse the source_id and media identifier from a media source item."""
117  if not item.identifier:
118  return None, None
119  source_id, _, media_id = item.identifier.partition(SOURCE_SEP)
120  return source_id or None, media_id or None
DidlPlayMedia async_resolve_media(self, MediaSourceItem item)
Definition: media_source.py:45
BrowseMediaSource async_browse_media(self, MediaSourceItem item)
Definition: media_source.py:64
DlnaDmrData get_domain_data(HomeAssistant hass)
Definition: data.py:120
DmsMediaSource async_get_media_source(HomeAssistant hass)
Definition: media_source.py:28
tuple[str|None, str|None] _parse_identifier(MediaSourceItem item)