Improve Roku media browser structure (#39754)

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
pull/39870/head
Chris Talkington 2020-09-09 16:22:26 -05:00 committed by GitHub
parent 9c40b511f2
commit a918981ff3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 149 additions and 90 deletions

View File

@ -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

View File

@ -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(