diff --git a/platypush/plugins/media/chromecast/__init__.py b/platypush/plugins/media/chromecast/__init__.py index 9afb435777..fcf1a446ae 100644 --- a/platypush/plugins/media/chromecast/__init__.py +++ b/platypush/plugins/media/chromecast/__init__.py @@ -31,7 +31,6 @@ class MediaChromecastPlugin(MediaPlugin, RunnablePlugin): :param poll_interval: How often the plugin should poll for new/removed Chromecast devices (default: 30 seconds). """ - super().__init__(poll_interval=poll_interval, **kwargs) self._is_local = False @@ -42,11 +41,18 @@ class MediaChromecastPlugin(MediaPlugin, RunnablePlugin): def _get_chromecasts(self, *args, **kwargs): with self._refresh_lock: - chromecasts = pychromecast.get_chromecasts(*args, **kwargs) + ret = pychromecast.get_chromecasts(*args, **kwargs) - if isinstance(chromecasts, tuple): - return chromecasts[0] - return chromecasts + if isinstance(ret, tuple): + chromecasts, browser = ret + if browser: + browser.stop_discovery() + if browser.zc: + browser.zc.close() + + return chromecasts + + return ret @staticmethod def _get_device_property(cc, prop: str): @@ -58,14 +64,25 @@ class MediaChromecastPlugin(MediaPlugin, RunnablePlugin): """ Convert a Chromecast object and its status to a dictionary. """ + if hasattr(cc, 'cast_info'): # Newer PyChromecast API + host = cc.cast_info.host + port = cc.cast_info.port + elif hasattr(cc, 'host'): + host = getattr(cc, 'host', None) + port = getattr(cc, 'port', None) + elif hasattr(cc, 'uri'): + host, port = cc.uri.split(':') + else: + raise RuntimeError('Invalid Chromecast object') + return { 'type': cc.cast_type, 'name': cc.name, 'manufacturer': self._get_device_property(cc, 'manufacturer'), 'model_name': cc.model_name, 'uuid': str(cc.uuid), - 'address': cc.host if hasattr(cc, 'host') else cc.uri.split(':')[0], - 'port': cc.port if hasattr(cc, 'port') else int(cc.uri.split(':')[1]), + 'address': host, + 'port': port, 'status': ( { 'app': { @@ -284,24 +301,23 @@ class MediaChromecastPlugin(MediaPlugin, RunnablePlugin): chromecast = chromecast or self.chromecast cast = self.get_chromecast(chromecast) - if cast.media_controller.is_paused: + if cast.media_controller.status.player_is_paused: cast.media_controller.play() - elif cast.media_controller.is_playing: + elif cast.media_controller.status.player_is_playing: cast.media_controller.pause() cast.wait() return self.status(chromecast=chromecast) @action - def stop(self, *_, chromecast: Optional[str] = None, **__): + def stop(self, *_, chromecast: Optional[str] = None, **__): # type: ignore chromecast = chromecast or self.chromecast if not chromecast: - return + return None cast = self.get_chromecast(chromecast) cast.media_controller.stop() cast.wait() - return self.status(chromecast=chromecast) @action @@ -347,51 +363,51 @@ class MediaChromecastPlugin(MediaPlugin, RunnablePlugin): def is_playing(self, chromecast: Optional[str] = None, **_): return self.get_chromecast( chromecast or self.chromecast - ).media_controller.is_playing + ).media_controller.status.player_is_playing @action def is_paused(self, chromecast: Optional[str] = None, **_): return self.get_chromecast( chromecast or self.chromecast - ).media_controller.is_paused + ).media_controller.status.player_is_paused @action def is_idle(self, chromecast: Optional[str] = None): return self.get_chromecast( chromecast or self.chromecast - ).media_controller.is_idle + ).media_controller.status.player_is_idle @action def list_subtitles(self, chromecast: Optional[str] = None): return self.get_chromecast( chromecast or self.chromecast - ).media_controller.subtitle_tracks + ).media_controller.status.subtitle_tracks @action def enable_subtitles( - self, chromecast: Optional[str] = None, track_id: Optional[str] = None, **_ + self, chromecast: Optional[str] = None, track_id: Optional[int] = None, **_ ): mc = self.get_chromecast(chromecast or self.chromecast).media_controller if track_id is not None: return mc.enable_subtitle(track_id) - if mc.subtitle_tracks: - return mc.enable_subtitle(mc.subtitle_tracks[0].get('trackId')) + if mc.status.subtitle_tracks: + return mc.enable_subtitle(mc.status.subtitle_tracks[0].get('trackId')) @action def disable_subtitles( - self, chromecast: Optional[str] = None, track_id: Optional[str] = None, **_ + self, chromecast: Optional[str] = None, track_id: Optional[int] = None, **_ ): mc = self.get_chromecast(chromecast or self.chromecast).media_controller if track_id: return mc.disable_subtitle(track_id) - if mc.current_subtitle_tracks: - return mc.disable_subtitle(mc.current_subtitle_tracks[0]) + if mc.status.current_subtitle_tracks: + return mc.disable_subtitle(mc.status.current_subtitle_tracks[0]) @action def toggle_subtitles(self, chromecast: Optional[str] = None, **_): mc = self.get_chromecast(chromecast or self.chromecast).media_controller all_subs = mc.status.subtitle_tracks - cur_subs = mc.status.status.current_subtitle_tracks + cur_subs = mc.status.current_subtitle_tracks if cur_subs: return self.disable_subtitles(chromecast, cur_subs[0]) @@ -511,7 +527,6 @@ class MediaChromecastPlugin(MediaPlugin, RunnablePlugin): self, chromecast: Optional[str] = None, timeout: Optional[float] = None, - blocking: bool = True, ): """ Disconnect a Chromecast and wait for it to terminate @@ -520,11 +535,9 @@ class MediaChromecastPlugin(MediaPlugin, RunnablePlugin): the default configured Chromecast will be used. :param timeout: Number of seconds to wait for disconnection (default: None: block until termination). - :param blocking: If set (default), then the code will wait until - disconnection, otherwise it will return immediately. """ cast = self.get_chromecast(chromecast) - cast.disconnect(timeout=timeout, blocking=blocking) + cast.disconnect(timeout=timeout) @action def join(self, chromecast: Optional[str] = None, timeout: Optional[float] = None): @@ -550,17 +563,6 @@ class MediaChromecastPlugin(MediaPlugin, RunnablePlugin): cast = self.get_chromecast(chromecast) cast.quit_app() - @action - def reboot(self, chromecast: Optional[str] = None): - """ - Reboots the Chromecast - - :param chromecast: Chromecast to cast to. If none is specified, then - the default configured Chromecast will be used. - """ - cast = self.get_chromecast(chromecast) - cast.reboot() - @action def set_volume(self, volume: float, chromecast: Optional[str] = None): """ @@ -621,7 +623,7 @@ class MediaChromecastPlugin(MediaPlugin, RunnablePlugin): """ chromecast = chromecast or self.chromecast cast = self.get_chromecast(chromecast) - cast.set_volume_muted(not cast.status.volume_muted) + cast.set_volume_muted(not cast.media_controller.status.volume_muted) cast.wait() return self.status(chromecast=chromecast) diff --git a/platypush/plugins/media/chromecast/_subtitles.py b/platypush/plugins/media/chromecast/_subtitles.py index 450e7a268d..ca3b2c4949 100644 --- a/platypush/plugins/media/chromecast/_subtitles.py +++ b/platypush/plugins/media/chromecast/_subtitles.py @@ -1,5 +1,9 @@ -# pylint: disable=too-few-public-methods -class SubtitlesAsyncHandler: +import logging + +from pychromecast.controllers.media import MediaStatusListener + + +class SubtitlesAsyncHandler(MediaStatusListener): """ This class is used to enable subtitles when the media is loaded. """ @@ -8,9 +12,17 @@ class SubtitlesAsyncHandler: self.mc = mc self.subtitle_id = subtitle_id self.initialized = False + self.logger = logging.getLogger(__name__) def new_media_status(self, *_): if self.subtitle_id and not self.initialized: self.mc.update_status() self.mc.enable_subtitle(self.subtitle_id) self.initialized = True + + def load_media_failed(self, queue_item_id: int, error_code: int) -> None: + self.logger.warning( + "Failed to load media with queue_item_id %d, error code: %d", + queue_item_id, + error_code, + )