mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 08:17:08 +00:00
Add image proxy to Kodi media browser (#47315)
This commit is contained in:
parent
09c51da3a2
commit
467bd91c80
@ -1,5 +1,5 @@
|
||||
"""Support for media browsing."""
|
||||
from contextlib import suppress
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from homeassistant.components.media_player import BrowseError, BrowseMedia
|
||||
@ -59,121 +59,20 @@ class UnknownMediaType(BrowseError):
|
||||
"""Unknown media type."""
|
||||
|
||||
|
||||
async def build_item_response(media_library, payload):
|
||||
async def build_item_response(media_library, payload, get_thumbnail_url=None):
|
||||
"""Create response payload for the provided media query."""
|
||||
search_id = payload["search_id"]
|
||||
search_type = payload["search_type"]
|
||||
|
||||
thumbnail = None
|
||||
title = None
|
||||
media = None
|
||||
|
||||
properties = ["thumbnail"]
|
||||
if search_type == MEDIA_TYPE_ALBUM:
|
||||
if search_id:
|
||||
album = await media_library.get_album_details(
|
||||
album_id=int(search_id), properties=properties
|
||||
)
|
||||
thumbnail = media_library.thumbnail_url(
|
||||
album["albumdetails"].get("thumbnail")
|
||||
)
|
||||
title = album["albumdetails"]["label"]
|
||||
media = await media_library.get_songs(
|
||||
album_id=int(search_id),
|
||||
properties=[
|
||||
"albumid",
|
||||
"artist",
|
||||
"duration",
|
||||
"album",
|
||||
"thumbnail",
|
||||
"track",
|
||||
],
|
||||
)
|
||||
media = media.get("songs")
|
||||
else:
|
||||
media = await media_library.get_albums(properties=properties)
|
||||
media = media.get("albums")
|
||||
title = "Albums"
|
||||
|
||||
elif search_type == MEDIA_TYPE_ARTIST:
|
||||
if search_id:
|
||||
media = await media_library.get_albums(
|
||||
artist_id=int(search_id), properties=properties
|
||||
)
|
||||
media = media.get("albums")
|
||||
artist = await media_library.get_artist_details(
|
||||
artist_id=int(search_id), properties=properties
|
||||
)
|
||||
thumbnail = media_library.thumbnail_url(
|
||||
artist["artistdetails"].get("thumbnail")
|
||||
)
|
||||
title = artist["artistdetails"]["label"]
|
||||
else:
|
||||
media = await media_library.get_artists(properties)
|
||||
media = media.get("artists")
|
||||
title = "Artists"
|
||||
|
||||
elif search_type == "library_music":
|
||||
library = {MEDIA_TYPE_ALBUM: "Albums", MEDIA_TYPE_ARTIST: "Artists"}
|
||||
media = [{"label": name, "type": type_} for type_, name in library.items()]
|
||||
title = "Music Library"
|
||||
|
||||
elif search_type == MEDIA_TYPE_MOVIE:
|
||||
media = await media_library.get_movies(properties)
|
||||
media = media.get("movies")
|
||||
title = "Movies"
|
||||
|
||||
elif search_type == MEDIA_TYPE_TVSHOW:
|
||||
if search_id:
|
||||
media = await media_library.get_seasons(
|
||||
tv_show_id=int(search_id),
|
||||
properties=["thumbnail", "season", "tvshowid"],
|
||||
)
|
||||
media = media.get("seasons")
|
||||
tvshow = await media_library.get_tv_show_details(
|
||||
tv_show_id=int(search_id), properties=properties
|
||||
)
|
||||
thumbnail = media_library.thumbnail_url(
|
||||
tvshow["tvshowdetails"].get("thumbnail")
|
||||
)
|
||||
title = tvshow["tvshowdetails"]["label"]
|
||||
else:
|
||||
media = await media_library.get_tv_shows(properties)
|
||||
media = media.get("tvshows")
|
||||
title = "TV Shows"
|
||||
|
||||
elif search_type == MEDIA_TYPE_SEASON:
|
||||
tv_show_id, season_id = search_id.split("/", 1)
|
||||
media = await media_library.get_episodes(
|
||||
tv_show_id=int(tv_show_id),
|
||||
season_id=int(season_id),
|
||||
properties=["thumbnail", "tvshowid", "seasonid"],
|
||||
)
|
||||
media = media.get("episodes")
|
||||
if media:
|
||||
season = await media_library.get_season_details(
|
||||
season_id=int(media[0]["seasonid"]), properties=properties
|
||||
)
|
||||
thumbnail = media_library.thumbnail_url(
|
||||
season["seasondetails"].get("thumbnail")
|
||||
)
|
||||
title = season["seasondetails"]["label"]
|
||||
|
||||
elif search_type == MEDIA_TYPE_CHANNEL:
|
||||
media = await media_library.get_channels(
|
||||
channel_group_id="alltv",
|
||||
properties=["thumbnail", "channeltype", "channel", "broadcastnow"],
|
||||
)
|
||||
media = media.get("channels")
|
||||
title = "Channels"
|
||||
_, title, media = await get_media_info(media_library, search_id, search_type)
|
||||
thumbnail = await get_thumbnail_url(search_type, search_id)
|
||||
|
||||
if media is None:
|
||||
return None
|
||||
|
||||
children = []
|
||||
for item in media:
|
||||
with suppress(UnknownMediaType):
|
||||
children.append(item_payload(item, media_library))
|
||||
children = await asyncio.gather(
|
||||
*[item_payload(item, get_thumbnail_url) for item in media]
|
||||
)
|
||||
|
||||
if search_type in (MEDIA_TYPE_TVSHOW, MEDIA_TYPE_MOVIE) and search_id == "":
|
||||
children.sort(key=lambda x: x.title.replace("The ", "", 1), reverse=False)
|
||||
@ -199,16 +98,13 @@ async def build_item_response(media_library, payload):
|
||||
return response
|
||||
|
||||
|
||||
def item_payload(item, media_library):
|
||||
async def item_payload(item, get_thumbnail_url=None):
|
||||
"""
|
||||
Create response payload for a single media item.
|
||||
|
||||
Used by async_browse_media.
|
||||
"""
|
||||
title = item["label"]
|
||||
thumbnail = item.get("thumbnail")
|
||||
if thumbnail:
|
||||
thumbnail = media_library.thumbnail_url(thumbnail)
|
||||
|
||||
media_class = None
|
||||
|
||||
@ -272,6 +168,12 @@ def item_payload(item, media_library):
|
||||
_LOGGER.debug("Unknown media type received: %s", media_content_type)
|
||||
raise UnknownMediaType from err
|
||||
|
||||
thumbnail = item.get("thumbnail")
|
||||
if thumbnail is not None and get_thumbnail_url is not None:
|
||||
thumbnail = await get_thumbnail_url(
|
||||
media_content_type, media_content_id, thumbnail_url=thumbnail
|
||||
)
|
||||
|
||||
return BrowseMedia(
|
||||
title=title,
|
||||
media_class=media_class,
|
||||
@ -283,7 +185,7 @@ def item_payload(item, media_library):
|
||||
)
|
||||
|
||||
|
||||
def library_payload(media_library):
|
||||
async def library_payload():
|
||||
"""
|
||||
Create response payload to describe contents of a specific library.
|
||||
|
||||
@ -305,12 +207,137 @@ def library_payload(media_library):
|
||||
MEDIA_TYPE_TVSHOW: "TV shows",
|
||||
MEDIA_TYPE_CHANNEL: "Channels",
|
||||
}
|
||||
for item in [{"label": name, "type": type_} for type_, name in library.items()]:
|
||||
library_info.children.append(
|
||||
|
||||
library_info.children = await asyncio.gather(
|
||||
*[
|
||||
item_payload(
|
||||
{"label": item["label"], "type": item["type"], "uri": item["type"]},
|
||||
media_library,
|
||||
{
|
||||
"label": item["label"],
|
||||
"type": item["type"],
|
||||
"uri": item["type"],
|
||||
},
|
||||
)
|
||||
for item in [
|
||||
{"label": name, "type": type_} for type_, name in library.items()
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
return library_info
|
||||
|
||||
|
||||
async def get_media_info(media_library, search_id, search_type):
|
||||
"""Fetch media/album."""
|
||||
thumbnail = None
|
||||
title = None
|
||||
media = None
|
||||
|
||||
properties = ["thumbnail"]
|
||||
if search_type == MEDIA_TYPE_ALBUM:
|
||||
if search_id:
|
||||
album = await media_library.get_album_details(
|
||||
album_id=int(search_id), properties=properties
|
||||
)
|
||||
thumbnail = media_library.thumbnail_url(
|
||||
album["albumdetails"].get("thumbnail")
|
||||
)
|
||||
title = album["albumdetails"]["label"]
|
||||
media = await media_library.get_songs(
|
||||
album_id=int(search_id),
|
||||
properties=[
|
||||
"albumid",
|
||||
"artist",
|
||||
"duration",
|
||||
"album",
|
||||
"thumbnail",
|
||||
"track",
|
||||
],
|
||||
)
|
||||
media = media.get("songs")
|
||||
else:
|
||||
media = await media_library.get_albums(properties=properties)
|
||||
media = media.get("albums")
|
||||
title = "Albums"
|
||||
|
||||
elif search_type == MEDIA_TYPE_ARTIST:
|
||||
if search_id:
|
||||
media = await media_library.get_albums(
|
||||
artist_id=int(search_id), properties=properties
|
||||
)
|
||||
media = media.get("albums")
|
||||
artist = await media_library.get_artist_details(
|
||||
artist_id=int(search_id), properties=properties
|
||||
)
|
||||
thumbnail = media_library.thumbnail_url(
|
||||
artist["artistdetails"].get("thumbnail")
|
||||
)
|
||||
title = artist["artistdetails"]["label"]
|
||||
else:
|
||||
media = await media_library.get_artists(properties)
|
||||
media = media.get("artists")
|
||||
title = "Artists"
|
||||
|
||||
elif search_type == "library_music":
|
||||
library = {MEDIA_TYPE_ALBUM: "Albums", MEDIA_TYPE_ARTIST: "Artists"}
|
||||
media = [{"label": name, "type": type_} for type_, name in library.items()]
|
||||
title = "Music Library"
|
||||
|
||||
elif search_type == MEDIA_TYPE_MOVIE:
|
||||
if search_id:
|
||||
movie = await media_library.get_movie_details(
|
||||
movie_id=int(search_id), properties=properties
|
||||
)
|
||||
thumbnail = media_library.thumbnail_url(
|
||||
movie["moviedetails"].get("thumbnail")
|
||||
)
|
||||
title = movie["moviedetails"]["label"]
|
||||
else:
|
||||
media = await media_library.get_movies(properties)
|
||||
media = media.get("movies")
|
||||
title = "Movies"
|
||||
|
||||
elif search_type == MEDIA_TYPE_TVSHOW:
|
||||
if search_id:
|
||||
media = await media_library.get_seasons(
|
||||
tv_show_id=int(search_id),
|
||||
properties=["thumbnail", "season", "tvshowid"],
|
||||
)
|
||||
media = media.get("seasons")
|
||||
tvshow = await media_library.get_tv_show_details(
|
||||
tv_show_id=int(search_id), properties=properties
|
||||
)
|
||||
thumbnail = media_library.thumbnail_url(
|
||||
tvshow["tvshowdetails"].get("thumbnail")
|
||||
)
|
||||
title = tvshow["tvshowdetails"]["label"]
|
||||
else:
|
||||
media = await media_library.get_tv_shows(properties)
|
||||
media = media.get("tvshows")
|
||||
title = "TV Shows"
|
||||
|
||||
elif search_type == MEDIA_TYPE_SEASON:
|
||||
tv_show_id, season_id = search_id.split("/", 1)
|
||||
media = await media_library.get_episodes(
|
||||
tv_show_id=int(tv_show_id),
|
||||
season_id=int(season_id),
|
||||
properties=["thumbnail", "tvshowid", "seasonid"],
|
||||
)
|
||||
media = media.get("episodes")
|
||||
if media:
|
||||
season = await media_library.get_season_details(
|
||||
season_id=int(media[0]["seasonid"]), properties=properties
|
||||
)
|
||||
thumbnail = media_library.thumbnail_url(
|
||||
season["seasondetails"].get("thumbnail")
|
||||
)
|
||||
title = season["seasondetails"]["label"]
|
||||
|
||||
elif search_type == MEDIA_TYPE_CHANNEL:
|
||||
media = await media_library.get_channels(
|
||||
channel_group_id="alltv",
|
||||
properties=["thumbnail", "channeltype", "channel", "broadcastnow"],
|
||||
)
|
||||
media = media.get("channels")
|
||||
title = "Channels"
|
||||
|
||||
return thumbnail, title, media
|
||||
|
@ -3,7 +3,7 @@
|
||||
"name": "Kodi",
|
||||
"documentation": "https://www.home-assistant.io/integrations/kodi",
|
||||
"requirements": [
|
||||
"pykodi==0.2.1"
|
||||
"pykodi==0.2.3"
|
||||
],
|
||||
"codeowners": [
|
||||
"@OnFreund",
|
||||
|
@ -3,8 +3,10 @@ from datetime import timedelta
|
||||
from functools import wraps
|
||||
import logging
|
||||
import re
|
||||
import urllib.parse
|
||||
|
||||
import jsonrpc_base
|
||||
from jsonrpc_base.jsonrpc import ProtocolError, TransportError
|
||||
from pykodi import CannotConnectError
|
||||
import voluptuous as vol
|
||||
|
||||
@ -61,9 +63,10 @@ from homeassistant.helpers import (
|
||||
entity_platform,
|
||||
)
|
||||
from homeassistant.helpers.event import async_track_time_interval
|
||||
from homeassistant.helpers.network import is_internal_request
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from .browse_media import build_item_response, library_payload
|
||||
from .browse_media import build_item_response, get_media_info, library_payload
|
||||
from .const import (
|
||||
CONF_WS_PORT,
|
||||
DATA_CONNECTION,
|
||||
@ -871,16 +874,50 @@ class KodiEntity(MediaPlayerEntity):
|
||||
|
||||
async def async_browse_media(self, media_content_type=None, media_content_id=None):
|
||||
"""Implement the websocket media browsing helper."""
|
||||
is_internal = is_internal_request(self.hass)
|
||||
|
||||
async def _get_thumbnail_url(
|
||||
media_content_type,
|
||||
media_content_id,
|
||||
media_image_id=None,
|
||||
thumbnail_url=None,
|
||||
):
|
||||
if is_internal:
|
||||
return self._kodi.thumbnail_url(thumbnail_url)
|
||||
|
||||
return self.get_browse_image_url(
|
||||
media_content_type,
|
||||
urllib.parse.quote_plus(media_content_id),
|
||||
media_image_id,
|
||||
)
|
||||
|
||||
if media_content_type in [None, "library"]:
|
||||
return await self.hass.async_add_executor_job(library_payload, self._kodi)
|
||||
return await library_payload()
|
||||
|
||||
payload = {
|
||||
"search_type": media_content_type,
|
||||
"search_id": media_content_id,
|
||||
}
|
||||
response = await build_item_response(self._kodi, payload)
|
||||
|
||||
response = await build_item_response(self._kodi, payload, _get_thumbnail_url)
|
||||
if response is None:
|
||||
raise BrowseError(
|
||||
f"Media not found: {media_content_type} / {media_content_id}"
|
||||
)
|
||||
return response
|
||||
|
||||
async def async_get_browse_image(
|
||||
self, media_content_type, media_content_id, media_image_id=None
|
||||
):
|
||||
"""Get media image from kodi server."""
|
||||
try:
|
||||
image_url, _, _ = await get_media_info(
|
||||
self._kodi, media_content_id, media_content_type
|
||||
)
|
||||
except (ProtocolError, TransportError):
|
||||
return (None, None)
|
||||
|
||||
if image_url:
|
||||
return await self._async_fetch_image(image_url)
|
||||
|
||||
return (None, None)
|
||||
|
@ -1476,7 +1476,7 @@ pykira==0.1.1
|
||||
pykmtronic==0.0.3
|
||||
|
||||
# homeassistant.components.kodi
|
||||
pykodi==0.2.1
|
||||
pykodi==0.2.3
|
||||
|
||||
# homeassistant.components.kulersky
|
||||
pykulersky==0.5.2
|
||||
|
@ -784,7 +784,7 @@ pykira==0.1.1
|
||||
pykmtronic==0.0.3
|
||||
|
||||
# homeassistant.components.kodi
|
||||
pykodi==0.2.1
|
||||
pykodi==0.2.3
|
||||
|
||||
# homeassistant.components.kulersky
|
||||
pykulersky==0.5.2
|
||||
|
Loading…
x
Reference in New Issue
Block a user