Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
Erik
deb196af36 Enable spotify media browsing for google cast devices 2022-01-28 14:26:35 +01:00
Erik
46239cc97c Minor refactoring of cast media_player 2022-01-28 14:23:18 +01:00

View file

@ -4,7 +4,6 @@ from __future__ import annotations
import asyncio import asyncio
from contextlib import suppress from contextlib import suppress
from datetime import datetime, timedelta from datetime import datetime, timedelta
import functools as ft
import json import json
import logging import logging
from urllib.parse import quote from urllib.parse import quote
@ -21,7 +20,7 @@ from pychromecast.socket_client import (
) )
import voluptuous as vol import voluptuous as vol
from homeassistant.components import media_source, plex, zeroconf from homeassistant.components import media_source, plex, spotify, zeroconf
from homeassistant.components.http.auth import async_sign_path from homeassistant.components.http.auth import async_sign_path
from homeassistant.components.media_player import ( from homeassistant.components.media_player import (
BrowseError, BrowseError,
@ -461,33 +460,10 @@ class CastDevice(MediaPlayerEntity):
media_controller = self._media_controller() media_controller = self._media_controller()
media_controller.seek(position) media_controller.seek(position)
async def async_browse_media(self, media_content_type=None, media_content_id=None): async def _async_root_payload(self, content_filter):
"""Implement the websocket media browsing helper.""" """Generate root node."""
kwargs = {}
children = [] children = []
# Add external sources
if self._chromecast.cast_type == pychromecast.const.CAST_TYPE_AUDIO:
kwargs["content_filter"] = lambda item: item.media_content_type.startswith(
"audio/"
)
if media_content_id is not None:
if plex.is_plex_media_id(media_content_id):
return await plex.async_browse_media(
self.hass,
media_content_type,
media_content_id,
platform=CAST_DOMAIN,
)
return await media_source.async_browse_media(
self.hass, media_content_id, **kwargs
)
if media_content_type == "plex":
return await plex.async_browse_media(
self.hass, None, None, platform=CAST_DOMAIN
)
if "plex" in self.hass.config.components: if "plex" in self.hass.config.components:
children.append( children.append(
BrowseMedia( BrowseMedia(
@ -501,15 +477,30 @@ class CastDevice(MediaPlayerEntity):
) )
) )
if "spotify" in self.hass.config.components:
children.append(
BrowseMedia(
title="Spotify",
media_class=MEDIA_CLASS_APP,
media_content_id="",
media_content_type="spotify",
thumbnail="https://brands.home-assistant.io/_/spotify/logo.png",
can_play=False,
can_expand=True,
)
)
# Add local media source
try: try:
result = await media_source.async_browse_media( result = await media_source.async_browse_media(
self.hass, media_content_id, **kwargs self.hass, None, content_filter=content_filter
) )
children.append(result) children.append(result)
except BrowseError: except BrowseError:
if not children: if not children:
raise raise
# If there's only one media source, resolve it
if len(children) == 1: if len(children) == 1:
return await self.async_browse_media( return await self.async_browse_media(
children[0].media_content_type, children[0].media_content_type,
@ -526,6 +517,44 @@ class CastDevice(MediaPlayerEntity):
children=children, children=children,
) )
async def async_browse_media(self, media_content_type=None, media_content_id=None):
"""Implement the websocket media browsing helper."""
content_filter = None
if self._chromecast.cast_type == pychromecast.const.CAST_TYPE_AUDIO:
def audio_content_filter(item):
"""Filter non audio content."""
return item.media_content_type.startswith("audio/")
content_filter = audio_content_filter
if media_content_id is None:
return await self._async_root_payload(content_filter)
if plex.is_plex_media_id(media_content_id):
return await plex.async_browse_media(
self.hass, media_content_type, media_content_id, platform=CAST_DOMAIN
)
if media_content_type == "plex":
return await plex.async_browse_media(
self.hass, None, None, platform=CAST_DOMAIN
)
if spotify.is_spotify_media_type(media_content_type):
return await spotify.async_browse_media(
self.hass, media_content_type, media_content_id, can_play_artist=False
)
if media_content_type == "spotify":
return await spotify.async_browse_media(
self.hass, None, None, can_play_artist=False
)
return await media_source.async_browse_media(
self.hass, media_content_id, content_filter=content_filter
)
async def async_play_media(self, media_type, media_id, **kwargs): async def async_play_media(self, media_type, media_id, **kwargs):
"""Play a piece of media.""" """Play a piece of media."""
# Handle media_source # Handle media_source
@ -547,12 +576,6 @@ class CastDevice(MediaPlayerEntity):
hass_url = get_url(self.hass, prefer_external=True) hass_url = get_url(self.hass, prefer_external=True)
media_id = f"{hass_url}{media_id}" media_id = f"{hass_url}{media_id}"
await self.hass.async_add_executor_job(
ft.partial(self.play_media, media_type, media_id, **kwargs)
)
def play_media(self, media_type, media_id, **kwargs):
"""Play media from a URL."""
extra = kwargs.get(ATTR_MEDIA_EXTRA, {}) extra = kwargs.get(ATTR_MEDIA_EXTRA, {})
metadata = extra.get("metadata") metadata = extra.get("metadata")
@ -571,7 +594,9 @@ class CastDevice(MediaPlayerEntity):
if "app_id" in app_data: if "app_id" in app_data:
app_id = app_data.pop("app_id") app_id = app_data.pop("app_id")
_LOGGER.info("Starting Cast app by ID %s", app_id) _LOGGER.info("Starting Cast app by ID %s", app_id)
self._chromecast.start_app(app_id) await self.hass.async_add_executor_job(
self._chromecast.start_app, app_id
)
if app_data: if app_data:
_LOGGER.warning( _LOGGER.warning(
"Extra keys %s were ignored. Please use app_name to cast media", "Extra keys %s were ignored. Please use app_name to cast media",
@ -581,21 +606,35 @@ class CastDevice(MediaPlayerEntity):
app_name = app_data.pop("app_name") app_name = app_data.pop("app_name")
try: try:
quick_play(self._chromecast, app_name, app_data) await self.hass.async_add_executor_job(
quick_play, self._chromecast, app_name, app_data
)
except NotImplementedError: except NotImplementedError:
_LOGGER.error("App %s not supported", app_name) _LOGGER.error("App %s not supported", app_name)
# Handle plex # Handle plex
elif media_id and media_id.startswith(PLEX_URI_SCHEME): elif media_id and media_id.startswith(PLEX_URI_SCHEME):
media_id = media_id[len(PLEX_URI_SCHEME) :] media_id = media_id[len(PLEX_URI_SCHEME) :]
media = lookup_plex_media(self.hass, media_type, media_id) media = await self.hass.async_add_executor_job(
lookup_plex_media, self.hass, media_type, media_id
)
if media is None: if media is None:
return return
controller = PlexController() controller = PlexController()
self._chromecast.register_handler(controller) self._chromecast.register_handler(controller)
controller.play_media(media) await self.hass.async_add_executor_job(controller.play_media, media)
# Handle spotify
elif media_id and media_id.startswith("spotify:"):
data = {"entity_id": self.entity_id, "uri": media_id}
await self.hass.services.async_call(
"spotcast", "start", data, blocking=False
)
else: else:
app_data = {"media_id": media_id, "media_type": media_type, **extra} app_data = {"media_id": media_id, "media_type": media_type, **extra}
quick_play(self._chromecast, "default_media_receiver", app_data) await self.hass.async_add_executor_job(
quick_play, self._chromecast, "default_media_receiver", app_data
)
def _media_status(self): def _media_status(self):
""" """