]> kaliko git repositories - mpd-sima.git/commitdiff
Major refactoring of Plugin class
authorkaliko <kaliko@azylum.org>
Fri, 18 Dec 2020 09:11:01 +0000 (10:11 +0100)
committerkaliko <kaliko@azylum.org>
Fri, 18 Dec 2020 09:11:01 +0000 (10:11 +0100)
Add an AdvancedPlugin class with advanced Player/db query. It permits to
share album search for album queue mode and other utilities previously
available in LastFM (ie. webserv) only.

sima/lib/plugin.py
sima/lib/webserv.py
sima/plugins/internal/tags.py

index 3839f8754505591550f632cee6a31ab27db1b59d..83c28b5fda38ad642df9ea4251de48a664e30f2e 100644 (file)
@@ -50,9 +50,10 @@ class Plugin:
 
     def __init__(self, daemon):
         self.log = daemon.log
 
     def __init__(self, daemon):
         self.log = daemon.log
-        self.__daemon = daemon
         self.player = daemon.player
         self.plugin_conf = None
         self.player = daemon.player
         self.plugin_conf = None
+        self.main_conf = daemon.config
+        self.sdb = daemon.sdb
         self.__get_config()
 
     def __str__(self):
         self.__get_config()
 
     def __str__(self):
@@ -61,7 +62,7 @@ class Plugin:
     def __get_config(self):
         """Get plugin's specific configuration from global applications's config
         """
     def __get_config(self):
         """Get plugin's specific configuration from global applications's config
         """
-        conf = self.__daemon.config
+        conf = self.main_conf
         for sec in conf.sections():  # Discovering plugin conf
             if sec == self.__class__.__name__.lower():
                 self.plugin_conf = conf[sec]
         for sec in conf.sections():  # Discovering plugin conf
             if sec == self.__class__.__name__.lower():
                 self.plugin_conf = conf[sec]
@@ -70,7 +71,7 @@ class Plugin:
         if not self.plugin_conf:
             self.plugin_conf = {'priority': '80'}
         #if self.plugin_conf:
         if not self.plugin_conf:
             self.plugin_conf = {'priority': '80'}
         #if self.plugin_conf:
-        #    self.log.debug('Got config for %s: %s', self, self.plugin_conf)
+        #   self.log.debug('Got config for %s: %s', self, self.plugin_conf)
 
     @property
     def priority(self):
 
     @property
     def priority(self):
@@ -127,25 +128,22 @@ class Plugin:
         pass
 
 
         pass
 
 
-class AdvancedLookUp:
+class AdvancedPlugin(Plugin):
     """Object to derive from for plugins
     Exposes advanced music library look up with use of play history
     """
 
     """Object to derive from for plugins
     Exposes advanced music library look up with use of play history
     """
 
-    def __init__(self, daemon):
-        self.log = daemon.log
-        self.daemon = daemon
-        self.player = daemon.player
-
     # Query History
     def get_history(self, artist=False):
     # Query History
     def get_history(self, artist=False):
-        """Constructs list of already played artists.
+        """Constructs Track list of already played artists.
+
+        :param Artist artist: Artist object to look history for
         """
         """
-        duration = self.daemon.config.getint('sima', 'history_duration')
+        duration = self.main_conf.getint('sima', 'history_duration')
         name = None
         if artist:
             name = artist.name
         name = None
         if artist:
             name = artist.name
-        from_db = self.daemon.sdb.get_history(duration=duration, artist=name)
+        from_db = self.sdb.get_history(duration=duration, artist=name)
         hist = [Track(artist=tr[0], album=tr[1], title=tr[2],
                       file=tr[3]) for tr in from_db]
         return hist
         hist = [Track(artist=tr[0], album=tr[1], title=tr[2],
                       file=tr[3]) for tr in from_db]
         return hist
@@ -168,8 +166,8 @@ class AdvancedLookUp:
         :param list(str) alist:
         """
         hist = list()
         :param list(str) alist:
         """
         hist = list()
-        duration = self.daemon.config.getint('sima', 'history_duration')
-        for art in self.daemon.sdb.get_artists_history(alist, duration=duration):
+        duration = self.main_conf.getint('sima', 'history_duration')
+        for art in self.sdb.get_artists_history(alist, duration=duration):
             if art not in hist:
                 hist.insert(0, art)
         reorg = [art for art in alist if art not in hist]
             if art not in hist:
                 hist.insert(0, art)
         reorg = [art for art in alist if art not in hist]
@@ -179,7 +177,8 @@ class AdvancedLookUp:
 
     # Find not recently played/unplayed
     def album_candidate(self, artist, unplayed=True):
 
     # Find not recently played/unplayed
     def album_candidate(self, artist, unplayed=True):
-        """
+        """Search an album for artist
+
         :param Artist artist: Artist to fetch an album for
         :param bool unplayed: Fetch only unplayed album
         """
         :param Artist artist: Artist to fetch an album for
         :param bool unplayed: Fetch only unplayed album
         """
@@ -189,7 +188,7 @@ class AdvancedLookUp:
             return []
         self.log.debug('Albums candidate: %s', albums)
         albums_hist = self.get_album_history(artist)
             return []
         self.log.debug('Albums candidate: %s', albums)
         albums_hist = self.get_album_history(artist)
-        self.log.debug('Albums history: %s', albums_hist)
+        self.log.debug('Albums history: %s', [a.name for a in albums_hist])
         albums_not_in_hist = [a for a in albums if a.name not in albums_hist]
         # Get to next artist if there are no unplayed albums
         if not albums_not_in_hist:
         albums_not_in_hist = [a for a in albums if a.name not in albums_hist]
         # Get to next artist if there are no unplayed albums
         if not albums_not_in_hist:
@@ -236,16 +235,16 @@ class AdvancedLookUp:
             if unplayed:
                 return None
         random.shuffle(not_in_hist)
             if unplayed:
                 return None
         random.shuffle(not_in_hist)
-        candidates = [_ for _ in not_in_hist if _ not in deny_list]
-        for trk in [_ for _ in not_in_hist if _ not in deny_list]:
-            # Should use albumartist heuristic as well
-        #     if self.plugin_conf.getboolean('single_album'):  # pylint: disable=no-member
-                if (trk.album == self.player.current.album or
-        #                 trk.album in [tr.album for tr in black_list]):
-                    self.log.debug('Found unplayed track ' +
-                                   'but from an album already queued: %s', trk)
-                    continue
-            candidates.append(trk)
+        candidates = []
+        for trk in [_ for _ in not_in_hist if _ not in deny_list]:
+            # Should use albumartist heuristic as well
+            if self.plugin_conf.getboolean('single_album', False):  # pylint: disable=no-member
+                if (trk.album == self.player.current.album or
+                        trk.album in [tr.album for tr in deny_list]):
+                    self.log.debug('Found unplayed track ' +
+                                   'but from an album already queued: %s', trk)
+                    continue
+            candidates.append(trk)
         if not candidates:
             return None
         return random.choice(candidates)
         if not candidates:
             return None
         return random.choice(candidates)
index c6a906fce7f5a92091f426a18f266856e6d04d42..c8ab86ec5ba7378bb2a711c8d51c847e7e55dc2e 100644 (file)
@@ -31,8 +31,7 @@ from hashlib import md5
 # third parties components
 
 # local import
 # third parties components
 
 # local import
-from .plugin import Plugin
-from .track import Track
+from .plugin import AdvancedPlugin
 from .meta import Artist, MetaContainer
 from ..utils.utils import WSError, WSNotFound, WSTimeout
 
 from .meta import Artist, MetaContainer
 from ..utils.utils import WSError, WSNotFound, WSTimeout
 
@@ -55,15 +54,13 @@ def cache(func):
     return wrapper
 
 
     return wrapper
 
 
-class WebService(Plugin):
+class WebService(AdvancedPlugin):
     """similar artists webservice
     """
     # pylint: disable=bad-builtin
 
     def __init__(self, daemon):
     """similar artists webservice
     """
     # pylint: disable=bad-builtin
 
     def __init__(self, daemon):
-        Plugin.__init__(self, daemon)
-        self.daemon_conf = daemon.config
-        self.sdb = daemon.sdb
+        super().__init__(daemon)
         self.history = daemon.short_history
         ##
         self.to_add = list()
         self.history = daemon.short_history
         ##
         self.to_add = list()
@@ -96,63 +93,6 @@ class WebService(Plugin):
                 while len(val) > 150:
                     val.popitem()
 
                 while len(val) > 150:
                     val.popitem()
 
-    def get_history(self, artist):
-        """Constructs list of Track for already played titles for an artist.
-        """
-        duration = self.daemon_conf.getint('sima', 'history_duration')
-        tracks_from_db = self.sdb.get_history(duration=duration, artist=artist)
-        # Construct Track() objects list from database history
-        played_tracks = [Track(artist=tr[-1], album=tr[1], title=tr[2],
-                               file=tr[3]) for tr in tracks_from_db]
-        return played_tracks
-
-    def filter_track(self, tracks):
-        """
-        Extract one unplayed track from a Track object list.
-            * not in history
-            * not already in the queue
-            * not blacklisted
-        Then add to candidates in self.to_add
-        """
-        artist = tracks[0].artist
-        # In random play mode use complete playlist to filter
-        if self.player.playmode.get('random'):
-            black_list = self.player.playlist + self.to_add
-        else:
-            black_list = self.player.queue + self.to_add
-        not_in_hist = list(set(tracks) - set(self.get_history(artist=artist)))
-        if self.plugin_conf.get('queue_mode') != 'top' and not not_in_hist:
-            self.log.debug('All tracks already played for "%s"', artist)
-        random.shuffle(not_in_hist)
-        candidate = []
-        for trk in [_ for _ in not_in_hist if _ not in black_list]:
-            # Should use albumartist heuristic as well
-            if self.plugin_conf.getboolean('single_album'):  # pylint: disable=no-member
-                if (trk.album == self.player.current.album or
-                        trk.album in [tr.album for tr in black_list]):
-                    self.log.debug('Found unplayed track ' +
-                                   'but from an album already queued: %s', trk)
-                    continue
-            candidate.append(trk)
-        if not candidate:
-            return False
-        self.to_add.append(random.choice(candidate))
-        return True
-
-    def _get_artists_list_reorg(self, alist):
-        """
-        Move around items in artists_list in order to play first not recently
-        played artists
-        """
-        hist = list()
-        duration = self.daemon_conf.getint('sima', 'history_duration')
-        for art in self.sdb.get_artists_history(alist, duration=duration):
-            if art not in hist:
-                hist.insert(0, art)
-        reorg = [art for art in alist if art not in hist]
-        reorg.extend(hist)
-        return reorg
-
     @cache
     def get_artists_from_player(self, similarities):
         """
     @cache
     def get_artists_from_player(self, similarities):
         """
@@ -273,7 +213,7 @@ class WebService(Plugin):
                 ret_extra = self.get_recursive_similar_artist()
         if ret_extra:
             # get them reorg to pick up best element
                 ret_extra = self.get_recursive_similar_artist()
         if ret_extra:
             # get them reorg to pick up best element
-            ret_extra = self._get_artists_list_reorg(ret_extra)
+            ret_extra = self.get_reorg_artists_list(ret_extra)
             # tries to pickup less artist from extra art
             if len(ret) < 4:
                 ret_extra = MetaContainer(ret_extra)
             # tries to pickup less artist from extra art
             if len(ret) < 4:
                 ret_extra = MetaContainer(ret_extra)
@@ -304,7 +244,7 @@ class WebService(Plugin):
         # Move around similars items to get in unplayed|not recently played
         # artist first.
         self.log.info('Got %d artists in library', len(ret))
         # Move around similars items to get in unplayed|not recently played
         # artist first.
         self.log.info('Got %d artists in library', len(ret))
-        candidates = self._get_artists_list_reorg(list(ret))
+        candidates = self.get_reorg_artists_list(list(ret))
         if candidates:
             self.log.info(' / '.join(map(str, candidates)))
         return candidates
         if candidates:
             self.log.info(' / '.join(map(str, candidates)))
         return candidates
@@ -325,35 +265,7 @@ class WebService(Plugin):
         nb_album_add = 0
         target_album_to_add = self.plugin_conf.getint('album_to_add')  # pylint: disable=no-member
         for artist in artists:
         nb_album_add = 0
         target_album_to_add = self.plugin_conf.getint('album_to_add')  # pylint: disable=no-member
         for artist in artists:
-            self.log.info('Looking for an album to add for "%s"...' % artist)
-            albums = self.player.search_albums(artist)
-            if not albums:
-                continue
-            self.log.debug('Albums candidate: %s', albums)
-            albums_hist = self._get_album_history(artist)
-            albums_not_in_hist = [a for a in albums if a.name not in albums_hist]
-            # Get to next artist if there are no unplayed albums
-            if not albums_not_in_hist:
-                self.log.info('No unplayed album found for "%s"' % artist)
-                continue
-            album_to_queue = []
-            random.shuffle(albums_not_in_hist)
-            for album in albums_not_in_hist:
-                # Controls the album found is not already queued
-                if album in {t.album for t in self.player.queue}:
-                    self.log.debug('"%s" already queued, skipping!', album)
-                    continue
-                # In random play mode use complete playlist to filter
-                if self.player.playmode.get('random'):
-                    if album in {t.album for t in self.player.playlist}:
-                        self.log.debug('"%s" already in playlist, skipping!', album)
-                        continue
-                album_to_queue = album
-            if not album_to_queue:
-                self.log.info('No album found for "%s"', artist)
-                continue
-            self.log.info('%s album candidate: %s - %s', self.ws.name,
-                          artist, album_to_queue)
+            album = self.album_candidate(artist)
             nb_album_add += 1
             candidates = self.player.find_tracks(album)
             if self.plugin_conf.getboolean('shuffle_album'):
             nb_album_add += 1
             candidates = self.player.find_tracks(album)
             if self.plugin_conf.getboolean('shuffle_album'):
@@ -387,7 +299,9 @@ class WebService(Plugin):
                 found = self.player.search_track(artist, trk.title)
                 if found:
                     random.shuffle(found)
                 found = self.player.search_track(artist, trk.title)
                 if found:
                     random.shuffle(found)
-                    if self.filter_track(found):
+                    top_trk = self.filter_track(found)
+                    if top_trk:
+                        self.to_add.append(top_trk)
                         break
 
     def _track(self):
                         break
 
     def _track(self):
@@ -403,7 +317,9 @@ class WebService(Plugin):
                 self.log.debug('Found nothing to queue for %s', artist)
                 continue
             # find tracks not in history for artist
                 self.log.debug('Found nothing to queue for %s', artist)
                 continue
             # find tracks not in history for artist
-            self.filter_track(found)
+            track_candidate = self.filter_track(found)
+            if track_candidate:
+                self.to_add.append(track_candidate)
             if len(self.to_add) == nbtracks_target:
                 break
         if not self.to_add:
             if len(self.to_add) == nbtracks_target:
                 break
         if not self.to_add:
index 1536c5ce2c87a91ba464861aa9a9d4b3ebef094a..0af81ef3f0b7699dfc408b9e931d1203359e1848 100644 (file)
@@ -28,8 +28,8 @@ import random
 from musicpd import CommandError
 
 # local import
 from musicpd import CommandError
 
 # local import
-from ...lib.plugin import Plugin, AdvancedLookUp
-from ...lib.meta import Artist, Album
+from ...lib.plugin import AdvancedPlugin
+from ...lib.meta import Artist
 from ...utils.utils import PluginException
 
 
 from ...utils.utils import PluginException
 
 
@@ -53,7 +53,7 @@ def forge_filter(cfg):
     return mpd_filter
 
 
     return mpd_filter
 
 
-class Tags(Plugin, AdvancedLookUp):
+class Tags(AdvancedPlugin):
     """Add track based on tags content
     """
     supported_tags = {'comment', 'date', 'genre', 'label', 'originaldate'}
     """Add track based on tags content
     """
     supported_tags = {'comment', 'date', 'genre', 'label', 'originaldate'}
@@ -61,7 +61,6 @@ class Tags(Plugin, AdvancedLookUp):
 
     def __init__(self, daemon):
         super().__init__(daemon)
 
     def __init__(self, daemon):
         super().__init__(daemon)
-        self.daemon = daemon
         self._control_conf()
         self.mpd_filter = forge_filter(self.plugin_conf)
         self._setup_tagsneeded()
         self._control_conf()
         self.mpd_filter = forge_filter(self.plugin_conf)
         self._setup_tagsneeded()