# -*- coding: utf-8 -*-
-# Copyright (c) 2020 kaliko <kaliko@azylum.org>
+# Copyright (c) 2020, 2021 kaliko <kaliko@azylum.org>
#
# This file is part of sima
#
"""
# standard library import
+import logging
import random
# third parties components
from ...utils.utils import PluginException
-def forge_filter(cfg):
+def control_config(tags_config):
+ log = logging.getLogger('sima')
+ sup_tags = Tags.supported_tags
+ config_tags = {k for k, v in tags_config.items()
+ if (v and k in Tags.supported_tags)}
+ if not tags_config.get('filter', None) and \
+ config_tags.isdisjoint(sup_tags):
+ log.warning('Found no config for Tags plugin! '
+ 'Need at least "filter" or a supported tag')
+ log.info('Supported Tags are : %s', ', '.join(sup_tags))
+ return False
+ if config_tags.difference(sup_tags):
+ log.error('Found unsupported tag in config: %s',
+ config_tags.difference(sup_tags))
+ return False
+ return True
+
+
+def forge_filter(cfg, logger):
+ """forge_filter merges tags config and user defined MPD filter into a single
+ MPD filter"""
tags = set(cfg.keys()) & Tags.supported_tags
cfg_filter = cfg.get('filter', None)
+ # Remove external enclosing parentheses in user defined MPD filter, for
+ # instance when there is more than one expression:
+ # ((genre == 'rock' ) AND (date =~ '198.'))
+ # Even though it's a valid MPD filter, forge_filter will enclose it
+ # properly. We do not want to through a syntax error at users since it's a
+ # valid MPD filter, hence trying to transparently reformat the filter
+ if cfg_filter.startswith('((') and cfg_filter.endswith('))'):
+ logger.debug('Drop external enclosing parentheses in user filter: %s',
+ cfg_filter[1:-1])
+ cfg['filter'] = cfg_filter[1:-1]
+ cfg_filter = cfg['filter']
mpd_filter = []
if cfg_filter:
mpd_filter.append(cfg_filter)
"""Add track based on tags content
"""
supported_tags = {'comment', 'date', 'genre', 'label', 'originaldate'}
- options = {'queue_mode', 'priority', 'filter', 'track_to_add', 'album_to_add'}
+ # options = {'queue_mode', 'priority', 'filter', 'track_to_add',
+ # 'album_to_add'}
def __init__(self, daemon):
super().__init__(daemon)
self._control_conf()
- self.mpd_filter = forge_filter(self.plugin_conf)
+ self.mpd_filter = forge_filter(self.plugin_conf, self.log)
self._setup_tagsneeded()
self.log.debug('mpd filter: %s', self.mpd_filter)
def _control_conf(self):
- sup_tags = Tags.supported_tags
- config_tags = {k for k, v in self.plugin_conf.items()
- if (v and k not in Tags.options)}
- if not self.plugin_conf.get('filter', None) and \
- config_tags.isdisjoint(sup_tags):
- self.log.error('Found no config for %s plugin! '
- 'Need at least "filter" or a supported tag', self)
- self.log.info('Supported Tags are : %s', ', '.join(sup_tags))
- raise PluginException('plugin misconfiguration')
- if config_tags.difference(sup_tags):
- self.log.error('Found unsupported tag in config: %s',
- config_tags.difference(sup_tags))
+ if not control_config(self.plugin_conf):
raise PluginException('plugin misconfiguration')
def _setup_tagsneeded(self):
'Need at least MPD 0.21 to use Tags plugin (filters required)')
self.player.disconnect()
raise PluginException('MPD >= 0.21 required')
+ if not self.plugin_conf['filter']:
+ return
# Check filter is valid
try:
- if self.plugin_conf['filter']:
- # Use window to limit response size
- self.player.find(self.plugin_conf['filter'], "window", (0, 1))
- except CommandError:
+ # Use window to limit response size
+ self.player.find(self.mpd_filter, "window", (0, 1))
+ except CommandError as err:
+ self.log.warning(err)
raise PluginException('Badly formated filter in tags plugin configuration: "%s"'
- % self.plugin_conf['filter'])
+ % self.plugin_conf['filter']) from err
def callback_need_track(self):
candidates = []
queue_mode = self.plugin_conf.get('queue_mode', 'track')
target = self.plugin_conf.getint(f'{queue_mode}_to_add')
# look for artists acording to filter
- artists = MetaContainer([Artist(name=a) for a in self.player.list('artist', self.mpd_filter)])
+ artists = [Artist(name=a) for a in self.player.list('artist', self.mpd_filter)]
random.shuffle(artists)
+ artists = MetaContainer(artists)
if not artists:
self.log.info('Tags plugin found nothing to queue')
return candidates
for artist in artists:
self.log.debug('looking for %s', artist)
tracks = self.player.find_tracks(artist)
- trk = self.filter_track(tracks)
+ if not tracks:
+ continue
+ trk = self.filter_track(tracks, candidates)
if not trk:
continue
if queue_mode == 'track':
- self.log.info('Tags plugin chose: {}'.format(trk))
+ self.log.info('Tags plugin chose: %s', trk)
candidates.append(trk)
if len(candidates) == target:
break