]> kaliko git repositories - mpd-sima.git/commitdiff
Some refactoring around player
authorkaliko <efrim@azylum.org>
Sun, 15 Dec 2013 10:41:29 +0000 (11:41 +0100)
committerkaliko <efrim@azylum.org>
Sun, 15 Dec 2013 10:41:29 +0000 (11:41 +0100)
Move out of lastfm plugin what was player logic (album search
heuristic mainly)

launch
sima/client.py
sima/lib/player.py
sima/plugins/contrib/placeholder.py
sima/plugins/internal/crop.py
sima/plugins/internal/lastfm.py
sima/plugins/internal/randomfallback.py

diff --git a/launch b/launch
index c7ff8e69825fbe50e2ede515b18ed7e22e429988..39fac649ba25ebdba7e8fc3e5b1a311ffd39a941 100755 (executable)
--- a/launch
+++ b/launch
@@ -3,7 +3,7 @@
 """Sima
 """
 
-# standart library import
+# standard library import
 import logging
 import sys
 
index 80c16e530875a2b6fd68fcff717568bbb339e96d..9724357c3c521acc87689a6143e91910903f33d9 100644 (file)
@@ -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:
@@ -32,6 +31,7 @@ class PlayerCommandError(PlayerError):
 
 PlayerUnHandledError = MPDError  # pylint: disable=C0103
 
+
 class PlayerClient(Player):
     """MPC Client
     From python-musicpd:
@@ -53,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:
@@ -105,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
@@ -123,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' %
@@ -172,13 +189,20 @@ class PlayerClient(Player):
 
     def find_albums(self, artist):
         """
-        Special wrapper around album search:
-        Album lookup is made through AlbumArtist/Album instead of Artist/Album
+        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
         """
-        alb_art_search = self.list('album', 'albumartist', artist,)
-        if alb_art_search:
-            return alb_art_search
-        return self.list('album', 'artist', 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
@@ -188,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)
@@ -200,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'))
@@ -266,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
index ea84a648632a2ae76b36811a651517e69dc5d62d..751e464ba7a3c551aabf3ff0dd2c974028fb3e82 100644 (file)
@@ -3,7 +3,7 @@
 # TODO:
 # Add decorator to filter through history?
 
-# standart library import
+# standard library import
 import logging
 
 # local import
@@ -16,6 +16,14 @@ class Player(object):
 
     When querying player music library for tracks, Player instance *must* return
     Track objects (usually a list of them)
+
+    Player instance should expose the following immutable attributes:
+        * artists
+        * state
+        * current
+        * queue
+        * playlist
+        *
     """
 
     def __init__(self):
@@ -65,10 +73,10 @@ class Player(object):
         """
         raise NotImplementedError
 
-    def fuzzy_find(self, artist):
+    def fuzzy_find_artist(self, artist):
         """
         Find artists based on a fuzzy search in the media library
-            >>> bea = player.fuzzy_find('beatles')
+            >>> bea = player.fuzzy_find_artist('beatles')
             >>> print(bea)
             >>> ['The Beatles']
 
index 665668d66faa30b2c1631b8b4fe41e0fd48d8d09..60e34b50f4c054326e643d9465fe59c98095e96b 100644 (file)
@@ -2,10 +2,10 @@
 """Crops playlist
 """
 
-# standart library import
+# standard library import
 #from select import select
 
-# third parties componants
+# third parties components
 
 # local import
 from sima.lib.plugin import Plugin
index d4df0c3211abc7acc1b728265f074adba7c2a99d..a38b45df2128e7906cc953204c24d7f180fb999a 100644 (file)
@@ -2,10 +2,10 @@
 """Crops playlist
 """
 
-# standart library import
+# standard library import
 #from select import select
 
-# third parties componants
+# third parties components
 
 # local import
 from ...lib.plugin import Plugin
index 03cea6cd4284247ac2180b9f831a56dea5022ca6..1b9ae41798cd39c46efc940f16eb77d99340a2f6 100644 (file)
@@ -3,14 +3,14 @@
 Fetching similar artists from last.fm web services
 """
 
-# standart library import
+# standard library import
 import random
 
 from collections import deque
 from itertools import dropwhile
 from hashlib import md5
 
-# third parties componants
+# third parties components
 
 # local import
 from ...lib.plugin import Plugin
@@ -96,11 +96,9 @@ class Lastfm(Plugin):
         else:
             self.log.info('Lastfm: Initialising cache!')
         self._cache = {
-                'artists': None,
                 'asearch': dict(),
                 'tsearch': dict(),
                 }
-        self._cache['artists'] = frozenset(self.player.list('artist'))
 
     def _cleanup_cache(self):
         """Avoid bloated cache
@@ -184,10 +182,10 @@ class Lastfm(Plugin):
         Look in player library for availability of similar artists in
         similarities
         """
-        dynamic = int(self.plugin_conf.get('dynamic'))
+        dynamic = self.plugin_conf.getint('dynamic')
         if dynamic <= 0:
             dynamic = 100
-        similarity = int(self.plugin_conf.get('similarity'))
+        similarity = self.plugin_conf.getint('similarity')
         results = list()
         similarities.reverse()
         while (len(results) < dynamic
@@ -195,7 +193,7 @@ class Lastfm(Plugin):
             art_pop, match = similarities.pop()
             if match < similarity:
                 break
-            results.extend(self.player.fuzzy_find(art_pop))
+            results.extend(self.player.fuzzy_find_artist(art_pop))
         results and self.log.debug('Similarity: %d%%' % match) # pylint: disable=w0106
         return results
 
@@ -231,7 +229,7 @@ class Lastfm(Plugin):
         depth = 0
         current = self.player.current
         extra_arts = list()
-        while depth < int(self.plugin_conf.get('depth')):
+        while depth < self.plugin_conf.getint('depth'):
             if len(history) == 0:
                 break
             trk = history.popleft()
@@ -282,29 +280,6 @@ class Lastfm(Plugin):
         # artist first.
         return self._get_artists_list_reorg(ret)
 
-    def _detects_var_artists_album(self, album, artist):
-        """Detects either an album is a "Various Artists" or a
-        single artist release."""
-        art_first_track = None
-        for track in self.player.find_album(artist, album):
-            if not art_first_track:  # set artist for the first track
-                art_first_track = track.artist
-            alb_art = track.albumartist
-            #  Special heuristic used when AlbumArtist is available
-            if (alb_art):
-                if artist == alb_art:
-                    # When album artist field is similar to the artist we're
-                    # looking an album for, the album is considered good to
-                    # queue
-                    return False
-                else:
-                    self.log.debug(track)
-                    self.log.debug('album art says "%s", looking for "%s",'
-                                   ' not queueing this album' %
-                                   (alb_art, artist))
-                    return True
-        return False
-
     def _get_album_history(self, artist=None):
         """Retrieve album history"""
         duration = self.daemon_conf.getint('sima', 'history_duration')
@@ -318,10 +293,10 @@ class Lastfm(Plugin):
         """
         self.to_add = list()
         nb_album_add = 0
-        target_album_to_add = int(self.plugin_conf.get('album_to_add'))
+        target_album_to_add = self.plugin_conf.getint('album_to_add')
         for artist in artists:
             self.log.info('Looking for an album to add for "%s"...' % artist)
-            albums = set(self.player.find_albums(artist))
+            albums = self.player.find_albums(artist)
             # albums yet in history for this artist
             albums_yet_in_hist = albums & self._get_album_history(artist=artist)
             albums_not_in_hist = list(albums - albums_yet_in_hist)
@@ -332,9 +307,7 @@ class Lastfm(Plugin):
             album_to_queue = str()
             random.shuffle(albums_not_in_hist)
             for album in albums_not_in_hist:
-                tracks = self.player.find('album', album)
-                if self._detects_var_artists_album(album, artist):
-                    continue
+                tracks = self.player.find_album(artist, album)
                 if tracks and self.sdb.get_bl_album(tracks[0], add_not=True):
                     self.log.info('Blacklisted album: "%s"' % album)
                     self.log.debug('using track: "%s"' % tracks[0])
@@ -361,7 +334,7 @@ class Lastfm(Plugin):
         """Get some tracks for track queue mode
         """
         artists = self.get_local_similar_artists()
-        nbtracks_target = int(self.plugin_conf.get('track_to_add'))
+        nbtracks_target = self.plugin_conf.getint('track_to_add')
         for artist in artists:
             self.log.debug('Trying to find titles to add for "{}"'.format(
                            artist))
index f2eaac4dc9eb412819c64b16a8c5068793697f2a..07201c028a16378cc976018a66ca898364ad9177 100644 (file)
@@ -3,10 +3,10 @@
 Fetching similar artists from last.fm web services
 """
 
-# standart library import
+# standard library import
 import random
 
-# third parties componants
+# third parties components
 
 # local import
 from ...lib.plugin import Plugin