X-Git-Url: http://git.kaliko.me/?a=blobdiff_plain;f=sima%2Fclient.py;h=9724357c3c521acc87689a6143e91910903f33d9;hb=8ce90ac30d1daa9bcf2ced0aa06c8c59302f71ca;hp=929f66281fd7489cc2d001a75efa5755e3ffe138;hpb=c660efb577c11bde6229d37550bf197fa6bae3e4;p=mpd-sima.git diff --git a/sima/client.py b/sima/client.py index 929f662..9724357 100644 --- a/sima/client.py +++ b/sima/client.py @@ -5,12 +5,11 @@ This client is built above python-musicpd a fork of python-mpd """ # pylint: disable=C0111 -# standart library import - +# standard library import from difflib import get_close_matches from select import select -# third parties componants +# third parties components try: from musicpd import (MPDClient, MPDError, CommandError) except ImportError as err: @@ -22,7 +21,6 @@ except ImportError as err: from .lib.player import Player from .lib.track import Track from .lib.simastr import SimaStr -from .utils.leven import levenshtein_ratio class PlayerError(Exception): @@ -33,6 +31,7 @@ class PlayerCommandError(PlayerError): PlayerUnHandledError = MPDError # pylint: disable=C0103 + class PlayerClient(Player): """MPC Client From python-musicpd: @@ -54,17 +53,22 @@ class PlayerClient(Player): self._mpd = host, port, password self._client = MPDClient() self._client.iterate = True + self._cache = None def __getattr__(self, attr): command = attr wrapper = self._execute return lambda *args: wrapper(command, args) + def __del__(self): + """Avoid hanging sockets""" + self.disconnect() + def _execute(self, command, args): self._write_command(command, args) return self._client_wrapper() - def _write_command(self, command, args=[]): + def _write_command(self, command, args=None): self._comm = command self._args = list() for arg in args: @@ -106,13 +110,26 @@ class PlayerClient(Player): return False return (self.current.id != old_curr.id) # pylint: disable=no-member + def _flush_cache(self): + """ + Both flushes and instantiates _cache + """ + if isinstance(self._cache, dict): + self.log.info('Player: Flushing cache!') + else: + self.log.info('Player: Initialising cache!') + self._cache = { + 'artists': None, + } + self._cache['artists'] = frozenset(self._client.list('artist')) + def find_track(self, artist, title=None): #return getattr(self, 'find')('artist', artist, 'title', title) if title: return self.find('artist', artist, 'title', title) return self.find('artist', artist) - def fuzzy_find(self, art): + def fuzzy_find_artist(self, art): """ Controls presence of artist in music library. Crosschecking artist names with SimaStr objects / difflib / levenshtein @@ -124,14 +141,13 @@ class PlayerClient(Player): """ matching_artists = list() artist = SimaStr(art) - all_artists = self.list('artist') # Check against the actual string in artist list - if artist.orig in all_artists: + if artist.orig in self.artists: self.log.debug('found exact match for "%s"' % artist) return [artist] # Then proceed with fuzzy matching if got nothing - match = get_close_matches(artist.orig, all_artists, 50, 0.73) + match = get_close_matches(artist.orig, self.artists, 50, 0.73) if not match: return [] self.log.debug('found close match for "%s": %s' % @@ -151,21 +167,14 @@ class PlayerClient(Player): matching_artists.append(fuzz_art) self.log.debug('"%s" matches "%s".' % (fuzz_art, artist)) return matching_artists - # Proceed with levenshtein and SimaStr - leven = levenshtein_ratio(artist.stripped.lower(), - SimaStr(fuzz_art).stripped.lower()) - # SimaStr string __eq__, not regular string comparison here + # SimaStr string __eq__ (not regular string comparison here) if artist == fuzz_art: matching_artists.append(fuzz_art) self.log.info('"%s" quite probably matches "%s" (SimaStr)' % (fuzz_art, artist)) - elif leven >= 0.82: # PARAM - matching_artists.append(fuzz_art) - self.log.debug('FZZZ: "%s" should match "%s" (lr=%1.3f)' % - (fuzz_art, artist, leven)) else: - self.log.debug('FZZZ: "%s" does not match "%s" (lr=%1.3f)' % - (fuzz_art, artist, leven)) + self.log.debug('FZZZ: "%s" does not match "%s"' % + (fuzz_art, artist)) return matching_artists def find_album(self, artist, album): @@ -178,6 +187,23 @@ class PlayerClient(Player): return alb_art_search return self.find('artist', artist, 'album', album) + def find_albums(self, artist): + """ + Fetch all albums for "AlbumArtist" == artist + Filter albums returned for "artist" == artist since MPD returns any + album containing at least a single track for artist + """ + albums = set() + albums.update(self.list('album', 'albumartist', artist)) + for album in self.list('album', 'artist', artist): + arts = set([trk.artist for trk in self.find('album', album)]) + if len(arts) < 2: + albums.add(album) + else: + self.log.debug('"{0}" probably not an album of "{1}"'.format( + album, artist) + '({0})'.format('/'.join(arts))) + return albums + def monitor(self): curr = self.current try: @@ -186,6 +212,8 @@ class PlayerClient(Player): ret = self._client.fetch_idle() if self.__skipped_track(curr): ret.append('skipped') + if 'database' in ret: + self._flush_cache() return ret except (MPDError, IOError) as err: raise PlayerError("Couldn't init idle: %s" % err) @@ -198,6 +226,10 @@ class PlayerClient(Player): object""" self._client.add(track.file) + @property + def artists(self): + return self._cache.get('artists') + @property def state(self): return str(self._client.status().get('state')) @@ -264,6 +296,7 @@ class PlayerClient(Player): raise PlayerError('Could connect to "%s", ' 'but command "%s" not available' % (host, nddcmd)) + self._flush_cache() def disconnect(self): # Try to tell MPD we're closing the connection first