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

View file

@ -2,27 +2,26 @@ IHate Plugin
============
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.
There also is a whitelist to avoid skipping bands you still like. There are two
groups: warn and skip. The skip group is checked first. Whitelist overrides any
other patterns.
import or warn you about them. It supports any query, params will be checked
with OR chained logic, so as long as long as one element is satisfied the album
will be skipped.
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
your beets config. Then, add an ``ihate:`` section to your configuration file::
ihate:
# you will be warned about these suspicious genres/artists (regexps):
warn_genre: rnb soul power\smetal
warn_artist: bad\band another\sbad\sband
warn_album: tribute\sto
# if you don't like a genre in general, but accept some band playing it,
# add exceptions here:
warn_whitelist: hate\sexception
# you will be warned about these suspicious genres/artists:
warn:
- artist:rnb
- genre: soul
#only warn about tribute albums in rock genre
- genre:rock album:tribute
# never import any of this:
skip_genre: russian\srock polka
skip_artist: manowar
skip_album: christmas
# but import this:
skip_whitelist: ''
skip:
- genre:russian\srock
- genre:polka
- artist:manowar
- album:christmas
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):
def test_hate_album(self):
""" iHate tests for album """
def test_hate(self):
genre_p = []
artist_p = []
album_p = []
white_p = []
match_pattern = {}
testItem = Item(
genre='TestGenre',
album = u'TestAlbum',
artist = u'TestArtist')
task = ImportTask()
task.cur_artist = u'Test Artist'
task.cur_album = u'Test Album'
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.assertTrue(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 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))
task.items = [testItem]
task.is_album = False
#empty query should let it pass
self.assertFalse(IHatePlugin.do_i_hate_this(task, match_pattern))
#1 query match
match_pattern = ["artist:bad_artist","artist:TestArtist"]
self.assertTrue(IHatePlugin.do_i_hate_this(task, match_pattern))
#2 query matches, either should trigger
match_pattern = ["album:test","artist:testartist"]
self.assertTrue(IHatePlugin.do_i_hate_this(task, match_pattern))
#query is blocked by AND clause
match_pattern = ["album:notthis genre:testgenre"]
self.assertFalse(IHatePlugin.do_i_hate_this(task, match_pattern))
#both queries are blocked by AND clause with unmatched condition
match_pattern = ["album:notthis genre:testgenre","artist:testartist album:notthis"]
self.assertFalse(IHatePlugin.do_i_hate_this(task, match_pattern))
#only one query should fire
match_pattern = ["album:testalbum genre:testgenre","artist:testartist album:notthis"]
self.assertTrue(IHatePlugin.do_i_hate_this(task, match_pattern))
def suite():