]> kaliko git repositories - mpd-sima.git/blobdiff - sima/client.py
Add priority to plugins
[mpd-sima.git] / sima / client.py
index 2893d9b52cf52bb5a53266ec2bdedc7f0e05ebcd..926e2952acc5a9c177d8b622f01bb4d620935217 100644 (file)
@@ -36,6 +36,7 @@ except ImportError as err:
     sexit(1)
 
 # local import
     sexit(1)
 
 # local import
+from .lib.simastr import SimaStr
 from .lib.player import Player, blacklist
 from .lib.track import Track
 from .lib.meta import Album, Artist
 from .lib.player import Player, blacklist
 from .lib.track import Track
 from .lib.meta import Album, Artist
@@ -50,6 +51,28 @@ class PlayerCommandError(PlayerError):
 
 PlayerUnHandledError = MPDError  # pylint: disable=C0103
 
 
 PlayerUnHandledError = MPDError  # pylint: disable=C0103
 
+def bl_artist(func):
+    def wrapper(*args, **kwargs):
+        cls = args[0]
+        if not args[0].database:
+            return func(*args, **kwargs)
+        result = func(*args, **kwargs)
+        if not result:
+            return
+        names = list()
+        for art in result.names:
+            if cls.database.get_bl_artist(art, add_not=True):
+                cls.log.debug('Blacklisted "{0}"'.format(art))
+                continue
+            names.append(art)
+        if not names:
+            return
+        resp = Artist(name=names.pop(), mbid=result.mbid)
+        for name in names:
+            resp.add_alias(name)
+        return resp
+    return wrapper
+
 
 class PlayerClient(Player):
     """MPD Client
 
 class PlayerClient(Player):
     """MPD Client
@@ -137,8 +160,11 @@ class PlayerClient(Player):
             self.log.info('Player: Initialising cache!')
         self._cache = {
                 'artists': None,
             self.log.info('Player: Initialising cache!')
         self._cache = {
                 'artists': None,
+                'nombid_artists': None,
                 }
                 }
-        self._cache['artists'] = frozenset(self._client.list('artist'))
+        self._cache['artists'] = frozenset(self._execute('list', ['artist']))
+        if Artist.use_mbid:
+            self._cache['nombid_artists'] = frozenset(self._execute('list', ['artist', 'musicbrainz_artistid', '']))
 
     @blacklist(track=True)
     def find_track(self, artist, title=None):
 
     @blacklist(track=True)
     def find_track(self, artist, title=None):
@@ -156,6 +182,67 @@ class PlayerClient(Player):
                                         'title', title))
         return list(tracks)
 
                                         'title', title))
         return list(tracks)
 
+    @bl_artist
+    def search_artist(self, artist):
+        """
+        Search artists based on a fuzzy search in the media library
+            >>> art = Artist(name='the beatles', mbid=<UUID4>) # mbid optional
+            >>> bea = player.search_artist(art)
+            >>> print(bea.names)
+            >>> ['The Beatles', 'Beatles', 'the beatles']
+
+        Returns an Artist object
+        """
+        found = False
+        if artist.mbid:
+            # look for exact search w/ musicbrainz_artistid
+            exact_m = self._execute('list', ['artist', 'musicbrainz_artistid', artist.mbid])
+            if exact_m:
+                [artist.add_alias(name) for name in exact_m]
+                found = True
+        else:
+            artist = Artist(name=artist.name)
+        # then complete with fuzzy search on artist with no musicbrainz_artistid
+        if artist.mbid:
+            # we already performed a lookup on artists with mbid set
+            # search through remaining artists
+            artists = self._cache.get('nombid_artists', [])
+        else:
+            artists = self._cache.get('artists', [])
+        match = get_close_matches(artist.name, artists, 50, 0.73)
+        if not match and not found:
+            return
+        if len(match) > 1:
+            self.log.debug('found close match for "%s": %s' %
+                           (artist, '/'.join(match)))
+        # Does not perform fuzzy matching on short and single word strings
+        # Only lowercased comparison
+        if ' ' not in artist.name and len(artist.name) < 8:
+            for fuzz_art in match:
+                # Regular lowered string comparison
+                if artist.name.lower() == fuzz_art.lower():
+                    artist.add_alias(fuzz_art)
+                    return artist
+        fzartist = SimaStr(artist.name)
+        for fuzz_art in match:
+            # Regular lowered string comparison
+            if artist.name.lower() == fuzz_art.lower():
+                found = True
+                artist.add_alias(fuzz_art)
+                if artist.name != fuzz_art:
+                    self.log.debug('"%s" matches "%s".' % (fuzz_art, artist))
+                continue
+            # SimaStr string __eq__ (not regular string comparison here)
+            if fzartist == fuzz_art:
+                found = True
+                artist.add_alias(fuzz_art)
+                self.log.info('"%s" quite probably matches "%s" (SimaStr)' %
+                              (fuzz_art, artist))
+        if found:
+            if artist.aliases:
+                self.log.debug('Found: {}'.format('/'.join(list(artist.names)[:4])))
+            return artist
+
     def fuzzy_find_track(self, artist, title):
         # Retrieve all tracks from artist
         all_tracks = self.find_track(artist, title)
     def fuzzy_find_track(self, artist, title):
         # Retrieve all tracks from artist
         all_tracks = self.find_track(artist, title)
@@ -196,8 +283,9 @@ class PlayerClient(Player):
                album containing at least a single track for artist
         """
         albums = []
                album containing at least a single track for artist
         """
         albums = []
-        for name in artist.aliases:
-            self.log.debug('Searching album for {}'.format(name))
+        for name in artist.names:
+            if len(artist.names) > 1:
+                self.log.debug('Searching album for aliase: "{}"'.format(name))
             kwalbart = {'albumartist':name, 'artist':name}
             for album in self.list('album', 'albumartist', artist):
                 if album and album not in albums:
             kwalbart = {'albumartist':name, 'artist':name}
             for album in self.list('album', 'albumartist', artist):
                 if album and album not in albums:
@@ -210,7 +298,7 @@ class PlayerClient(Player):
                 arts = set([trk.artist for trk in album_trks])
                 if len(set(arts)) < 2:  # TODO: better heuristic, use a ratio instead
                     if album not in albums:
                 arts = set([trk.artist for trk in album_trks])
                 if len(set(arts)) < 2:  # TODO: better heuristic, use a ratio instead
                     if album not in albums:
-                        albums.append(Album(name=album, albumartist=artist))
+                        albums.append(Album(name=album, **kwalbart))
                 elif album and album not in albums:
                     self.log.debug('"{0}" probably not an album of "{1}"'.format(
                                    album, artist) + '({0})'.format('/'.join(arts)))
                 elif album and album not in albums:
                     self.log.debug('"{0}" probably not an album of "{1}"'.format(
                                    album, artist) + '({0})'.format('/'.join(arts)))
@@ -244,11 +332,11 @@ class PlayerClient(Player):
     def add(self, track):
         """Overriding MPD's add method to accept add signature with a Track
         object"""
     def add(self, track):
         """Overriding MPD's add method to accept add signature with a Track
         object"""
-        self._client.add(track.file)
+        self._execute('add', [track.file])
 
     @property
     def artists(self):
 
     @property
     def artists(self):
-        return self._cache.get('artists')
+        return self._cache.get('artists') | self._cache.get('nombid_artists')
 
     @property
     def state(self):
 
     @property
     def state(self):