forked from platypush/platypush
[media.chromecast] Resource clean up + new API adaptations.
- `pychromecast.get_chromecasts` returns both a list of devices and a browser object. Since the Chromecast plugin is the most likely culprit of the excessive number of open MDNS sockets, it seems that we may need to explicitly stop discovery on the browser and close the ZeroConf object after the discovery is done. - I was still using an ancient version of pychromecast on my RPi4, and I didn't notice that more recent versions implemented several breaking changes. Adapted the code to cope with those changes.
This commit is contained in:
parent
4972c8bdcf
commit
f99f6bdab9
2 changed files with 55 additions and 41 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue