Improve Roku media browser structure (#39754)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>pull/39870/head
parent
9c40b511f2
commit
a918981ff3
|
@ -0,0 +1,142 @@
|
|||
"""Support for media browsing."""
|
||||
|
||||
from homeassistant.components.media_player import BrowseMedia
|
||||
from homeassistant.components.media_player.const import (
|
||||
MEDIA_CLASS_APP,
|
||||
MEDIA_CLASS_CHANNEL,
|
||||
MEDIA_CLASS_DIRECTORY,
|
||||
MEDIA_TYPE_APP,
|
||||
MEDIA_TYPE_APPS,
|
||||
MEDIA_TYPE_CHANNEL,
|
||||
MEDIA_TYPE_CHANNELS,
|
||||
)
|
||||
|
||||
CONTENT_TYPE_MEDIA_CLASS = {
|
||||
MEDIA_TYPE_APP: MEDIA_CLASS_APP,
|
||||
MEDIA_TYPE_APPS: MEDIA_CLASS_APP,
|
||||
MEDIA_TYPE_CHANNEL: MEDIA_CLASS_CHANNEL,
|
||||
MEDIA_TYPE_CHANNELS: MEDIA_CLASS_CHANNEL,
|
||||
}
|
||||
|
||||
PLAYABLE_MEDIA_TYPES = [
|
||||
MEDIA_TYPE_APP,
|
||||
MEDIA_TYPE_CHANNEL,
|
||||
]
|
||||
|
||||
EXPANDABLE_MEDIA_TYPES = [
|
||||
MEDIA_TYPE_APPS,
|
||||
MEDIA_TYPE_CHANNELS,
|
||||
]
|
||||
|
||||
|
||||
def build_item_response(coordinator, payload):
|
||||
"""Create response payload for the provided media query."""
|
||||
search_id = payload["search_id"]
|
||||
search_type = payload["search_type"]
|
||||
|
||||
thumbnail = None
|
||||
title = None
|
||||
media = None
|
||||
|
||||
if search_type == MEDIA_TYPE_APPS:
|
||||
title = "Apps"
|
||||
media = [
|
||||
{"app_id": item.app_id, "title": item.name, "type": MEDIA_TYPE_APP}
|
||||
for item in coordinator.data.apps
|
||||
]
|
||||
elif search_type == MEDIA_TYPE_CHANNELS:
|
||||
title = "Channels"
|
||||
media = [
|
||||
{
|
||||
"channel_number": item.number,
|
||||
"title": item.name,
|
||||
"type": MEDIA_TYPE_CHANNEL,
|
||||
}
|
||||
for item in coordinator.data.channels
|
||||
]
|
||||
|
||||
if media is None:
|
||||
return None
|
||||
|
||||
return BrowseMedia(
|
||||
media_class=CONTENT_TYPE_MEDIA_CLASS[search_type],
|
||||
media_content_id=search_id,
|
||||
media_content_type=search_type,
|
||||
title=title,
|
||||
can_play=search_type in PLAYABLE_MEDIA_TYPES and search_id,
|
||||
can_expand=True,
|
||||
children=[item_payload(item, coordinator) for item in media],
|
||||
thumbnail=thumbnail,
|
||||
)
|
||||
|
||||
|
||||
def item_payload(item, coordinator):
|
||||
"""
|
||||
Create response payload for a single media item.
|
||||
|
||||
Used by async_browse_media.
|
||||
"""
|
||||
thumbnail = None
|
||||
|
||||
if "app_id" in item:
|
||||
media_content_type = MEDIA_TYPE_APP
|
||||
media_content_id = item["app_id"]
|
||||
thumbnail = coordinator.roku.app_icon_url(item["app_id"])
|
||||
elif "channel_number" in item:
|
||||
media_content_type = MEDIA_TYPE_CHANNEL
|
||||
media_content_id = item["channel_number"]
|
||||
else:
|
||||
media_content_type = item["type"]
|
||||
media_content_id = ""
|
||||
|
||||
title = item["title"]
|
||||
can_play = media_content_type in PLAYABLE_MEDIA_TYPES and media_content_id
|
||||
can_expand = media_content_type in EXPANDABLE_MEDIA_TYPES
|
||||
|
||||
return BrowseMedia(
|
||||
title=title,
|
||||
media_class=CONTENT_TYPE_MEDIA_CLASS[media_content_type],
|
||||
media_content_type=media_content_type,
|
||||
media_content_id=media_content_id,
|
||||
can_play=can_play,
|
||||
can_expand=can_expand,
|
||||
thumbnail=thumbnail,
|
||||
)
|
||||
|
||||
|
||||
def library_payload(coordinator):
|
||||
"""
|
||||
Create response payload to describe contents of a specific library.
|
||||
|
||||
Used by async_browse_media.
|
||||
"""
|
||||
library_info = BrowseMedia(
|
||||
media_class=MEDIA_CLASS_DIRECTORY,
|
||||
media_content_id="library",
|
||||
media_content_type="library",
|
||||
title="Media Library",
|
||||
can_play=False,
|
||||
can_expand=True,
|
||||
children=[],
|
||||
)
|
||||
|
||||
library = {
|
||||
MEDIA_TYPE_APPS: "Apps",
|
||||
MEDIA_TYPE_CHANNELS: "Channels",
|
||||
}
|
||||
|
||||
for item in [{"title": name, "type": type_} for type_, name in library.items()]:
|
||||
if (
|
||||
item["type"] == MEDIA_TYPE_CHANNELS
|
||||
and coordinator.data.info.device_type != "tv"
|
||||
):
|
||||
continue
|
||||
|
||||
library_info.children.append(
|
||||
item_payload(
|
||||
{"title": item["title"], "type": item["type"]},
|
||||
coordinator,
|
||||
)
|
||||
)
|
||||
|
||||
return library_info
|
|
@ -7,17 +7,11 @@ import voluptuous as vol
|
|||
from homeassistant.components.media_player import (
|
||||
DEVICE_CLASS_RECEIVER,
|
||||
DEVICE_CLASS_TV,
|
||||
BrowseMedia,
|
||||
MediaPlayerEntity,
|
||||
)
|
||||
from homeassistant.components.media_player.const import (
|
||||
MEDIA_CLASS_APP,
|
||||
MEDIA_CLASS_CHANNEL,
|
||||
MEDIA_CLASS_DIRECTORY,
|
||||
MEDIA_TYPE_APP,
|
||||
MEDIA_TYPE_APPS,
|
||||
MEDIA_TYPE_CHANNEL,
|
||||
MEDIA_TYPE_CHANNELS,
|
||||
SUPPORT_BROWSE_MEDIA,
|
||||
SUPPORT_NEXT_TRACK,
|
||||
SUPPORT_PAUSE,
|
||||
|
@ -42,6 +36,7 @@ from homeassistant.const import (
|
|||
from homeassistant.helpers import entity_platform
|
||||
|
||||
from . import RokuDataUpdateCoordinator, RokuEntity, roku_exception_handler
|
||||
from .browse_media import build_item_response, library_payload
|
||||
from .const import ATTR_KEYWORD, DOMAIN, SERVICE_SEARCH
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -78,44 +73,6 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
|||
)
|
||||
|
||||
|
||||
def browse_media_library(channels: bool = False) -> BrowseMedia:
|
||||
"""Create response payload to describe contents of a specific library."""
|
||||
library_info = BrowseMedia(
|
||||
title="Media Library",
|
||||
media_class=MEDIA_CLASS_DIRECTORY,
|
||||
media_content_id="library",
|
||||
media_content_type="library",
|
||||
can_play=False,
|
||||
can_expand=True,
|
||||
children=[],
|
||||
)
|
||||
|
||||
library_info.children.append(
|
||||
BrowseMedia(
|
||||
title="Apps",
|
||||
media_class=MEDIA_CLASS_APP,
|
||||
media_content_id="apps",
|
||||
media_content_type=MEDIA_TYPE_APPS,
|
||||
can_expand=True,
|
||||
can_play=False,
|
||||
)
|
||||
)
|
||||
|
||||
if channels:
|
||||
library_info.children.append(
|
||||
BrowseMedia(
|
||||
title="Channels",
|
||||
media_class=MEDIA_CLASS_CHANNEL,
|
||||
media_content_id="channels",
|
||||
media_content_type=MEDIA_TYPE_CHANNELS,
|
||||
can_expand=True,
|
||||
can_play=False,
|
||||
)
|
||||
)
|
||||
|
||||
return library_info
|
||||
|
||||
|
||||
class RokuMediaPlayer(RokuEntity, MediaPlayerEntity):
|
||||
"""Representation of a Roku media player on the network."""
|
||||
|
||||
|
@ -284,53 +241,13 @@ class RokuMediaPlayer(RokuEntity, MediaPlayerEntity):
|
|||
async def async_browse_media(self, media_content_type=None, media_content_id=None):
|
||||
"""Implement the websocket media browsing helper."""
|
||||
if media_content_type in [None, "library"]:
|
||||
is_tv = self.coordinator.data.info.device_type == "tv"
|
||||
return browse_media_library(channels=is_tv)
|
||||
return library_payload(self.coordinator)
|
||||
|
||||
response = None
|
||||
|
||||
if media_content_type == MEDIA_TYPE_APPS:
|
||||
response = BrowseMedia(
|
||||
title="Apps",
|
||||
media_class=MEDIA_CLASS_APP,
|
||||
media_content_id="apps",
|
||||
media_content_type=MEDIA_TYPE_APPS,
|
||||
can_expand=True,
|
||||
can_play=False,
|
||||
children=[
|
||||
BrowseMedia(
|
||||
title=app.name,
|
||||
thumbnail=self.coordinator.roku.app_icon_url(app.app_id),
|
||||
media_class=MEDIA_CLASS_APP,
|
||||
media_content_id=app.app_id,
|
||||
media_content_type=MEDIA_TYPE_APP,
|
||||
can_play=True,
|
||||
can_expand=False,
|
||||
)
|
||||
for app in self.coordinator.data.apps
|
||||
],
|
||||
)
|
||||
|
||||
if media_content_type == MEDIA_TYPE_CHANNELS:
|
||||
response = BrowseMedia(
|
||||
title="Channels",
|
||||
media_class=MEDIA_CLASS_CHANNEL,
|
||||
media_content_id="channels",
|
||||
media_content_type=MEDIA_TYPE_CHANNELS,
|
||||
can_expand=True,
|
||||
can_play=False,
|
||||
children=[
|
||||
BrowseMedia(
|
||||
title=channel.name,
|
||||
media_class=MEDIA_CLASS_CHANNEL,
|
||||
media_content_id=channel.number,
|
||||
media_content_type=MEDIA_TYPE_CHANNEL,
|
||||
can_play=True,
|
||||
can_expand=False,
|
||||
)
|
||||
for channel in self.coordinator.data.channels
|
||||
],
|
||||
)
|
||||
payload = {
|
||||
"search_type": media_content_type,
|
||||
"search_id": media_content_id,
|
||||
}
|
||||
response = build_item_response(self.coordinator, payload)
|
||||
|
||||
if response is None:
|
||||
raise BrowseError(
|
||||
|
|
Loading…
Reference in New Issue