X-Git-Url: https://git.kaliko.me/?a=blobdiff_plain;f=sima%2Fmpdclient.py;h=53a62d0ceddfe9bd74584494a210584e37c3f719;hb=774e755;hp=4e6af68f37ec2614b71f3114da9746d56ae85af8;hpb=a978d2975c3201dbac0c9e43a08337a800438d2f;p=mpd-sima.git diff --git a/sima/mpdclient.py b/sima/mpdclient.py index 4e6af68..53a62d0 100644 --- a/sima/mpdclient.py +++ b/sima/mpdclient.py @@ -20,9 +20,10 @@ from difflib import get_close_matches from functools import wraps from logging import getLogger +from select import select # external module -from musicpd import MPDClient, MPDError as PlayerError +from musicpd import MPDClient, MPDError # local import @@ -30,6 +31,11 @@ from .lib.meta import Meta, Artist, Album from .lib.track import Track from .lib.simastr import SimaStr from .utils.leven import levenshtein_ratio +from .utils.utils import MPDSimaException + + +class PlayerError(MPDSimaException): + """Fatal error in the player.""" # Some decorators @@ -49,6 +55,7 @@ def bl_artist(func): return result return wrapper + def set_artist_mbid(func): def wrapper(*args, **kwargs): cls = args[0] @@ -62,6 +69,7 @@ def set_artist_mbid(func): return result return wrapper + def tracks_wrapper(func): """Convert plain track mapping as returned by MPDClient into :py:obj:`sima.lib.track.Track` objects. This decorator accepts single track or list of tracks as input. @@ -106,6 +114,7 @@ class MPD(MPDClient): def __init__(self, config): super().__init__() + self.socket_timeout = 10 self.use_mbid = True self.log = getLogger('sima') self.config = config @@ -115,9 +124,12 @@ class MPD(MPDClient): def __getattr__(self, cmd): """Wrapper around MPDClient calls for abstract overriding""" track_wrapped = {'currentsong', 'find', 'playlistinfo', } - if cmd in track_wrapped: - return tracks_wrapper(super().__getattr__(cmd)) - return super().__getattr__(cmd) + try: + if cmd in track_wrapped: + return tracks_wrapper(super().__getattr__(cmd)) + return super().__getattr__(cmd) + except OSError as err: + raise PlayerError(err) from err def disconnect(self): """Overriding explicitly MPDClient.disconnect()""" @@ -137,19 +149,19 @@ class MPD(MPDClient): # Catch socket errors except OSError as err: raise PlayerError('Could not connect to "%s:%s": %s' % - (host, port, err.strerror)) + (host, port, err.strerror)) from err # Catch all other possible errors # ConnectionError and ProtocolError are always fatal. Others may not # be, but we don't know how to handle them here, so treat them as if # they are instead of ignoring them. - except PlayerError as err: + except MPDError as err: raise PlayerError('Could not connect to "%s:%s": %s' % - (host, port, err)) + (host, port, err)) from err if password: try: self.password(password) - except (PlayerError, OSError) as err: - raise PlayerError("Could not connect to '%s': %s" % (host, err)) + except (MPDError, OSError) as err: + raise PlayerError("Could not connect to '%s': %s" % (host, err)) from err # Controls we have sufficient rights available_cmd = self.commands() for cmd in MPD.needed_cmds: @@ -226,15 +238,22 @@ class MPD(MPDClient): * skipped current track skipped """ curr = self.current - try: - ret = self.idle('database', 'playlist', 'player', 'options') - except (PlayerError, OSError) as err: - raise PlayerError("Couldn't init idle: %s" % err) - if self._skipped_track(curr): - ret.append('skipped') - if 'database' in ret: - self._reset_cache() - return ret + select_timeout = 5 + while True: + self.send_idle('database', 'playlist', 'player', 'options') + _read, _, _ = select([self], [], [], select_timeout) + if _read: # tries to read response + ret = self.fetch_idle() + if self._skipped_track(curr): + ret.append('skipped') + if 'database' in ret: + self._reset_cache() + return ret + # Nothing to read, canceling idle + try: # noidle cmd does not go through __getattr__, need to catch OSError then + self.noidle() + except OSError as err: + raise PlayerError(err) from err def clean(self): """Clean blocking event (idle) and pending commands