Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add manually triggered mode for zero plugin #2329

Merged
merged 14 commits into from
Dec 26, 2016
37 changes: 35 additions & 2 deletions beetsplug/zero.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from beets.plugins import BeetsPlugin
from beets.mediafile import MediaFile
from beets.importer import action
from beets.ui import Subcommand, decargs, input_yn
from beets.util import confit

__author__ = '[email protected]'
Expand All @@ -39,6 +40,7 @@ def __init__(self):
self.import_task_choice_event)

self.config.add({
'auto': True,
'fields': [],
'keep_fields': [],
'update_database': False,
Expand All @@ -63,6 +65,20 @@ def __init__(self):
field not in ('id', 'path', 'album_id')):
self._set_pattern(field)

def commands(self):
zero_command = Subcommand('zero', help='set fields to null')

def zero_fields(lib, opts, args):
if not decargs(args) and not input_yn(
u"Remove fields for all items? (Y/n)",
True):
return
for item in lib.items(decargs(args)):
self.process_item(item)

zero_command.func = zero_fields
return [zero_command]

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
Expand Down Expand Up @@ -93,24 +109,41 @@ def write_event(self, item, path, tags):
"""Set values in tags to `None` if the key and value are matched
by `self.patterns`.
"""
if self.config['auto']:
self.set_fields(item, tags)

def set_fields(self, item, tags):
fields_set = False

if not self.fields_to_progs:
self._log.warning(u'no fields, nothing to do')
return
return False

for field, progs in self.fields_to_progs.items():
if field in tags:
value = tags[field]
match = _match_progs(value, progs, self._log)
match = _match_progs(tags[field], progs, self._log)
else:
value = ''
match = not progs

if match:
fields_set = True
self._log.debug(u'{0}: {1} -> None', field, value)
tags[field] = None
if self.config['update_database']:
item[field] = None

return fields_set

def process_item(self, item):
tags = dict(item)

if self.set_fields(item, tags):
item.write(tags=tags)
if self.config['update_database']:
item.store(fields=tags)


def _match_progs(value, progs, log):
"""Check if field (as string) is matching any of the patterns in
Expand Down
4 changes: 4 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ Fixes:

* :doc:`/plugins/bpd`: Fix a crash on non-ASCII MPD commands. :bug:`2332`

Features:

* :doc:`/plugins/zero`: Added ``zero`` command to manually trigger the zero
plugin.

1.4.2 (December 16, 2016)
-------------------------
Expand Down
7 changes: 7 additions & 0 deletions docs/plugins/zero.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Configuration
Make a ``zero:`` section in your configuration file. You can specify the
fields to nullify and the conditions for nullifying them:

* Set ``auto`` to ``yes`` to null fields automatically on import. Default ``yes``
* Set ``fields`` to a whitespace-separated list of fields to change. You can
get the list of all available fields by running ``beet fields``. In
addition, the ``images`` field allows you to remove any images
Expand All @@ -42,3 +43,9 @@ If a custom pattern is not defined for a given field, the field will be nulled
unconditionally.

Note that the plugin currently does not zero fields when importing "as-is".

Manually Triggering Zero
------------------------

The ``zero`` command will invoke the zero plugin on items matching a query. Use the
command ``beet zero [QUERY]``
209 changes: 206 additions & 3 deletions test/test_zero.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from __future__ import division, absolute_import, print_function

import unittest
from test.helper import TestHelper
from test.helper import TestHelper, control_stdin

from beets.library import Item
from beetsplug.zero import ZeroPlugin
Expand Down Expand Up @@ -105,8 +105,211 @@ def test_album_art(self):
self.load_plugins('zero')
item.write()

mediafile = MediaFile(syspath(path))
self.assertEqual(0, len(mediafile.images))
mf = MediaFile(syspath(path))
self.assertEqual(0, len(mf.images))

def test_auto_false(self):
self.config['zero']['fields'] = ['year']
self.config['zero']['update_database'] = True
self.config['zero']['auto'] = False

item = self.add_item_fixture(year=2000)
item.write()

self.load_plugins('zero')
item.write()

self.assertEqual(item['year'], 2000)

def test_subcommand_update_database_true(self):
item = self.add_item_fixture(
year=2016,
day=13,
month=3,
comments=u'test comment'
)
item.write()
item_id = item.id
self.config['zero']['fields'] = ['comments']
self.config['zero']['update_database'] = True
self.config['zero']['auto'] = False

self.load_plugins('zero')
with control_stdin('y'):
self.run_command('zero')

mf = MediaFile(syspath(item.path))
item = self.lib.get_item(item_id)

self.assertEqual(item['year'], 2016)
self.assertEqual(mf.year, 2016)
self.assertEqual(mf.comments, None)
self.assertEqual(item['comments'], u'')

def test_subcommand_update_database_false(self):
item = self.add_item_fixture(
year=2016,
day=13,
month=3,
comments=u'test comment'
)
item.write()
item_id = item.id

self.config['zero']['fields'] = ['comments']
self.config['zero']['update_database'] = False
self.config['zero']['auto'] = False

self.load_plugins('zero')
with control_stdin('y'):
self.run_command('zero')

mf = MediaFile(syspath(item.path))
item = self.lib.get_item(item_id)

self.assertEqual(item['year'], 2016)
self.assertEqual(mf.year, 2016)
self.assertEqual(item['comments'], u'test comment')
self.assertEqual(mf.comments, None)

def test_subcommand_query_include(self):
item = self.add_item_fixture(
year=2016,
day=13,
month=3,
comments=u'test comment'
)

item.write()

self.config['zero']['fields'] = ['comments']
self.config['zero']['update_database'] = False
self.config['zero']['auto'] = False

self.load_plugins('zero')
self.run_command('zero', 'year: 2016')

mf = MediaFile(syspath(item.path))

self.assertEqual(mf.year, 2016)
self.assertEqual(mf.comments, None)

def test_subcommand_query_exclude(self):
item = self.add_item_fixture(
year=2016,
day=13,
month=3,
comments=u'test comment'
)

item.write()

self.config['zero']['fields'] = ['comments']
self.config['zero']['update_database'] = False
self.config['zero']['auto'] = False

self.load_plugins('zero')
self.run_command('zero', 'year: 0000')

mf = MediaFile(syspath(item.path))

self.assertEqual(mf.year, 2016)
self.assertEqual(mf.comments, u'test comment')

def test_no_fields(self):
item = self.add_item_fixture(year=2016)
item.write()
mediafile = MediaFile(syspath(item.path))
self.assertEqual(mediafile.year, 2016)

item_id = item.id

self.load_plugins('zero')
with control_stdin('y'):
self.run_command('zero')

item = self.lib.get_item(item_id)

self.assertEqual(item['year'], 2016)
self.assertEqual(mediafile.year, 2016)

def test_whitelist_and_blacklist(self):
item = self.add_item_fixture(year=2016)
item.write()
mf = MediaFile(syspath(item.path))
self.assertEqual(mf.year, 2016)

item_id = item.id
self.config['zero']['fields'] = [u'year']
self.config['zero']['keep_fields'] = [u'comments']

self.load_plugins('zero')
with control_stdin('y'):
self.run_command('zero')

item = self.lib.get_item(item_id)

self.assertEqual(item['year'], 2016)
self.assertEqual(mf.year, 2016)

def test_keep_fields(self):
item = self.add_item_fixture(year=2016, comments=u'test comment')
self.config['zero']['keep_fields'] = [u'year']
self.config['zero']['fields'] = None
self.config['zero']['update_database'] = True

tags = {
'comments': u'test comment',
'year': 2016,
}
self.load_plugins('zero')

z = ZeroPlugin()
z.write_event(item, item.path, tags)
self.assertEqual(tags['comments'], None)
self.assertEqual(tags['year'], 2016)

def test_keep_fields_removes_preserved_tags(self):
self.config['zero']['keep_fields'] = [u'year']
self.config['zero']['fields'] = None
self.config['zero']['update_database'] = True

z = ZeroPlugin()

self.assertNotIn('id', z.fields_to_progs)

def test_fields_removes_preserved_tags(self):
self.config['zero']['fields'] = [u'year id']
self.config['zero']['update_database'] = True

z = ZeroPlugin()

self.assertNotIn('id', z.fields_to_progs)

def test_empty_query_n_response_no_changes(self):
item = self.add_item_fixture(
year=2016,
day=13,
month=3,
comments=u'test comment'
)
item.write()
item_id = item.id
self.config['zero']['fields'] = ['comments']
self.config['zero']['update_database'] = True
self.config['zero']['auto'] = False

self.load_plugins('zero')
with control_stdin('n'):
self.run_command('zero')

mf = MediaFile(syspath(item.path))
item = self.lib.get_item(item_id)

self.assertEqual(item['year'], 2016)
self.assertEqual(mf.year, 2016)
self.assertEqual(mf.comments, u'test comment')
self.assertEqual(item['comments'], u'test comment')


def suite():
Expand Down