updated iHate plugin to replace hardcoded regex system in favour of more flexible queries

changed config layout of iHate plugin to a simpler system
updated unit test for ihate plugin accordingly
updated docs for ihate plugin accordingly
This commit is contained in:
BrainDamage 2014-02-07 21:37:33 +01:00
parent c82b31e750
commit 6836e9c725
3 changed files with 62 additions and 148 deletions

View file

@ -18,10 +18,14 @@ import re
import logging import logging
from beets.plugins import BeetsPlugin from beets.plugins import BeetsPlugin
from beets.importer import action from beets.importer import action
from beets.dbcore.query import AndQuery
from beets.library import query_from_strings
from beets.library import Item
from beets.library import Album
__author__ = 'baobab@heresiarch.info' __author__ = 'baobab@heresiarch.info'
__version__ = '1.0' __version__ = '2.0'
class IHatePlugin(BeetsPlugin): class IHatePlugin(BeetsPlugin):
@ -29,88 +33,46 @@ class IHatePlugin(BeetsPlugin):
_instance = None _instance = None
_log = logging.getLogger('beets') _log = logging.getLogger('beets')
warn_genre = []
warn_artist = []
warn_album = []
warn_whitelist = []
skip_genre = []
skip_artist = []
skip_album = []
skip_whitelist = []
def __init__(self): def __init__(self):
super(IHatePlugin, self).__init__() super(IHatePlugin, self).__init__()
self.register_listener('import_task_choice', self.register_listener('import_task_choice',
self.import_task_choice_event) self.import_task_choice_event)
self.config.add({ self.config.add({
'warn_genre': [], 'warn': {},
'warn_artist': [], 'skip': {},
'warn_album': [],
'warn_whitelist': [],
'skip_genre': [],
'skip_artist': [],
'skip_album': [],
'skip_whitelist': [],
}) })
@classmethod @classmethod
def match_patterns(cls, s, patterns): def do_i_hate_this(cls, task, action_patterns):
"""Check if string is matching any of the patterns in the list."""
for p in patterns:
if re.findall(p, s, flags=re.IGNORECASE):
return True
return False
@classmethod
def do_i_hate_this(cls, task, genre_patterns, artist_patterns,
album_patterns, whitelist_patterns):
"""Process group of patterns (warn or skip) and returns True if """Process group of patterns (warn or skip) and returns True if
task is hated and not whitelisted. task is hated and not whitelisted.
""" """
hate = False if action_patterns:
try: for queryString in action_patterns:
genre = task.items[0].genre blockQuery = None
except: if task.is_album:
genre = u'' blockQuery = query_from_strings(AndQuery,Album,queryString)
if genre and genre_patterns: else:
if cls.match_patterns(genre, genre_patterns): blockQuery = query_from_strings(AndQuery,Item,queryString)
hate = True if any(blockQuery.match(item) for item in task.items):
if not hate and getattr(task, 'cur_album', None) and album_patterns: return True
if cls.match_patterns(task.cur_album, album_patterns): return False
hate = True
if not hate and getattr(task, 'cur_artist', None) and artist_patterns:
if cls.match_patterns(task.cur_artist, artist_patterns):
hate = True
if hate and whitelist_patterns:
if cls.match_patterns(task.cur_artist, whitelist_patterns):
hate = False
return hate
def job_to_do(self): def job_to_do(self):
"""Return True if at least one pattern is defined.""" """Return True if at least one pattern is defined."""
return any(self.config[l].as_str_seq() for l in return any(self.config[l].as_str_seq() for l in ('warn', 'skip'))
('warn_genre', 'warn_artist', 'warn_album',
'skip_genre', 'skip_artist', 'skip_album'))
def import_task_choice_event(self, session, task): def import_task_choice_event(self, session, task):
if task.choice_flag == action.APPLY: if task.choice_flag == action.APPLY:
if self.job_to_do(): if self.job_to_do():
self._log.debug('[ihate] processing your hate') self._log.debug('[ihate] processing your hate')
if self.do_i_hate_this(task, if self.do_i_hate_this(task, self.config['skip']):
self.config['skip_genre'].as_str_seq(),
self.config['skip_artist'].as_str_seq(),
self.config['skip_album'].as_str_seq(),
self.config['skip_whitelist'].as_str_seq()):
task.choice_flag = action.SKIP task.choice_flag = action.SKIP
self._log.info(u'[ihate] skipped: {0} - {1}' self._log.info(u'[ihate] skipped: {0} - {1}'
.format(task.cur_artist, task.cur_album)) .format(task.cur_artist, task.cur_album))
return return
if self.do_i_hate_this(task, if self.do_i_hate_this(task, self.config['warn']):
self.config['warn_genre'].as_str_seq(),
self.config['warn_artist'].as_str_seq(),
self.config['warn_album'].as_str_seq(),
self.config['warn_whitelist'].as_str_seq()):
self._log.info(u'[ihate] you maybe hate this: {0} - {1}' self._log.info(u'[ihate] you maybe hate this: {0} - {1}'
.format(task.cur_artist, task.cur_album)) .format(task.cur_artist, task.cur_album))
else: else:

View file

@ -2,27 +2,26 @@ IHate Plugin
============ ============
The ``ihate`` plugin allows you to automatically skip things you hate during The ``ihate`` plugin allows you to automatically skip things you hate during
import or warn you about them. It supports album, artist and genre patterns. import or warn you about them. It supports any query, params will be checked
There also is a whitelist to avoid skipping bands you still like. There are two with OR chained logic, so as long as long as one element is satisfied the album
groups: warn and skip. The skip group is checked first. Whitelist overrides any will be skipped.
other patterns. There are two groups: warn and skip. The skip group is checked first.
To use the plugin, enable it by including ``ihate`` in the ``plugins`` line of To use the plugin, enable it by including ``ihate`` in the ``plugins`` line of
your beets config. Then, add an ``ihate:`` section to your configuration file:: your beets config. Then, add an ``ihate:`` section to your configuration file::
ihate: ihate:
# you will be warned about these suspicious genres/artists (regexps): # you will be warned about these suspicious genres/artists:
warn_genre: rnb soul power\smetal warn:
warn_artist: bad\band another\sbad\sband - artist:rnb
warn_album: tribute\sto - genre: soul
# if you don't like a genre in general, but accept some band playing it, #only warn about tribute albums in rock genre
# add exceptions here: - genre:rock album:tribute
warn_whitelist: hate\sexception
# never import any of this: # never import any of this:
skip_genre: russian\srock polka skip:
skip_artist: manowar - genre:russian\srock
skip_album: christmas - genre:polka
# but import this: - artist:manowar
skip_whitelist: '' - album:christmas
Note: The plugin will trust your decision in 'as-is' mode. Note: The plugin will trust your decision in 'as-is' mode.

View file

@ -8,80 +8,33 @@ from beetsplug.ihate import IHatePlugin
class IHatePluginTest(unittest.TestCase): class IHatePluginTest(unittest.TestCase):
def test_hate_album(self): def test_hate(self):
""" iHate tests for album """
genre_p = [] match_pattern = {}
artist_p = [] testItem = Item(
album_p = [] genre='TestGenre',
white_p = [] album = u'TestAlbum',
artist = u'TestArtist')
task = ImportTask() task = ImportTask()
task.cur_artist = u'Test Artist' task.items = [testItem]
task.cur_album = u'Test Album' task.is_album = False
task.items = [Item(genre='Test Genre')] #empty query should let it pass
self.assertFalse(IHatePlugin.do_i_hate_this(task, genre_p, artist_p, self.assertFalse(IHatePlugin.do_i_hate_this(task, match_pattern))
album_p, white_p)) #1 query match
genre_p = 'some_genre test\sgenre'.split() match_pattern = ["artist:bad_artist","artist:TestArtist"]
self.assertTrue(IHatePlugin.do_i_hate_this(task, genre_p, artist_p, self.assertTrue(IHatePlugin.do_i_hate_this(task, match_pattern))
album_p, white_p)) #2 query matches, either should trigger
genre_p = [] match_pattern = ["album:test","artist:testartist"]
artist_p = 'bad_artist test\sartist' self.assertTrue(IHatePlugin.do_i_hate_this(task, match_pattern))
self.assertTrue(IHatePlugin.do_i_hate_this(task, genre_p, artist_p, #query is blocked by AND clause
album_p, white_p)) match_pattern = ["album:notthis genre:testgenre"]
artist_p = [] self.assertFalse(IHatePlugin.do_i_hate_this(task, match_pattern))
album_p = 'tribute christmas test'.split() #both queries are blocked by AND clause with unmatched condition
self.assertTrue(IHatePlugin.do_i_hate_this(task, genre_p, artist_p, match_pattern = ["album:notthis genre:testgenre","artist:testartist album:notthis"]
album_p, white_p)) self.assertFalse(IHatePlugin.do_i_hate_this(task, match_pattern))
album_p = [] #only one query should fire
white_p = 'goodband test\sartist another_band'.split() match_pattern = ["album:testalbum genre:testgenre","artist:testartist album:notthis"]
genre_p = 'some_genre test\sgenre'.split() self.assertTrue(IHatePlugin.do_i_hate_this(task, match_pattern))
self.assertFalse(IHatePlugin.do_i_hate_this(task, genre_p, artist_p,
album_p, white_p))
genre_p = []
artist_p = 'bad_artist test\sartist'
self.assertFalse(IHatePlugin.do_i_hate_this(task, genre_p, artist_p,
album_p, white_p))
artist_p = []
album_p = 'tribute christmas test'.split()
self.assertFalse(IHatePlugin.do_i_hate_this(task, genre_p, artist_p,
album_p, white_p))
def test_hate_singleton(self):
""" iHate tests for singleton """
genre_p = []
artist_p = []
album_p = []
white_p = []
task = ImportTask()
task.cur_artist = u'Test Artist'
task.items = [Item(genre='Test Genre')]
self.assertFalse(IHatePlugin.do_i_hate_this(task, genre_p, artist_p,
album_p, white_p))
genre_p = 'some_genre test\sgenre'.split()
self.assertTrue(IHatePlugin.do_i_hate_this(task, genre_p, artist_p,
album_p, white_p))
genre_p = []
artist_p = 'bad_artist test\sartist'
self.assertTrue(IHatePlugin.do_i_hate_this(task, genre_p, artist_p,
album_p, white_p))
artist_p = []
album_p = 'tribute christmas test'.split()
self.assertFalse(IHatePlugin.do_i_hate_this(task, genre_p, artist_p,
album_p, white_p))
album_p = []
white_p = 'goodband test\sartist another_band'.split()
genre_p = 'some_genre test\sgenre'.split()
self.assertFalse(IHatePlugin.do_i_hate_this(task, genre_p, artist_p,
album_p, white_p))
genre_p = []
artist_p = 'bad_artist test\sartist'
self.assertFalse(IHatePlugin.do_i_hate_this(task, genre_p, artist_p,
album_p, white_p))
artist_p = []
album_p = 'tribute christmas test'.split()
self.assertFalse(IHatePlugin.do_i_hate_this(task, genre_p, artist_p,
album_p, white_p))
def suite(): def suite():