1 """Browse media features for media player."""
3 from __future__
import annotations
5 from collections.abc
import Sequence
6 from datetime
import timedelta
9 from urllib.parse
import quote
18 get_supervisor_network_url,
23 from .const
import CONTENT_AUTH_EXPIRY_TIME, MediaClass, MediaType
26 PATHS_WITHOUT_AUTH = (
"/api/tts_proxy/",
"/api/esphome/ffmpeg_proxy/")
32 media_content_id: str,
34 allow_relative_url: bool =
False,
35 for_supervisor_network: bool =
False,
37 """Update a media URL with authentication if it points at Home Assistant."""
38 parsed = yarl.URL(media_content_id)
40 if parsed.scheme
and parsed.scheme
not in (
"http",
"https"):
41 return media_content_id
43 if parsed.is_absolute():
45 return media_content_id
46 elif media_content_id[0] !=
"/":
47 return media_content_id
52 logging.getLogger(__name__).debug(
53 "Not signing path for content with query param"
55 elif parsed.path.startswith(PATHS_WITHOUT_AUTH):
64 timedelta(seconds=CONTENT_AUTH_EXPIRY_TIME),
66 media_content_id =
str(parsed.join(yarl.URL(signed_path)))
69 if not parsed.is_absolute()
and not allow_relative_url:
71 if for_supervisor_network:
77 except NoURLAvailableError
as err:
78 msg =
"Unable to determine Home Assistant URL to send to device"
81 and hass.config.api.use_ssl
82 and (
not hass.config.external_url
or not hass.config.internal_url)
84 msg +=
". Configure internal and external URL in general settings."
87 media_content_id = f
"{base_url}{media_content_id}"
89 return media_content_id
93 """Represent a browsable media file."""
98 media_class: MediaClass | str,
99 media_content_id: str,
100 media_content_type: MediaType | str,
104 children: Sequence[BrowseMedia] |
None =
None,
105 children_media_class: MediaClass | str |
None =
None,
106 thumbnail: str |
None =
None,
109 """Initialize browse media item."""
121 def as_dict(self, *, parent: bool =
True) -> dict[str, Any]:
122 """Convert Media class to browse media dictionary."""
126 response: dict[str, Any] = {
127 "title": self.
titletitle,
140 response[
"not_shown"] = self.
not_shownnot_shown
143 response[
"children"] = [
144 child.as_dict(parent=
False)
for child
in self.
childrenchildren
147 response[
"children"] = []
152 """Count the children media classes and calculate the correct class."""
154 assert self.
childrenchildren
is not None
155 proposed_class = self.
childrenchildren[0].media_class
156 if all(child.media_class == proposed_class
for child
in self.
childrenchildren):
160 """Return representation of browse media."""
161 return f
"<BrowseMedia {self.title} ({self.media_class})>"
str async_sign_path(HomeAssistant hass, str path, timedelta expiration, *str|None refresh_token_id=None, bool use_content_user=False)
str|None get_supervisor_network_url(HomeAssistant hass, *bool allow_ssl=False)
str get_url(HomeAssistant hass, *bool require_current_request=False, bool require_ssl=False, bool require_standard_port=False, bool require_cloud=False, bool allow_internal=True, bool allow_external=True, bool allow_cloud=True, bool|None allow_ip=None, bool|None prefer_external=None, bool prefer_cloud=False)
bool is_hass_url(HomeAssistant hass, str url)