diff --git a/beetsplug/zero.py b/beetsplug/zero.py index 1401b11de..5659207fe 100644 --- a/beetsplug/zero.py +++ b/beetsplug/zero.py @@ -16,22 +16,20 @@ """ Clears tag fields in media files.""" from __future__ import division, absolute_import, print_function +import six import re + from beets.plugins import BeetsPlugin from beets.mediafile import MediaFile from beets.importer import action from beets.util import confit -import six __author__ = 'baobab@heresiarch.info' __version__ = '0.10' class ZeroPlugin(BeetsPlugin): - - _instance = None - def __init__(self): super(ZeroPlugin, self).__init__() @@ -46,60 +44,43 @@ class ZeroPlugin(BeetsPlugin): 'update_database': False, }) - self.patterns = {} + self.fields_to_progs = {} self.warned = False - # We'll only handle `fields` or `keep_fields`, but not both. if self.config['fields'] and self.config['keep_fields']: self._log.warning( u'cannot blacklist and whitelist at the same time' ) - # Blacklist mode. - if self.config['fields']: - self.validate_config('fields') + elif self.config['fields']: for field in self.config['fields'].as_str_seq(): - self.set_pattern(field) - + self._set_pattern(field) # Whitelist mode. elif self.config['keep_fields']: - self.validate_config('keep_fields') - for field in MediaFile.fields(): - if field in self.config['keep_fields'].as_str_seq(): - continue - self.set_pattern(field) + if (field not in self.config['keep_fields'].as_str_seq() and + # These fields should always be preserved. + field not in ('id', 'path', 'album_id')): + self._set_pattern(field) - # These fields should always be preserved. - for key in ('id', 'path', 'album_id'): - if key in self.patterns: - del self.patterns[key] - - def validate_config(self, mode): - """Check whether fields in the configuration are valid. - - `mode` should either be "fields" or "keep_fields", indicating - the section of the configuration to validate. - """ - for field in self.config[mode].as_str_seq(): - if field not in MediaFile.fields(): - self._log.error(u'invalid field: {0}', field) - continue - if mode == 'fields' and field in ('id', 'path', 'album_id'): - self._log.warning(u'field \'{0}\' ignored, zeroing ' - u'it would be dangerous', field) - continue - - def set_pattern(self, field): + def _set_pattern(self, field): """Set a field in `self.patterns` to a string list corresponding to the configuration, or `True` if the field has no specific configuration. """ - try: - self.patterns[field] = self.config[field].as_str_seq() - except confit.NotFoundError: - # Matches everything - self.patterns[field] = True + if field not in MediaFile.fields(): + self._log.error(u'invalid field: {0}', field) + elif field in ('id', 'path', 'album_id'): + self._log.warning(u'field \'{0}\' ignored, zeroing ' + u'it would be dangerous', field) + else: + try: + for pattern in self.config[field].as_str_seq(): + prog = re.compile(pattern, re.IGNORECASE) + self.fields_to_progs.setdefault(field, []).append(prog) + except confit.NotFoundError: + # Matches everything + self.fields_to_progs[field] = [] def import_task_choice_event(self, session, task): """Listen for import_task_choice event.""" @@ -108,36 +89,36 @@ class ZeroPlugin(BeetsPlugin): self.warned = True # TODO request write in as-is mode - @classmethod - def match_patterns(cls, field, patterns): - """Check if field (as string) is matching any of the patterns in - the list. - """ - if patterns is True: - return True - for p in patterns: - if re.search(p, six.text_type(field), flags=re.IGNORECASE): - return True - return False - def write_event(self, item, path, tags): """Set values in tags to `None` if the key and value are matched by `self.patterns`. """ - if not self.patterns: + if not self.fields_to_progs: self._log.warning(u'no fields, nothing to do') return - for field, patterns in self.patterns.items(): + for field, progs in self.fields_to_progs.items(): if field in tags: value = tags[field] - match = self.match_patterns(tags[field], patterns) + match = _match_progs(value, progs, self._log) else: value = '' - match = patterns is True + match = not progs if match: self._log.debug(u'{0}: {1} -> None', field, value) tags[field] = None if self.config['update_database']: item[field] = None + + +def _match_progs(value, progs, log): + """Check if field (as string) is matching any of the patterns in + the list. + """ + if not progs: + return True + for prog in progs: + if prog.search(six.text_type(value)): + return True + return False diff --git a/test/test_zero.py b/test/test_zero.py index fb28330ee..1223e5c9e 100644 --- a/test/test_zero.py +++ b/test/test_zero.py @@ -8,7 +8,6 @@ import unittest from test.helper import TestHelper from beets.library import Item -from beets import config from beetsplug.zero import ZeroPlugin from beets.mediafile import MediaFile from beets.util import syspath @@ -17,108 +16,96 @@ from beets.util import syspath class ZeroPluginTest(unittest.TestCase, TestHelper): def setUp(self): self.setup_beets() + self.config['zero'] = { + 'fields': [], + 'keep_fields': [], + 'update_database': False, + } def tearDown(self): + ZeroPlugin.listeners = None self.teardown_beets() self.unload_plugins() def test_no_patterns(self): - tags = { - 'comments': u'test comment', - 'day': 13, - 'month': 3, - 'year': 2012, - } - z = ZeroPlugin() - z.debug = False - z.fields = ['comments', 'month', 'day'] - z.patterns = {'comments': [u'.'], - 'month': [u'.'], - 'day': [u'.']} - z.write_event(None, None, tags) - self.assertEqual(tags['comments'], None) - self.assertEqual(tags['day'], None) - self.assertEqual(tags['month'], None) - self.assertEqual(tags['year'], 2012) + self.config['zero']['fields'] = ['comments', 'month'] - def test_patterns(self): - z = ZeroPlugin() - z.debug = False - z.fields = ['comments', 'year'] - z.patterns = {'comments': u'eac lame'.split(), - 'year': u'2098 2099'.split()} - - tags = { - 'comments': u'from lame collection, ripped by eac', - 'year': 2012, - } - z.write_event(None, None, tags) - self.assertEqual(tags['comments'], None) - self.assertEqual(tags['year'], 2012) - - def test_delete_replaygain_tag(self): - path = self.create_mediafile_fixture() - item = Item.from_path(path) - item.rg_track_peak = 0.0 + item = self.add_item_fixture( + comments=u'test comment', + title=u'Title', + month=1, + year=2000, + ) item.write() - mediafile = MediaFile(syspath(item.path)) - self.assertIsNotNone(mediafile.rg_track_peak) - self.assertIsNotNone(mediafile.rg_track_gain) - - config['zero'] = { - 'fields': ['rg_track_peak', 'rg_track_gain'], - } self.load_plugins('zero') - item.write() - mediafile = MediaFile(syspath(item.path)) - self.assertIsNone(mediafile.rg_track_peak) - self.assertIsNone(mediafile.rg_track_gain) + + mf = MediaFile(syspath(item.path)) + self.assertIsNone(mf.comments) + self.assertIsNone(mf.month) + self.assertEqual(mf.title, u'Title') + self.assertEqual(mf.year, 2000) + + def test_pattern_match(self): + self.config['zero']['fields'] = ['comments'] + self.config['zero']['comments'] = [u'encoded by'] + + item = self.add_item_fixture(comments=u'encoded by encoder') + item.write() + + self.load_plugins('zero') + item.write() + + mf = MediaFile(syspath(item.path)) + self.assertIsNone(mf.comments) + + def test_pattern_nomatch(self): + self.config['zero']['fields'] = ['comments'] + self.config['zero']['comments'] = [u'encoded by'] + + item = self.add_item_fixture(comments=u'recorded at place') + item.write() + + self.load_plugins('zero') + item.write() + + mf = MediaFile(syspath(item.path)) + self.assertEqual(mf.comments, u'recorded at place') def test_do_not_change_database(self): + self.config['zero']['fields'] = ['year'] + item = self.add_item_fixture(year=2000) item.write() - mediafile = MediaFile(syspath(item.path)) - self.assertEqual(2000, mediafile.year) - config['zero'] = {'fields': ['year']} self.load_plugins('zero') - item.write() - mediafile = MediaFile(syspath(item.path)) + self.assertEqual(item['year'], 2000) - self.assertIsNone(mediafile.year) def test_change_database(self): + self.config['zero']['fields'] = ['year'] + self.config['zero']['update_database'] = True + item = self.add_item_fixture(year=2000) item.write() - mediafile = MediaFile(syspath(item.path)) - self.assertEqual(2000, mediafile.year) - config['zero'] = { - 'fields': [u'year'], - 'update_database': True, - } self.load_plugins('zero') - item.write() - mediafile = MediaFile(syspath(item.path)) + self.assertEqual(item['year'], 0) - self.assertIsNone(mediafile.year) def test_album_art(self): + self.config['zero']['fields'] = ['images'] + path = self.create_mediafile_fixture(images=['jpg']) item = Item.from_path(path) - mediafile = MediaFile(syspath(item.path)) - self.assertNotEqual(0, len(mediafile.images)) - - config['zero'] = {'fields': [u'images']} self.load_plugins('zero') - item.write() - mediafile = MediaFile(syspath(item.path)) + + mediafile = MediaFile(syspath(path)) self.assertEqual(0, len(mediafile.images))