From 23309f235879380a5ef7db3a51bda4d12e31902e Mon Sep 17 00:00:00 2001 From: kaliko Date: Thu, 5 Feb 2015 15:50:45 +0100 Subject: [PATCH] Add priority to plugins --- data/man/mpd_sima.cfg.5.xml | 18 +++---- doc/Changelog | 3 +- doc/examples/all_settings.cfg | 13 +++-- sima/client.py | 2 +- sima/core.py | 43 +++++++++++----- sima/launch.py | 10 ++-- sima/lib/plugin.py | 11 ++++- .../internal/{randomfallback.py => random.py} | 12 ++--- sima/utils/config.py | 7 ++- tests/test_plugin.py | 49 +++++++++++++++++++ 10 files changed, 124 insertions(+), 44 deletions(-) rename sima/plugins/internal/{randomfallback.py => random.py} (90%) create mode 100644 tests/test_plugin.py diff --git a/data/man/mpd_sima.cfg.5.xml b/data/man/mpd_sima.cfg.5.xml index ef483a1..b6bd123 100644 --- a/data/man/mpd_sima.cfg.5.xml +++ b/data/man/mpd_sima.cfg.5.xml @@ -222,13 +222,13 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ "AwesomePlugin" declared here gets its configuration from the corresponding section "[awesomeplugin]".internal plugins will look for a section named after the lower-cased name of the - pluglin, ie. RandomFallBack → randomfallback. + plugin, ie. Random → random. - Crop, RandomFallBack, Lastfm + Crop, Random, Lastfm - and + and are utilities plugins while is the actual queue plugin. Another queue plugin is available as a "techno preview", it relies on EchoNest web services, replace @@ -259,13 +259,13 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ - - RandomFallback section - RandomFallback plugin's configuration: - - + + Random section + Random plugin's configuration: + + - + sensible When no similar tracks are found, falling back to diff --git a/doc/Changelog b/doc/Changelog index f9466e1..bbdbf17 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,9 +1,10 @@ MPD_sima v0.14.0 + * Add priority feature for plugins + * More robust MPD client * Fixed top track mode * Fixed default conf file name (back to mpd_sima.cfg) * Cleanup code (removed users similarities) - * More robust MPD client -- kaliko jack UNRELEASED diff --git a/doc/examples/all_settings.cfg b/doc/examples/all_settings.cfg index 01c9100..4778e1a 100644 --- a/doc/examples/all_settings.cfg +++ b/doc/examples/all_settings.cfg @@ -65,7 +65,7 @@ verbosity = info # contrib = Scrobble, AwesomePlugin, # ExperimentalTest, AnotherTest # default: -# internal = "Crop, Lastfm, RandomFallBack" +# internal = "Crop, Lastfm, Random" # contrib = # description: Plugins list declaration. # Optional plugin's configuration lays in its own section. @@ -73,11 +73,11 @@ verbosity = info # gets its configuration from the corresponding section: # "[awesomeplugin]" # internal plugins will look for a section named after the lower-cased name -# of the pluglin, ie. RandomFallBack → randomfallback. +# of the pluglin, ie. Random → random. # # Two plugins sources are available, internal and contrib # -internal = Crop, Lastfm, RandomFallBack +internal = Crop, Lastfm, Random #contrib = ## HISTORY_DURATION @@ -118,12 +118,11 @@ musicbrainzid = True # Leave commented to keep all tracks #consume = 10 -[randomfallback] +[random] ## FLAVOUR # type: string # default: sensible -# description: When no similar tracks are found, falling back to random -# queuing. Different mode, aka random flavour, are available. +# description: Random queuing, different mode, aka random flavour, are available. # random flavour : # * pure: complete random choice among all tracks available in the # player media library @@ -132,7 +131,7 @@ musicbrainzid = True # chose among the same genre as current track (using genre # tag). If no genre tag is available "sensible" flavour # is used instead -#flavour=sensible +flavour=sensible ## TRACK_TO_ADD # type: integer diff --git a/sima/client.py b/sima/client.py index f64277e..926e295 100644 --- a/sima/client.py +++ b/sima/client.py @@ -336,7 +336,7 @@ class PlayerClient(Player): @property def artists(self): - return self._cache.get('artists') + return self._cache.get('artists') | self._cache.get('nombid_artists') @property def state(self): diff --git a/sima/core.py b/sima/core.py index 3532c88..264d97e 100644 --- a/sima/core.py +++ b/sima/core.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2009, 2010, 2011, 2013, 2014 Jack Kaliko +# Copyright (c) 2009, 2010, 2011, 2013, 2014, 2015 Jack Kaliko # # This file is part of sima # @@ -43,7 +43,8 @@ class Sima(Daemon): self.sdb = SimaDB(db_path=conf.get('sima', 'db_file')) PlayerClient.database = self.sdb self.log = getLogger('sima') - self.plugins = list() + self._plugins = list() + self._core_plugins = list() self.player = self.__get_player() # Player client self.short_history = deque(maxlen=60) @@ -55,19 +56,38 @@ class Sima(Daemon): return PlayerClient(host, port, pswd) def add_history(self): - """Handle local short history""" + """Handle local, in memory, short history""" self.short_history.appendleft(self.player.current) def register_plugin(self, plugin_class): """Registers plugin in Sima instance...""" - self.plugins.append(plugin_class(self)) + plgn = plugin_class(self) + prio = int(plgn.priority) + self._plugins.append((prio, plgn)) + + def register_core_plugin(self, plugin_class): + """Registers core plugins""" + plgn = plugin_class(self) + prio = int(plgn.priority) + self._core_plugins.append((prio, plgn)) def foreach_plugin(self, method, *args, **kwds): """Plugin's callbacks dispatcher""" + for plugin in self.core_plugins: + getattr(plugin, method)(*args, **kwds) for plugin in self.plugins: #self.log.debug('dispatching {0} to {1}'.format(method, plugin)) getattr(plugin, method)(*args, **kwds) + @property + def core_plugins(self): + return [plugin[1] for plugin in + sorted(self._core_plugins, key=lambda pl: pl[0], reverse=True)] + + @property + def plugins(self): + return [plugin[1] for plugin in sorted(self._plugins, key=lambda pl: pl[0], reverse=True)] + def need_tracks(self): """Is the player in need for tracks""" if not self.enabled: @@ -84,15 +104,12 @@ class Sima(Daemon): def queue(self): to_add = list() for plugin in self.plugins: - pl_callback = getattr(plugin, 'callback_need_track')() - if pl_callback: - to_add.extend(pl_callback) - if not to_add: - self.log.warning('Queue plugins returned nothing!') - for plugin in self.plugins: - pl_callback = getattr(plugin, 'callback_need_track_fb')() - if pl_callback: - to_add.extend(pl_callback) + self.log.info('running {}'.format(plugin)) + pl_candidates = getattr(plugin, 'callback_need_track')() + if pl_candidates: + to_add.extend(pl_candidates) + if to_add: + break for track in to_add: self.player.add(track) diff --git a/sima/launch.py b/sima/launch.py index 1ff6c94..c2dad9e 100644 --- a/sima/launch.py +++ b/sima/launch.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2013, 2014 Jack Kaliko +# Copyright (c) 2013, 2014, 2015 Jack Kaliko # # This file is part of sima # @@ -54,6 +54,7 @@ def load_plugins(sima, source): if not sima.config.get('sima', source): return logger = logging.getLogger('sima') + # TODO: Sanity check for "sima.config.get('sima', source)" ? for plugin in sima.config.get('sima', source).split(','): plugin = plugin.strip(' \n') module = 'sima.plugins.{0}.{1}'.format(source, plugin.lower()) @@ -71,7 +72,7 @@ def load_plugins(sima, source): sima.shutdown() sys.exit(1) logger.info('Loading {0} plugin: {name} ({doc})'.format( - source, **plugin_obj.info())) + source, **plugin_obj.info())) sima.register_plugin(plugin_obj) @@ -107,14 +108,15 @@ def start(sopt, restart=False): core_plugins = [History, MpdOptions, Uniq] for cplgn in core_plugins: logger.debug('Register core {name} ({doc})'.format(**cplgn.info())) - sima.register_plugin(cplgn) + sima.register_core_plugin(cplgn) + logger.debug('core loaded, prioriy: {}'.format(' > '.join(map(str, sima.core_plugins)))) # Loading internal plugins load_plugins(sima, 'internal') # Loading contrib plugins load_plugins(sima, 'contrib') - + logger.info('plugins loaded, prioriy: {}'.format(' > '.join(map(str, sima.plugins)))) # Set use of MusicBrainzIdentifier if not config.getboolean('sima', 'musicbrainzid'): logger.info('Disabling MusicBrainzIdentifier') diff --git a/sima/lib/plugin.py b/sima/lib/plugin.py index bb80a27..559e59f 100644 --- a/sima/lib/plugin.py +++ b/sima/lib/plugin.py @@ -21,6 +21,7 @@ Plugin object to derive from """ + class Plugin: """ First non-empty line of the docstring is used as description @@ -57,13 +58,21 @@ class Plugin: """Get plugin's specific configuration from global applications's config """ conf = self.__daemon.config - for sec in conf.sections(): + for sec in conf.sections(): # Discovering plugin conf if sec == self.__class__.__name__.lower(): self.plugin_conf = conf[sec] + if 'priority' not in self.plugin_conf: + self.plugin_conf['priority'] = '80' + if not self.plugin_conf: + self.plugin_conf = {'priority': '80'} #if self.plugin_conf: # self.log.debug('Got config for {0}: {1}'.format(self, # self.plugin_conf)) + @property + def priority(self): + return self.plugin_conf.get('priority') + def start(self): """ Called when the daemon().run() is called and diff --git a/sima/plugins/internal/randomfallback.py b/sima/plugins/internal/random.py similarity index 90% rename from sima/plugins/internal/randomfallback.py rename to sima/plugins/internal/random.py index ee0be20..f446b82 100644 --- a/sima/plugins/internal/randomfallback.py +++ b/sima/plugins/internal/random.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2013, 2014 Jack Kaliko +# Copyright (c) 2013, 2014, 2015 Jack Kaliko # # This file is part of sima # @@ -18,7 +18,7 @@ # # """ -Fetching similar artists from last.fm web services +Add random title """ # standard library import @@ -31,8 +31,8 @@ from ...lib.plugin import Plugin from ...lib.meta import Artist -class RandomFallBack(Plugin): - """Add random track as fallback +class Random(Plugin): + """Add random track TODO: refactor, this plugin does not look good to me. callback_need_track_fb/get_trk articulation is not elegant at all """ @@ -57,7 +57,7 @@ class RandomFallBack(Plugin): artists = [tr[-1] for tr in tracks_from_db] return set(artists) - def callback_need_track_fb(self): + def callback_need_track(self): trks = list() target = self.plugin_conf.getint('track_to_add') limit = 0 @@ -92,7 +92,7 @@ class RandomFallBack(Plugin): trks = self.player.find_track(Artist(art)) if trks: trk = random.choice(trks) - self.log.info('random fallback ({}): {}'.format(self.mode, trk)) + self.log.info('Random candidate ({}): {}'.format(self.mode, trk)) return trk diff --git a/sima/utils/config.py b/sima/utils/config.py index 0e775ec..d3bcbe2 100644 --- a/sima/utils/config.py +++ b/sima/utils/config.py @@ -72,6 +72,7 @@ DEFAULT_CONF = { 'track_to_add': 1, 'album_to_add': 1, 'depth': 1, + 'priority': 100, }, 'lastfm': { 'queue_mode': "track", #TODO control values @@ -81,11 +82,13 @@ DEFAULT_CONF = { 'album_to_add': 1, 'depth': 1, 'cache': True, + 'priority': 100, }, - 'randomfallback': { + 'random': { 'flavour': "sensible", # in pure, sensible 'track_to_add': 1, - } + 'priority': 50, + }, } # diff --git a/tests/test_plugin.py b/tests/test_plugin.py new file mode 100644 index 0000000..5c3a384 --- /dev/null +++ b/tests/test_plugin.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +import unittest +import configparser + +from unittest.mock import Mock + +import sima.lib.plugin + +class SomePlugin(sima.lib.plugin.Plugin): + + def __init__(self, daemon): + sima.lib.plugin.Plugin.__init__(self, daemon) + + +class TestFileAccessControl(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_plugin_conf_discovery(self): + config = configparser.ConfigParser() + default = {'priority': '42', 'option': 'value'} + config.read_dict({'someplugin': default}) + daemon = Mock(config=config) + plugin = SomePlugin(daemon) + self.assertEqual(dict(plugin.plugin_conf), default) + + def test_plugin_default_priority(self): + config = configparser.ConfigParser() + default = {'option': 'value'} + config.read_dict({'someplugin': default}) + daemon = Mock(config=config) + plugin = SomePlugin(daemon) + self.assertEqual(plugin.plugin_conf.get('priority'), '80') + self.assertEqual(plugin.plugin_conf.get('option'), default.get('option')) + + config = configparser.ConfigParser() + config.read_dict({}) + daemon = Mock(config=config) + plugin = SomePlugin(daemon) + self.assertEqual(plugin.plugin_conf.get('priority'), '80') + + +# VIM MODLINE +# vim: ai ts=4 sw=4 sts=4 expandtab -- 2.39.2