[#389] Fix for "Too many open files" media issue. #390

Merged
blacklight merged 4 commits from fix-too-many-open-files-during-media into master 2024-04-17 04:08:11 +02:00
2 changed files with 55 additions and 41 deletions
Showing only changes of commit f99f6bdab9 - Show all commits

View File

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

View File

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