From 23309f235879380a5ef7db3a51bda4d12e31902e Mon Sep 17 00:00:00 2001 From: kaliko Date: Thu, 5 Feb 2015 15:50:45 +0100 Subject: [PATCH 01/16] 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 From 604fff9480725be074699b0360121484b1875eaa Mon Sep 17 00:00:00 2001 From: kaliko Date: Sat, 7 Feb 2015 17:03:27 +0100 Subject: [PATCH 02/16] Improved documentation --- INSTALL | 17 +++++++++-------- setup.py | 2 +- vinstall.py | 0 3 files changed, 10 insertions(+), 9 deletions(-) mode change 100644 => 100755 vinstall.py diff --git a/INSTALL b/INSTALL index ac0b895..36b7864 100644 --- a/INSTALL +++ b/INSTALL @@ -1,23 +1,24 @@ -Design for python >= 3.2 +Design for python3 >= 3.2 Requires: python-musicpd >= 0.4.0 [0], requests >= 2.2.0 [1] - [0] http://media.kaliko.me/src/musicpd/ + [0] http://media.kaliko.me/src/musicpd/releases/ [1] http://docs.python-requests.org/ Virtualenv installation: -Run "python3 ./vinstall.py" to generate the python virtualenv and install -requirements (need at least python 3.3). +With python >= 3.3: + Run "python3 ./vinstall.py" to generate the python virtualenv and install + requirements (need at least python 3.3). -It will setup a virtualenv within a "venv" directory (same level as vinstall.py file). -It should also write a shell wrapper to run mpd-sima within the virtualenv. + It will setup a virtualenv within a "venv" directory (same level as vinstall.py file). + It should also write a shell wrapper to run mpd-sima within the virtualenv. - ./vmpd-sima --help + ./vmpd-sima --help -You might prefer manual installation: +For pyton < 3.3 and > 3.1 or if you prefer manual installation: # On your system may be "pyenv", "python3 -m venv" or "virtualenv" # Refer to the relevant manual to install a python env, 3.2 minimum. diff --git a/setup.py b/setup.py index c39047d..cf773c7 100755 --- a/setup.py +++ b/setup.py @@ -50,7 +50,7 @@ setup(name='MPD_sima', entry_points={ 'console_scripts': ['mpd-sima = sima.launch:main',] }, - test_suite="tests", + test_suite="tests", ) # VIM MODLINE diff --git a/vinstall.py b/vinstall.py old mode 100644 new mode 100755 -- 2.39.2 From 0bca6ee3dd5a5426c985e85e0e50fac77e4ec8f1 Mon Sep 17 00:00:00 2001 From: kaliko Date: Sun, 8 Feb 2015 16:30:37 +0100 Subject: [PATCH 03/16] Update manuals --- data/man/files.xml | 2 +- data/man/mpd-sima.1 | 8 +- data/man/mpd_sima.cfg.5 | 139 ++++++++++++++++++++++++---------- data/man/mpd_sima.cfg.5.xml | 132 ++++++++++++++++++++++---------- data/man/simadb_cli.1 | 6 +- doc/examples/all_settings.cfg | 12 +-- sima/utils/config.py | 3 +- 7 files changed, 207 insertions(+), 95 deletions(-) diff --git a/data/man/files.xml b/data/man/files.xml index 381b223..806d5de 100644 --- a/data/man/files.xml +++ b/data/man/files.xml @@ -25,7 +25,7 @@ ${HOME}/.local/share and XDG_CONFIG_HOME to ${HOME}/.config.You may override them using command line option (cf. - mpd_sima + mpd-sima 1) + + + + + + + Lastfm, Random, Crop + + &dhpackage;'s plugin management for internal source plugin + and contrib (ie. external plugins). Plugins list is a + comma separated string list. Optional plugin's + configuration lays in its own section.For instance a + "AwesomePlugin" declared here gets its configuration from the + corresponding section "[awesomeplugin]". + + The default list of plugins to load at startup: ,,. + is an utility plugin, it does not queue any tracks (cf. below). + will queue a track at random if other plugins did not return any tracks. + + You can add here as many plugins you want, + currently shipping and + only. + The priority may be used to order them. + + + 8 How far to look back in history to avoid to play twice the same track/title (duration in - hours). + hours). + The is also used to give priority to not recently played artists. + - 1 + 2 - This value triggers queue process if the queue - length is less than specified - queue_length. + Threshold value triggering queue process. true Use MusicBrainzIdentifier to search music (mainly - for artists). Consider using these metadata as it - enhances a lot artist/album/tracks identification. + for artists). Default is True, switch to False if you don't have MusicBrainzIdentifier set for at least 80% of you - music library. + music library. Consider using these metadata as it + enhances a lot artist/album/tracks identification. Use Picard to tag your file: . - &dhpackage;'s plugin management for internal source plugin - and contrib (ie. external plugins). Plugins list is a - comma separated string list. Optional plugin's - configuration lays in its own section.For instance a - "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 - plugin, ie. Random → random. - - - - Crop, Random, Lastfm - - 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 - with to try. - - - - - - - - @@ -258,6 +260,14 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ + + 10 + + + Plugin priority + + + Random section @@ -291,6 +301,14 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ + + 50 + + + Plugin priority + + + LastFm section @@ -363,6 +381,47 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ + + 100 + + + Plugin priority + + + + + + + EchoNest section + EchoNest plugin's configuration. + Options for EchoNest are exactly the same as LastFm (same + default as well), except for cache plugin which is always + needed to limit number of requests to the service. + + + + + + track + + + 10 + + + 1 + + + false + + + 1 + + + 1 + + + 100 + @@ -409,10 +468,7 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ and avoid album where this tag is set with "Various Artists". If a single track within an album is found with AlbumArtists:"Various Artists" the complete album - is skipped and won't be queued. - It is planned to allow users to set the values of - AlbumArtists tag triggering this behaviour. cf. - feature request #2085 on the tracker. + is skipped and won't be queued. diff --git a/data/man/simadb_cli.1 b/data/man/simadb_cli.1 index c2c5172..19a4fe9 100644 --- a/data/man/simadb_cli.1 +++ b/data/man/simadb_cli.1 @@ -2,12 +2,12 @@ .\" Title: simadb_cli .\" Author: Jack Kaliko .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 01/28/2015 -.\" Manual: mpd-sima 0.13.1 User Manual +.\" Date: 02/08/2015 +.\" Manual: mpd-sima 0.14.0 User Manual .\" Source: mpd-sima .\" Language: English .\" -.TH "SIMADB_CLI" "1" "01/28/2015" "mpd-sima" "mpd-sima 0.13.1 User Manual" +.TH "SIMADB_CLI" "1" "02/08/2015" "mpd-sima" "mpd-sima 0.14.0 User Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- diff --git a/doc/examples/all_settings.cfg b/doc/examples/all_settings.cfg index 4778e1a..ca8e9e6 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, Random" +# internal = "Lastfm, Random, Crop" # 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. Random → random. +# of the plugin, ie. AwesomePlugin → awesomeplugin. # # Two plugins sources are available, internal and contrib # -internal = Crop, Lastfm, Random +internal = Lastfm, Random, Crop #contrib = ## HISTORY_DURATION @@ -97,9 +97,9 @@ user_db = false ## QUEUE_LENGTH # type: integer -# default: 1 -# description: Queue length triggering tracks addition -queue_length = 1 +# default: 2 +# description: Queue length threshold triggering tracks addition +queue_length = 2 ## MUSICBRAINZID # type: boolean diff --git a/sima/utils/config.py b/sima/utils/config.py index d3bcbe2..f7ec982 100644 --- a/sima/utils/config.py +++ b/sima/utils/config.py @@ -46,7 +46,7 @@ DEFAULT_CONF = { 'port': 6600, }, 'sima': { - 'internal': "Crop, Lastfm, RandomFallBack", + 'internal': "Crop, Lastfm, Random", 'contrib': "", 'user_db': "false", 'history_duration': 8, @@ -64,6 +64,7 @@ DEFAULT_CONF = { }, 'crop': { 'consume': 10, + 'priority': 0, }, 'echonest': { 'queue_mode': "track", #TODO control values -- 2.39.2 From 64f927d489fa6f0edfe47e9c6105198c7f2a1c89 Mon Sep 17 00:00:00 2001 From: kaliko Date: Sun, 8 Feb 2015 17:19:17 +0100 Subject: [PATCH 04/16] Fixed a potential infinite loop --- sima/lib/webserv.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sima/lib/webserv.py b/sima/lib/webserv.py index 3a484c2..0a04ddb 100644 --- a/sima/lib/webserv.py +++ b/sima/lib/webserv.py @@ -168,7 +168,7 @@ class WebService(Plugin): results.append(res) return results - def ws_similar_artists(self, artist=None): + def ws_similar_artists(self, artist): """ Retrieve similar artists from WebServive. """ @@ -179,9 +179,13 @@ class WebService(Plugin): try: [as_art.append(art) for art in as_artists] except WSNotFound as err: - if artist.mbid: - return self.ws_similar_artists(Artist(name=artist.name)) self.log.warning('{}: {}'.format(self.ws.name, err)) + if artist.mbid: + self.log.debug('Trying without MusicBrainzID') + try: + return self.ws_similar_artists(Artist(name=artist.name)) + except WSNotFound as err: + self.log.debug('{}: {}'.format(self.ws.name, err)) except WSError as err: self.log.warning('{}: {}'.format(self.ws.name, err)) if as_art: @@ -264,9 +268,6 @@ class WebService(Plugin): if not ret: self.log.warning('Got nothing from music library.') return [] - # WARNING: - # * operation on set will not match against aliases - # * composite set w/ mbid set and whitout won't match either queued_artists = MetaContainer([trk.Artist for trk in self.player.queue]) if ret & queued_artists: self.log.debug('Removing already queued artists: ' -- 2.39.2 From 418a891bc69e8729f327e0c9fc38e45786ade65d Mon Sep 17 00:00:00 2001 From: kaliko Date: Sun, 8 Feb 2015 18:59:59 +0100 Subject: [PATCH 05/16] Fixed http caching for Lastfm. --- doc/Changelog | 1 + sima/lib/cache.py | 2 +- sima/plugins/internal/lastfm.py | 5 ++--- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index bbdbf17..2a1bbf0 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -4,6 +4,7 @@ MPD_sima v0.14.0 * More robust MPD client * Fixed top track mode * Fixed default conf file name (back to mpd_sima.cfg) + * Fixed http cache for lastFM * Cleanup code (removed users similarities) -- kaliko jack UNRELEASED diff --git a/sima/lib/cache.py b/sima/lib/cache.py index e16ca0e..9603c8b 100644 --- a/sima/lib/cache.py +++ b/sima/lib/cache.py @@ -73,7 +73,7 @@ class FileCache: self.forever = forever if not os.path.isdir(self.directory): - os.makedirs(self.directory) + os.makedirs(self.directory, mode=0o755) def encode(self, val): return md5(val.encode('utf-8')).hexdigest() diff --git a/sima/plugins/internal/lastfm.py b/sima/plugins/internal/lastfm.py index f6008dc..66ced04 100644 --- a/sima/plugins/internal/lastfm.py +++ b/sima/plugins/internal/lastfm.py @@ -38,14 +38,13 @@ class Lastfm(WebService): def __init__(self, daemon): WebService.__init__(self, daemon) - self.ws = SimaFM() # Set persitent cache vardir = daemon.config['sima']['var_dir'] persitent_cache = daemon.config.getboolean('lastfm', 'cache') if persitent_cache: + self.log.debug('Persistant cache enabled in {}'.format(join(vardir, 'http', 'LastFM'))) SimaFM.cache = FileCache(join(vardir, 'http', 'LastFM')) - else: - SimaFM.cache = DictCache() + self.ws = SimaFM() # VIM MODLINE -- 2.39.2 From 89a6fb2a516fc26e17771bda6213265dad6100dd Mon Sep 17 00:00:00 2001 From: kaliko Date: Sun, 8 Feb 2015 19:02:13 +0100 Subject: [PATCH 06/16] Update config example --- doc/examples/all_settings.cfg | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/examples/all_settings.cfg b/doc/examples/all_settings.cfg index ca8e9e6..65cc61f 100644 --- a/doc/examples/all_settings.cfg +++ b/doc/examples/all_settings.cfg @@ -67,6 +67,13 @@ verbosity = info # default: # internal = "Lastfm, Random, Crop" # contrib = +# +# Possible values for internal +# Lastfm : Queue using last.fm web service for suggestions. +# EchoNest : Queue using EchoNest web service for suggestions. +# Random : Queue a track at random (different flavour, cf. documentation) +# Crop : Remove old tracks from queue +# # description: Plugins list declaration. # Optional plugin's configuration lays in its own section. # For instance a "AwesomePlugin" declared here @@ -88,13 +95,6 @@ internal = Lastfm, Random, Crop # history_duration = 8 -## USER_DB # NOT IMPLEMENTED # -# type: boolean -# description: Load user database to find similar artists -# User DB is loaded from $XDG_CONFIG_HOME/mpd_sima/sima.db -# Use simadb_cli to edit/add entries. -user_db = false - ## QUEUE_LENGTH # type: integer # default: 2 @@ -140,7 +140,7 @@ flavour=sensible #track_to_add = 1 -# EchoNest or LastFM +# EchoNest or Lastfm #[echonest] [lastfm] ## QUEUE_MODE -- 2.39.2 From 0c911454ac7f8377003a5f0d90244aa3f06b2695 Mon Sep 17 00:00:00 2001 From: kaliko Date: Mon, 9 Feb 2015 14:52:57 +0100 Subject: [PATCH 07/16] Rework 6538573, actually fixes the issue --- sima/launch.py | 2 -- sima/lib/logger.py | 15 +++------------ sima/utils/utils.py | 4 ++-- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/sima/launch.py b/sima/launch.py index c2dad9e..2e2d1dd 100644 --- a/sima/launch.py +++ b/sima/launch.py @@ -81,8 +81,6 @@ def start(sopt, restart=False): """ # set logger verbosity = sopt.options.get('verbosity', 'warning') - logfile = sopt.options.get('logfile', None) - set_logger(verbosity, logfile) # loads configuration config = ConfMan(sopt.options).config logfile = config.get('log', 'logfile') diff --git a/sima/lib/logger.py b/sima/lib/logger.py index f156933..09d7a69 100644 --- a/sima/lib/logger.py +++ b/sima/lib/logger.py @@ -38,15 +38,6 @@ LOG_FORMATS = { DATE_FMT = "%Y-%m-%d %H:%M:%S" -class LevelFilter(logging.Filter): - """ - Enable logging between two log level by filtering everything < level. - """ - def filter(self, record): - """Defines loglevel""" - return record.levelno <= ERROR - - def set_logger(level='info', logfile=None): """ logger: @@ -88,10 +79,10 @@ def set_logger(level='info', logfile=None): return # create console handler with a specified log level (STDOUT) couth = logging.StreamHandler(sys.stdout) - couth.addFilter(LevelFilter()) + couth.addFilter(lambda record: record.levelno < ERROR) # create console handler with warning log level (STDERR) - cerrh = logging.StreamHandler() + cerrh = logging.StreamHandler(sys.stderr) cerrh.setLevel(ERROR) # add formatter to the handlers @@ -100,7 +91,7 @@ def set_logger(level='info', logfile=None): # add the handlers to SIMA_LOGGER logger.addHandler(couth) - #logger.addHandler(cerrh) # Already added creating the handler‽ Still have to figure it out. + logger.addHandler(cerrh) # Already added creating the handler‽ Still have to figure it out. # VIM MODLINE # vim: ai ts=4 sw=4 sts=4 expandtab diff --git a/sima/utils/utils.py b/sima/utils/utils.py index 2cb7c35..8a63f1b 100644 --- a/sima/utils/utils.py +++ b/sima/utils/utils.py @@ -22,6 +22,7 @@ """ # pylint: disable=C0111 +import logging import traceback import sys @@ -69,8 +70,7 @@ def normalize_path(path): def exception_log(): """Log unknown exceptions""" - import logging - log = logging.getLogger('sima') + log = logging.getLogger(__name__) log.error('Unhandled Exception!!!') log.error(''.join(traceback.format_exc())) log.info('Please report the previous message' -- 2.39.2 From 73405cb1d2bbeac4bdd956b9746a1e8a3b7355d7 Mon Sep 17 00:00:00 2001 From: kaliko Date: Wed, 11 Feb 2015 23:47:32 +0100 Subject: [PATCH 08/16] Fix artists cache initialising (thanks mathieui) --- sima/client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sima/client.py b/sima/client.py index 926e295..e171c0b 100644 --- a/sima/client.py +++ b/sima/client.py @@ -159,8 +159,8 @@ class PlayerClient(Player): else: self.log.info('Player: Initialising cache!') self._cache = { - 'artists': None, - 'nombid_artists': None, + 'artists': frozenset(), + 'nombid_artists': frozenset(), } self._cache['artists'] = frozenset(self._execute('list', ['artist'])) if Artist.use_mbid: @@ -336,7 +336,7 @@ class PlayerClient(Player): @property def artists(self): - return self._cache.get('artists') | self._cache.get('nombid_artists') + return self._cache.get('artists') @property def state(self): -- 2.39.2 From d86ba5f99e61ff81ab750aba226df8b8a78dcae5 Mon Sep 17 00:00:00 2001 From: kaliko Date: Wed, 11 Feb 2015 23:51:11 +0100 Subject: [PATCH 09/16] Add trace level to the logger --- sima/lib/logger.py | 16 +++++++++++++++- sima/lib/webserv.py | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/sima/lib/logger.py b/sima/lib/logger.py index 09d7a69..2079406 100644 --- a/sima/lib/logger.py +++ b/sima/lib/logger.py @@ -26,6 +26,8 @@ Logging facility for sima. import logging import sys +from os import environ + DEBUG = logging.DEBUG INFO = logging.INFO ERROR = logging.ERROR @@ -37,6 +39,15 @@ LOG_FORMATS = { } DATE_FMT = "%Y-%m-%d %H:%M:%S" +TRACE_LEVEL_NUM = 5 +logging.addLevelName(TRACE_LEVEL_NUM, 'TRACE') +def trace(self, message, *args, **kwargs): + # Yes, logger takes its '*args' as 'args'. + if self.isEnabledFor(TRACE_LEVEL_NUM): + self._log(TRACE_LEVEL_NUM, message, args, **kwargs) + +logging.Logger.trace = trace + def set_logger(level='info', logfile=None): """ @@ -46,7 +57,10 @@ def set_logger(level='info', logfile=None): """ name = 'sima' - user_log_level = getattr(logging, level.upper()) + if environ.get('TRACE', False): + user_log_level = TRACE_LEVEL_NUM + else: + user_log_level = getattr(logging, level.upper()) if user_log_level > DEBUG: log_format = LOG_FORMATS.get(INFO) else: diff --git a/sima/lib/webserv.py b/sima/lib/webserv.py index 0a04ddb..6a127f4 100644 --- a/sima/lib/webserv.py +++ b/sima/lib/webserv.py @@ -269,6 +269,8 @@ class WebService(Plugin): self.log.warning('Got nothing from music library.') return [] queued_artists = MetaContainer([trk.Artist for trk in self.player.queue]) + self.log.trace('Already queued: {}'.format(queued_artists)) + self.log.trace('Candidate: {}'.format(ret)) if ret & queued_artists: self.log.debug('Removing already queued artists: ' '{0}'.format('/'.join(map(str, ret & queued_artists)))) -- 2.39.2 From d71073a0cf4bae0da48270594eb5a0a33abff05f Mon Sep 17 00:00:00 2001 From: kaliko Date: Wed, 11 Feb 2015 23:51:52 +0100 Subject: [PATCH 10/16] Documentation improvements --- INSTALL | 4 ++-- doc/examples/album_mode.cfg | 2 +- vinstall.py | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/INSTALL b/INSTALL index 36b7864..1b02e69 100644 --- a/INSTALL +++ b/INSTALL @@ -1,4 +1,4 @@ -Design for python3 >= 3.2 +Designed for python3 >= 3.2 Requires: python-musicpd >= 0.4.0 [0], requests >= 2.2.0 [1] @@ -18,7 +18,7 @@ With python >= 3.3: ./vmpd-sima --help -For pyton < 3.3 and > 3.1 or if you prefer manual installation: +For python < 3.3 or if you prefer manual installation: # On your system may be "pyenv", "python3 -m venv" or "virtualenv" # Refer to the relevant manual to install a python env, 3.2 minimum. diff --git a/doc/examples/album_mode.cfg b/doc/examples/album_mode.cfg index 4d1c8cf..d6f8aec 100644 --- a/doc/examples/album_mode.cfg +++ b/doc/examples/album_mode.cfg @@ -16,7 +16,7 @@ verbosity = info ######################## SIMA CORE #################################### [sima] -history_duration = 24 # in hours +history_duration = 96 # 4 days in hours, get a larger history for album mode queue_length = 5 ######################### PLUGINS ##################################### diff --git a/vinstall.py b/vinstall.py index 5b63f5a..9e3f891 100755 --- a/vinstall.py +++ b/vinstall.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 # Copyright (C) 2013 Vinay Sajip. New BSD License. # Copyright (C) 2014 Kaliko Jack # -- 2.39.2 From fe1ed51724531b8c1e78e7d7eb590c9be20e275c Mon Sep 17 00:00:00 2001 From: kaliko Date: Thu, 12 Feb 2015 14:27:42 +0100 Subject: [PATCH 11/16] Add option to generate config on stdout --- data/man/mpd-sima.1 | 20 +++++++++++++------- data/man/mpd_sima.1.xml | 27 +++++++++++++++++---------- doc/Changelog | 1 + sima/client.py | 4 ++-- sima/launch.py | 6 +++++- sima/utils/config.py | 4 ++-- sima/utils/startopt.py | 7 +++++++ 7 files changed, 47 insertions(+), 22 deletions(-) diff --git a/data/man/mpd-sima.1 b/data/man/mpd-sima.1 index 2aa20a9..3e9f6be 100644 --- a/data/man/mpd-sima.1 +++ b/data/man/mpd-sima.1 @@ -2,12 +2,12 @@ .\" Title: mpd-sima .\" Author: Jack Kaliko .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 02/08/2015 +.\" Date: 02/12/2015 .\" Manual: mpd-sima 0.14.0 User Manual .\" Source: mpd-sima .\" Language: English .\" -.TH "MPD\-SIMA" "1" "02/08/2015" "mpd-sima" "mpd-sima 0.14.0 User Manual" +.TH "MPD\-SIMA" "1" "02/12/2015" "mpd-sima" "mpd-sima 0.14.0 User Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -33,9 +33,11 @@ mpd-sima \- mpd\-sima will try to maintain some titles ahead in the queue follow .HP \w'\fBmpd\-sima\fR\ 'u \fBmpd\-sima\fR [\fB\-\-daemon\fR] [\fB\-\-config=\fR\fIconf_file\fR] [\fB\-\-var_dir=\fR\fIvar_directory\fR] [\fB\-\-pid=\fR\fIpid_file\fR] [\fB\-\-log=\fR\fIlog_file\fR] [\fB\-\-log\-level=\fR\fIlog_level\fR] [\fB\-\-host=\fR\fImpd_host\fR] [\fB\-\-mpd_port=\fR\fImpd_port\fR] .HP \w'\fBmpd\-sima\fR\ 'u -\fBmpd\-sima\fR [\fB\-\-var_dir=\fR\fIvar_directory\fR] [\fB\-\-create\-db\fR] +\fBmpd\-sima\fR \-\-create\-db [\fB\-\-var_dir=\fR\fIvar_directory\fR] .HP \w'\fBmpd\-sima\fR\ 'u -\fBmpd\-sima\fR [{\fB\-h\fR\ |\ \fB\-\-help\fR} | \fB\-\-version\fR] +\fBmpd\-sima\fR \-\-generate\-config [\fB\&...\fR] +.HP \w'\fBmpd\-sima\fR\ 'u +\fBmpd\-sima\fR {{\fB\-h\fR\ |\ \fB\-\-help\fR}\ \fB\-\-version\fR} .SH "DESCRIPTION" .PP This manual page documents briefly the @@ -149,6 +151,11 @@ see also the section called \(lqFILES\(rq .RE .PP +\fB\-\-generate\-config\fR +.RS 4 +Generate a sample configuration file according to the current configuration\&. You can put other options with this one to get them in the generated configuration\&. +.RE +.PP \fB\-\-create\-db\fR .RS 4 Create the database and exit\&. Uses folder specified with @@ -258,10 +265,9 @@ DEFAULTS .RS 4 Default is to look for MPD server at localhost:6600 (or MPD_HOST/MPD_PORT env\&. var\&. if set)\&. .sp -The default behavior is to add one track, this track is to be chosen among titles from artists similar to the last artist in the queue\&. MPD_sima will add one track when the unplayed queue is one track long\&. +The get the defaults as detected by mpd\-sima on your system you can run mpd\-sima to print the config: .sp -To change these defaults, use the configuration file -mpd_sima\&.cfg +\fBmpd\-sima \-\-generate\-config\fR .RE .PP For details about mpd_sima\&.cfg refer to the manual diff --git a/data/man/mpd_sima.1.xml b/data/man/mpd_sima.1.xml index 48cf028..9a36ee5 100644 --- a/data/man/mpd_sima.1.xml +++ b/data/man/mpd_sima.1.xml @@ -73,21 +73,26 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ &dhpackage; + --create-db var_directory - + + + &dhpackage; + --generate-config + &dhpackage; - - + + + - @@ -190,6 +195,12 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ Use the specific path var_directory to look for (or create) var files (ie. database) instead of looking at the default user data location.Default is to look in $XDG_DATA_HOME/mpd_sima/. Concerning $XDG_DATA_HOME see also + + + + Generate a sample configuration file according to the current configuration. You can put other options with this one to get them in the generated configuration. + + @@ -250,12 +261,8 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ DEFAULTS Default is to look for MPD server at localhost:6600 (or MPD_HOST/MPD_PORT env. var. if set). - The default behavior is to add one track, this - track is to be chosen among titles from artists - similar to the last artist in the queue. MPD_sima - will add one track when the unplayed queue is one - track long. - To change these defaults, use the configuration file mpd_sima.cfg + The get the defaults as detected by &dhpackage; on your system you can run &dhpackage; to print the config: + &dhpackage; --generate-config diff --git a/doc/Changelog b/doc/Changelog index 2a1bbf0..87f6b04 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,5 +1,6 @@ MPD_sima v0.14.0 + * Add option to generate config on stdout * Add priority feature for plugins * More robust MPD client * Fixed top track mode diff --git a/sima/client.py b/sima/client.py index e171c0b..66894a7 100644 --- a/sima/client.py +++ b/sima/client.py @@ -206,9 +206,9 @@ class PlayerClient(Player): if artist.mbid: # we already performed a lookup on artists with mbid set # search through remaining artists - artists = self._cache.get('nombid_artists', []) + artists = self._cache.get('nombid_artists') else: - artists = self._cache.get('artists', []) + artists = self._cache.get('artists') match = get_close_matches(artist.name, artists, 50, 0.73) if not match and not found: return diff --git a/sima/launch.py b/sima/launch.py index 2e2d1dd..9fb0b2d 100644 --- a/sima/launch.py +++ b/sima/launch.py @@ -83,7 +83,7 @@ def start(sopt, restart=False): verbosity = sopt.options.get('verbosity', 'warning') # loads configuration config = ConfMan(sopt.options).config - logfile = config.get('log', 'logfile') + logfile = config.get('log', 'logfile', fallback=None) verbosity = config.get('log', 'verbosity') set_logger(verbosity, logfile) logger = logging.getLogger('sima') @@ -99,6 +99,10 @@ def start(sopt, restart=False): logger.info('Done, bye...') sys.exit(0) + if sopt.options.get('generate_config'): + config.write(sys.stdout, space_around_delimiters=True) + sys.exit(0) + logger.info('Starting...') sima = core.Sima(config) diff --git a/sima/utils/config.py b/sima/utils/config.py index f7ec982..168938a 100644 --- a/sima/utils/config.py +++ b/sima/utils/config.py @@ -137,8 +137,8 @@ class ConfMan(object): # CONFIG MANAGER CLASS argparse. """ ok = True - for op, ftochk in [('log', self.config['log']['logfile']), - ('pidfile', self.config['daemon']['pidfile']),]: + for op, ftochk in [('logfile', self.config.get('log','logfile')), + ('pidfile', self.config.get('daemon', 'pidfile')),]: if not ftochk: continue if isdir(ftochk): diff --git a/sima/utils/startopt.py b/sima/utils/startopt.py index 51319f6..2081c19 100644 --- a/sima/utils/startopt.py +++ b/sima/utils/startopt.py @@ -81,6 +81,13 @@ OPTS = [ 'dest': 'conf_file', 'action': Rfile, 'help': 'Configuration file to load'}, + { + 'sw':['--generate-config'], + 'dest': 'generate_config', + 'action': 'store_true', + 'help': 'Generate a sample configuration file to stdout according to the current\ + configuration. You can put other options with this one to get them in\ + the generated configuration.'}, { 'sw':['--var_dir'], 'dest': 'var_dir', -- 2.39.2 From e18be49d2739809a619626f8ddb51ce3f1838a19 Mon Sep 17 00:00:00 2001 From: kaliko Date: Sun, 15 Feb 2015 12:26:57 +0100 Subject: [PATCH 12/16] Some cleanup, CLI/bash completion/Doc improvements --- data/bash/completion.sh | 9 +++++---- doc/examples/all_settings.cfg | 3 +-- sima/launch.py | 5 ++--- sima/lib/logger.py | 1 - sima/utils/startopt.py | 6 +++--- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/data/bash/completion.sh b/data/bash/completion.sh index d1d6b64..0a8ff29 100644 --- a/data/bash/completion.sh +++ b/data/bash/completion.sh @@ -33,7 +33,8 @@ _sima() { -S --host \ -P --port \ -h --help --version \ - --var_dir \ + --var-dir --var_dir \ + --generate-config \ -d --daemon" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then @@ -54,11 +55,11 @@ _sima() { -c|--config) _filedir if [ -z $XDG_DATA_HOME ]; then - local confnames=$(for x in $(ls -1 $HOME/.config/mpd_sima/*.cfg 2>/dev/null) ; do echo "${x##*//}"; done) + local confnames=$(for x in $(ls -1 $HOME/.config/mpd_sima/*.cfg $PWD/*.cfg 2>/dev/null) ; do echo "${x##*//}"; done) else - local confnames=$(for x in $(ls -1 $HOME/.config/mpd_sima/*.cfg $XDG_DATA_HOME/mpd_sima/*.cfg 2>/dev/null) ; do echo "${x##*//}"; done) + local confnames=$(for x in $(ls -1 $HOME/.config/mpd_sima/*.cfg $XDG_DATA_HOME/mpd_sima/*.cfg $PWD/*.cfg 2>/dev/null) ; do echo "${x##*//}"; done) fi - COMPREPLY+=( $(compgen -W "${confnames}") ) + COMPREPLY+=( $(compgen -W "${confnames}" -- ${cur} ) ) return 0 ;; --host|-S) diff --git a/doc/examples/all_settings.cfg b/doc/examples/all_settings.cfg index 65cc61f..e8470be 100644 --- a/doc/examples/all_settings.cfg +++ b/doc/examples/all_settings.cfg @@ -112,10 +112,9 @@ musicbrainzid = True [crop] ## CONSUME # type: integer -# default: unset, not cropping playlist +# default: 10 # description: How many played tracks to keep in the playlist. # Allow to maintain a fixed length playlist. -# Leave commented to keep all tracks #consume = 10 [random] diff --git a/sima/launch.py b/sima/launch.py index 9fb0b2d..4cc60e3 100644 --- a/sima/launch.py +++ b/sima/launch.py @@ -79,14 +79,13 @@ def load_plugins(sima, source): def start(sopt, restart=False): """starts application """ - # set logger - verbosity = sopt.options.get('verbosity', 'warning') # loads configuration config = ConfMan(sopt.options).config + # set logger + logger = logging.getLogger('sima') logfile = config.get('log', 'logfile', fallback=None) verbosity = config.get('log', 'verbosity') set_logger(verbosity, logfile) - logger = logging.getLogger('sima') logger.debug('Command line say: {0}'.format(sopt.options)) # Create Database db_file = config.get('sima', 'db_file') diff --git a/sima/lib/logger.py b/sima/lib/logger.py index 2079406..61d2d25 100644 --- a/sima/lib/logger.py +++ b/sima/lib/logger.py @@ -54,7 +54,6 @@ def set_logger(level='info', logfile=None): logger: level: in debug, info, warning,… logfile: file to log to - """ name = 'sima' if environ.get('TRACE', False): diff --git a/sima/utils/startopt.py b/sima/utils/startopt.py index 2081c19..e393304 100644 --- a/sima/utils/startopt.py +++ b/sima/utils/startopt.py @@ -89,7 +89,7 @@ OPTS = [ configuration. You can put other options with this one to get them in\ the generated configuration.'}, { - 'sw':['--var_dir'], + 'sw':['--var-dir', '--var_dir'], 'dest': 'var_dir', 'action': Wdir, 'help': 'Directory to store var content (ie. database, cache)'}, @@ -106,14 +106,14 @@ OPTS = [ #'help': 'Queue mode in [track, top, album]', 'help': SUPPRESS, }, { - 'sw':['--purge_history'], + 'sw':['--purge-history'], 'action': 'store_true', 'dest': 'do_purge_history', 'help': SUPPRESS}, ] -class StartOpt(object): +class StartOpt: """Command line management. """ -- 2.39.2 From ea112d4630c7fc4e33d28d2ad77475a097e5f9a7 Mon Sep 17 00:00:00 2001 From: kaliko Date: Sun, 15 Feb 2015 12:53:09 +0100 Subject: [PATCH 13/16] crop: Allow negative value for consume to disable plugin --- data/man/mpd_sima.cfg.5 | 8 ++++---- data/man/mpd_sima.cfg.5.xml | 4 ++-- doc/examples/all_settings.cfg | 1 + sima/plugins/internal/crop.py | 9 +++++++-- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/data/man/mpd_sima.cfg.5 b/data/man/mpd_sima.cfg.5 index faf4003..cd7cf01 100644 --- a/data/man/mpd_sima.cfg.5 +++ b/data/man/mpd_sima.cfg.5 @@ -2,12 +2,12 @@ .\" Title: mpd_sima.cfg .\" Author: Jack Kaliko .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 02/08/2015 +.\" Date: 02/15/2015 .\" Manual: mpd-sima 0.14.0 User Manual .\" Source: mpd-sima .\" Language: English .\" -.TH "MPD_SIMA\&.CFG" "5" "02/08/2015" "mpd-sima" "mpd-sima 0.14.0 User Manual" +.TH "MPD_SIMA\&.CFG" "5" "02/15/2015" "mpd-sima" "mpd-sima 0.14.0 User Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -220,9 +220,9 @@ crop plugin\*(Aqs configuration: .RS 4 .RE .PP -\fBconsume=\fR\fI0\fR +\fBconsume=\fR\fI10\fR .RS 4 -How many played tracks to keep in the queue\&. Allows you to maintain a fixed length queue\&. Set to 0 to keep all played tracks\&. +How many played tracks to keep in the queue\&. Allows you to maintain a fixed length queue\&. Set to some negative integer to keep all played tracks\&. .RE .PP \fBpriority=\fR\fI10\fR diff --git a/data/man/mpd_sima.cfg.5.xml b/data/man/mpd_sima.cfg.5.xml index 71189d4..d097909 100644 --- a/data/man/mpd_sima.cfg.5.xml +++ b/data/man/mpd_sima.cfg.5.xml @@ -252,11 +252,11 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ - 0 + 10 How many played tracks to keep in the queue. Allows you to maintain a fixed length queue. - Set to 0 to keep all played tracks. + Set to some negative integer to keep all played tracks. diff --git a/doc/examples/all_settings.cfg b/doc/examples/all_settings.cfg index e8470be..4904c0e 100644 --- a/doc/examples/all_settings.cfg +++ b/doc/examples/all_settings.cfg @@ -115,6 +115,7 @@ musicbrainzid = True # default: 10 # description: How many played tracks to keep in the playlist. # Allow to maintain a fixed length playlist. +# Set a negative value to disable cropping (or remove plugin from sima/internal) #consume = 10 [random] diff --git a/sima/plugins/internal/crop.py b/sima/plugins/internal/crop.py index 1f8ab2c..fd0656c 100644 --- a/sima/plugins/internal/crop.py +++ b/sima/plugins/internal/crop.py @@ -38,14 +38,19 @@ class Crop(Plugin): self.target = None if not self.plugin_conf: return - target = self.plugin_conf.get('consume', None) + target = self.plugin_conf.get('consume') if not target: return - if not target.isdigit(): + try: + if int(target) < 0: + self.log.info('Negative value for consume, not cropping') + return + except ValueError: self.log.warning('Bad value for consume, ' 'expecting an integer, not "{}"'.format(target)) else: self.target = int(target) + self.log.debug('Cropping at 15') def callback_next_song(self): if not self.target: -- 2.39.2 From 113157e3ae7b1fd57654542030c3de9c03c4261a Mon Sep 17 00:00:00 2001 From: kaliko Date: Sat, 2 May 2015 15:30:10 +0200 Subject: [PATCH 14/16] Better explicit error msg on 'Failed to load plugin' --- sima/launch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sima/launch.py b/sima/launch.py index 4cc60e3..2f31491 100644 --- a/sima/launch.py +++ b/sima/launch.py @@ -61,7 +61,7 @@ def load_plugins(sima, source): try: mod_obj = __import__(module, fromlist=[plugin]) except ImportError as err: - logger.error('Failed to load plugin\'s module: ' + + logger.error('Failed to load "{}" plugin\'s module: '.format(plugin) + '{0} ({1})'.format(module, err)) sima.shutdown() sys.exit(1) -- 2.39.2 From 5a10f68f5d4b6bda63db74f205e6024d48c60e51 Mon Sep 17 00:00:00 2001 From: kaliko Date: Wed, 6 May 2015 17:23:33 +0200 Subject: [PATCH 15/16] Improved client's find_track method with mbids --- sima/client.py | 15 ++++++++------- sima/lib/webserv.py | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/sima/client.py b/sima/client.py index 66894a7..499250f 100644 --- a/sima/client.py +++ b/sima/client.py @@ -169,17 +169,18 @@ class PlayerClient(Player): @blacklist(track=True) def find_track(self, artist, title=None): 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)) + else: + tracks |= set(self.find('musicbrainz_artistid', artist.mbid)) + else: + for name in artist.names: + if title: + tracks |= set(self.find('artist', name, 'title', title)) + else: + tracks |= set(self.find('artist', name)) return list(tracks) @bl_artist diff --git a/sima/lib/webserv.py b/sima/lib/webserv.py index 6a127f4..dcc8707 100644 --- a/sima/lib/webserv.py +++ b/sima/lib/webserv.py @@ -368,7 +368,7 @@ class WebService(Plugin): artists = self.get_local_similar_artists() nbtracks_target = self.plugin_conf.getint('track_to_add') for artist in artists: - self.log.debug('Trying to find titles to add for "{}"'.format( + self.log.debug('Trying to find titles to add for "{!r}"'.format( artist)) found = self.player.find_track(artist) random.shuffle(found) -- 2.39.2 From ac0d934fc127484a0ed14386e4c4ecd4f915b6ca Mon Sep 17 00:00:00 2001 From: kaliko Date: Sun, 9 Aug 2015 14:56:46 +0200 Subject: [PATCH 16/16] Fixed bug in uniq plugin --- sima/core.py | 2 +- sima/plugins/core/uniq.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sima/core.py b/sima/core.py index 264d97e..60f6b2b 100644 --- a/sima/core.py +++ b/sima/core.py @@ -162,10 +162,10 @@ class Sima(Daemon): try: self.log.info('Connecting MPD: {0}:{1}'.format(*self.player._mpd)) self.player.connect() + self.foreach_plugin('start') except (PlayerError, PlayerUnHandledError) as err: self.log.warning('Player: {}'.format(err)) self.reconnect_player() - self.foreach_plugin('start') while 42: try: self.loop() diff --git a/sima/plugins/core/uniq.py b/sima/plugins/core/uniq.py index 1abdd62..0f42718 100644 --- a/sima/plugins/core/uniq.py +++ b/sima/plugins/core/uniq.py @@ -42,14 +42,15 @@ class Uniq(Plugin): Plugin.__init__(self, daemon) self.chan = 'mpd_sima:{0}.{1}'.format(getfqdn(), getpid()) self.channels = [] - self.uniq = True + self._registred = False def start(self): if not self.is_capable(): self.log.warning('MPD does not provide client to client') return self.is_uniq() - self.sub_chan() + if not self._registred: + self.sub_chan() def is_capable(self): if {'channels', 'subscribe'}.issubset(set(self.player.commands())): @@ -67,11 +68,11 @@ class Uniq(Plugin): if channels: self.log.warning('Another instance is queueing on this MPD host') self.log.warning(' '.join(channels)) - self.uniq = False def sub_chan(self): self.log.debug('Registering as {}'.format(self.chan)) self.player.subscribe(self.chan) + self._registred = True def callback_need_track(self): if self.is_capable(): -- 2.39.2