]> kaliko git repositories - mpd-sima.git/blobdiff - sima/client.py
Better MusicBrainz ID integration
[mpd-sima.git] / sima / client.py
index 0bfc8826be36ca0027df7b6df3af3598456527f7..b8ccea522a2ee62993ee22beb42b631120e42c4a 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,29 +160,86 @@ 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._client.list('artist'))
+        self._cache['nombid_artists'] = frozenset(self._client.list('artist', 'musicbrainz_artistid', ''))
 
     @blacklist(track=True)
 
     @blacklist(track=True)
-    def _find_track(self, artist, title):
-        #return getattr(self, 'find')('artist', artist, 'title', title)
-        if title:
-            return self.find('artist', artist, 'title', title)
-        return self.find('artist', artist)
-
     def find_track(self, artist, title=None):
     def find_track(self, artist, title=None):
-        tracks = list()
-        if isinstance(artist, Artist):
-            for name in artist.names:
-                tracks.extend(self._find_track(name, title=title))
+        tracks = set()
+        for name in artist.names:
+            if title:
+                tracks |= set(self.find('artist', name, 'title', title))
+            else:
+                tracks |= set(self.find('artist', name))
+        if artist.mbid:
+            if title:
+                tracks |= set(self.find('musicbrainz_artistid', artist.mbid))
+            else:
+                tracks |= set(self.find('musicbrainz_artistid', artist.mbid,
+                                        '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
+            [artist.add_alias(name) for name in
+                    self._client.list('artist', 'musicbrainz_artistid', artist.mbid)]
+            if artist.aliases:
+                found = True
         else:
         else:
-            tracks.extend(self._find_track(artist,title=title))
-        return tracks
+            artist = Artist(name=artist.name)
+        # then complete with fuzzy search on artist with no musicbrainz_artistid
+        nombid_artists = self._cache.get('nombid_artists', [])
+        match = get_close_matches(artist.name, nombid_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
 
 
-    @blacklist(track=True)
     def fuzzy_find_track(self, artist, title):
         # Retrieve all tracks from artist
     def fuzzy_find_track(self, artist, title):
         # Retrieve all tracks from artist
-        all_tracks = self.find('artist', artist)
+        all_tracks = self.find_track(artist, title)
         # Get all titles (filter missing titles set to 'None')
         all_artist_titles = frozenset([tr.title for tr in all_tracks
                                        if tr.title is not None])
         # Get all titles (filter missing titles set to 'None')
         all_artist_titles = frozenset([tr.title for tr in all_tracks
                                        if tr.title is not None])
@@ -197,8 +277,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:
@@ -324,6 +405,8 @@ class PlayerClient(Player):
                 self.log.warning('Use of MusicBrainzIdentifier is set but MPD is '
                         'not providing related metadata')
                 self.log.info(self._client.tagtypes())
                 self.log.warning('Use of MusicBrainzIdentifier is set but MPD is '
                         'not providing related metadata')
                 self.log.info(self._client.tagtypes())
+                self.log.warning('Disabling MusicBrainzIdentifier')
+                Artist.use_mbid = False
         else:
             self.log.warning('Use of MusicBrainzIdentifier disabled!')
             self.log.info('Consider using MusicBrainzIdentifier for your music library')
         else:
             self.log.warning('Use of MusicBrainzIdentifier disabled!')
             self.log.info('Consider using MusicBrainzIdentifier for your music library')