Proxy Plex media browser images (#43111)
Co-authored-by: rajlaud <50647620+rajlaud@users.noreply.github.com> Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
parent
e10762af9b
commit
7c0e148b59
3 changed files with 85 additions and 65 deletions
|
@ -52,14 +52,69 @@ ITEM_TYPE_MEDIA_CLASS = {
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def browse_media(
|
def browse_media(entity, is_internal, media_content_type=None, media_content_id=None):
|
||||||
entity_id, plex_server, media_content_type=None, media_content_id=None
|
|
||||||
):
|
|
||||||
"""Implement the websocket media browsing helper."""
|
"""Implement the websocket media browsing helper."""
|
||||||
|
|
||||||
|
def item_payload(item):
|
||||||
|
"""Create response payload for a single media item."""
|
||||||
|
try:
|
||||||
|
media_class = ITEM_TYPE_MEDIA_CLASS[item.type]
|
||||||
|
except KeyError as err:
|
||||||
|
_LOGGER.debug("Unknown type received: %s", item.type)
|
||||||
|
raise UnknownMediaType from err
|
||||||
|
payload = {
|
||||||
|
"title": item.title,
|
||||||
|
"media_class": media_class,
|
||||||
|
"media_content_id": str(item.ratingKey),
|
||||||
|
"media_content_type": item.type,
|
||||||
|
"can_play": True,
|
||||||
|
"can_expand": item.type in EXPANDABLES,
|
||||||
|
}
|
||||||
|
if hasattr(item, "thumbUrl"):
|
||||||
|
entity.plex_server.thumbnail_cache.setdefault(
|
||||||
|
str(item.ratingKey), item.thumbUrl
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_internal:
|
||||||
|
thumbnail = item.thumbUrl
|
||||||
|
else:
|
||||||
|
thumbnail = entity.get_browse_image_url(item.type, item.ratingKey)
|
||||||
|
|
||||||
|
payload["thumbnail"] = thumbnail
|
||||||
|
|
||||||
|
return BrowseMedia(**payload)
|
||||||
|
|
||||||
|
def library_payload(library_id):
|
||||||
|
"""Create response payload to describe contents of a specific library."""
|
||||||
|
library = entity.plex_server.library.sectionByID(library_id)
|
||||||
|
library_info = library_section_payload(library)
|
||||||
|
library_info.children = []
|
||||||
|
library_info.children.append(special_library_payload(library_info, "On Deck"))
|
||||||
|
library_info.children.append(
|
||||||
|
special_library_payload(library_info, "Recently Added")
|
||||||
|
)
|
||||||
|
for item in library.all():
|
||||||
|
try:
|
||||||
|
library_info.children.append(item_payload(item))
|
||||||
|
except UnknownMediaType:
|
||||||
|
continue
|
||||||
|
return library_info
|
||||||
|
|
||||||
|
def playlists_payload():
|
||||||
|
"""Create response payload for all available playlists."""
|
||||||
|
playlists_info = {**PLAYLISTS_BROWSE_PAYLOAD, "children": []}
|
||||||
|
for playlist in entity.plex_server.playlists():
|
||||||
|
try:
|
||||||
|
playlists_info["children"].append(item_payload(playlist))
|
||||||
|
except UnknownMediaType:
|
||||||
|
continue
|
||||||
|
response = BrowseMedia(**playlists_info)
|
||||||
|
response.children_media_class = MEDIA_CLASS_PLAYLIST
|
||||||
|
return response
|
||||||
|
|
||||||
def build_item_response(payload):
|
def build_item_response(payload):
|
||||||
"""Create response payload for the provided media query."""
|
"""Create response payload for the provided media query."""
|
||||||
media = plex_server.lookup_media(**payload)
|
media = entity.plex_server.lookup_media(**payload)
|
||||||
|
|
||||||
if media is None:
|
if media is None:
|
||||||
return None
|
return None
|
||||||
|
@ -85,19 +140,21 @@ def browse_media(
|
||||||
if (
|
if (
|
||||||
media_content_type
|
media_content_type
|
||||||
and media_content_type == "server"
|
and media_content_type == "server"
|
||||||
and media_content_id != plex_server.machine_identifier
|
and media_content_id != entity.plex_server.machine_identifier
|
||||||
):
|
):
|
||||||
raise BrowseError(
|
raise BrowseError(
|
||||||
f"Plex server with ID '{media_content_id}' is not associated with {entity_id}"
|
f"Plex server with ID '{media_content_id}' is not associated with {entity.entity_id}"
|
||||||
)
|
)
|
||||||
|
|
||||||
if special_folder:
|
if special_folder:
|
||||||
if media_content_type == "server":
|
if media_content_type == "server":
|
||||||
library_or_section = plex_server.library
|
library_or_section = entity.plex_server.library
|
||||||
children_media_class = MEDIA_CLASS_DIRECTORY
|
children_media_class = MEDIA_CLASS_DIRECTORY
|
||||||
title = plex_server.friendly_name
|
title = entity.plex_server.friendly_name
|
||||||
elif media_content_type == "library":
|
elif media_content_type == "library":
|
||||||
library_or_section = plex_server.library.sectionByID(media_content_id)
|
library_or_section = entity.plex_server.library.sectionByID(
|
||||||
|
media_content_id
|
||||||
|
)
|
||||||
title = library_or_section.title
|
title = library_or_section.title
|
||||||
try:
|
try:
|
||||||
children_media_class = ITEM_TYPE_MEDIA_CLASS[library_or_section.TYPE]
|
children_media_class = ITEM_TYPE_MEDIA_CLASS[library_or_section.TYPE]
|
||||||
|
@ -133,10 +190,10 @@ def browse_media(
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if media_content_type in ["server", None]:
|
if media_content_type in ["server", None]:
|
||||||
return server_payload(plex_server)
|
return server_payload(entity.plex_server)
|
||||||
|
|
||||||
if media_content_type == "library":
|
if media_content_type == "library":
|
||||||
return library_payload(plex_server, media_content_id)
|
return library_payload(media_content_id)
|
||||||
|
|
||||||
except UnknownMediaType as err:
|
except UnknownMediaType as err:
|
||||||
raise BrowseError(
|
raise BrowseError(
|
||||||
|
@ -144,7 +201,7 @@ def browse_media(
|
||||||
) from err
|
) from err
|
||||||
|
|
||||||
if media_content_type == "playlists":
|
if media_content_type == "playlists":
|
||||||
return playlists_payload(plex_server)
|
return playlists_payload()
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
"media_type": DOMAIN,
|
"media_type": DOMAIN,
|
||||||
|
@ -156,27 +213,6 @@ def browse_media(
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
def item_payload(item):
|
|
||||||
"""Create response payload for a single media item."""
|
|
||||||
try:
|
|
||||||
media_class = ITEM_TYPE_MEDIA_CLASS[item.type]
|
|
||||||
except KeyError as err:
|
|
||||||
_LOGGER.debug("Unknown type received: %s", item.type)
|
|
||||||
raise UnknownMediaType from err
|
|
||||||
payload = {
|
|
||||||
"title": item.title,
|
|
||||||
"media_class": media_class,
|
|
||||||
"media_content_id": str(item.ratingKey),
|
|
||||||
"media_content_type": item.type,
|
|
||||||
"can_play": True,
|
|
||||||
"can_expand": item.type in EXPANDABLES,
|
|
||||||
}
|
|
||||||
if hasattr(item, "thumbUrl"):
|
|
||||||
payload["thumbnail"] = item.thumbUrl
|
|
||||||
|
|
||||||
return BrowseMedia(**payload)
|
|
||||||
|
|
||||||
|
|
||||||
def library_section_payload(section):
|
def library_section_payload(section):
|
||||||
"""Create response payload for a single library section."""
|
"""Create response payload for a single library section."""
|
||||||
try:
|
try:
|
||||||
|
@ -229,33 +265,3 @@ def server_payload(plex_server):
|
||||||
server_info.children.append(library_section_payload(library))
|
server_info.children.append(library_section_payload(library))
|
||||||
server_info.children.append(BrowseMedia(**PLAYLISTS_BROWSE_PAYLOAD))
|
server_info.children.append(BrowseMedia(**PLAYLISTS_BROWSE_PAYLOAD))
|
||||||
return server_info
|
return server_info
|
||||||
|
|
||||||
|
|
||||||
def library_payload(plex_server, library_id):
|
|
||||||
"""Create response payload to describe contents of a specific library."""
|
|
||||||
library = plex_server.library.sectionByID(library_id)
|
|
||||||
library_info = library_section_payload(library)
|
|
||||||
library_info.children = []
|
|
||||||
library_info.children.append(special_library_payload(library_info, "On Deck"))
|
|
||||||
library_info.children.append(
|
|
||||||
special_library_payload(library_info, "Recently Added")
|
|
||||||
)
|
|
||||||
for item in library.all():
|
|
||||||
try:
|
|
||||||
library_info.children.append(item_payload(item))
|
|
||||||
except UnknownMediaType:
|
|
||||||
continue
|
|
||||||
return library_info
|
|
||||||
|
|
||||||
|
|
||||||
def playlists_payload(plex_server):
|
|
||||||
"""Create response payload for all available playlists."""
|
|
||||||
playlists_info = {**PLAYLISTS_BROWSE_PAYLOAD, "children": []}
|
|
||||||
for playlist in plex_server.playlists():
|
|
||||||
try:
|
|
||||||
playlists_info["children"].append(item_payload(playlist))
|
|
||||||
except UnknownMediaType:
|
|
||||||
continue
|
|
||||||
response = BrowseMedia(**playlists_info)
|
|
||||||
response.children_media_class = MEDIA_CLASS_PLAYLIST
|
|
||||||
return response
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ from homeassistant.const import STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYI
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity_registry import async_get_registry
|
from homeassistant.helpers.entity_registry import async_get_registry
|
||||||
|
from homeassistant.helpers.network import is_internal_request
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
|
@ -629,10 +630,22 @@ class PlexMediaPlayer(MediaPlayerEntity):
|
||||||
|
|
||||||
async def async_browse_media(self, media_content_type=None, media_content_id=None):
|
async def async_browse_media(self, media_content_type=None, media_content_id=None):
|
||||||
"""Implement the websocket media browsing helper."""
|
"""Implement the websocket media browsing helper."""
|
||||||
|
is_internal = is_internal_request(self.hass)
|
||||||
return await self.hass.async_add_executor_job(
|
return await self.hass.async_add_executor_job(
|
||||||
browse_media,
|
browse_media,
|
||||||
self.entity_id,
|
self,
|
||||||
self.plex_server,
|
is_internal,
|
||||||
media_content_type,
|
media_content_type,
|
||||||
media_content_id,
|
media_content_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def async_get_browse_image(
|
||||||
|
self, media_content_type, media_content_id, media_image_id=None
|
||||||
|
):
|
||||||
|
"""Get media image from Plex server."""
|
||||||
|
image_url = self.plex_server.thumbnail_cache.get(media_content_id)
|
||||||
|
if image_url:
|
||||||
|
result = await self._async_fetch_image(image_url)
|
||||||
|
return result
|
||||||
|
|
||||||
|
return (None, None)
|
||||||
|
|
|
@ -97,6 +97,7 @@ class PlexServer:
|
||||||
immediate=True,
|
immediate=True,
|
||||||
function=self._async_update_platforms,
|
function=self._async_update_platforms,
|
||||||
).async_call
|
).async_call
|
||||||
|
self.thumbnail_cache = {}
|
||||||
|
|
||||||
# Header conditionally added as it is not available in config entry v1
|
# Header conditionally added as it is not available in config entry v1
|
||||||
if CONF_CLIENT_ID in server_config:
|
if CONF_CLIENT_ID in server_config:
|
||||||
|
|
Loading…
Add table
Reference in a new issue