diff --git a/beets/ui/commands.py b/beets/ui/commands.py index d0ff320a3..e7e631a49 100644 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -1082,7 +1082,7 @@ default_commands.append(version_cmd) # modify: Declaratively change metadata. -def modify_items(lib, mods, query, write, move, album, confirm): +def modify_items(lib, mods, dels, query, write, move, album, confirm): """Modifies matching items according to key=value assignments.""" # Parse key=value specifications into a dictionary. model_cls = library.Album if album else library.Item @@ -1102,6 +1102,8 @@ def modify_items(lib, mods, query, write, move, album, confirm): for obj in objs: for field, value in fsets.iteritems(): obj[field] = value + for field in dels: + del obj[field] if ui.show_model_changes(obj): changed.add(obj) @@ -1155,13 +1157,22 @@ modify_cmd.parser.add_option('-f', '--format', action='store', help='print with custom format', default=None) def modify_func(lib, opts, args): args = decargs(args) - mods = [a for a in args if '=' in a] - query = [a for a in args if '=' not in a] - if not mods: + mods = [] + dels = [] + query = [] + for arg in args: + if arg.endswith('!') and '=' not in arg and ':' not in arg: + dels.append(arg[:-1]) + elif '=' in arg: + mods.append(arg) + else: + query.append(arg) + if not mods and not dels: raise ui.UserError('no modifications specified') write = opts.write if opts.write is not None else \ config['import']['write'].get(bool) - modify_items(lib, mods, query, write, opts.move, opts.album, not opts.yes) + modify_items(lib, mods, dels, query, write, opts.move, opts.album, + not opts.yes) modify_cmd.func = modify_func default_commands.append(modify_cmd) diff --git a/docs/changelog.rst b/docs/changelog.rst index a7debca29..b5e4e47a5 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -32,8 +32,10 @@ New stuff: * We now only use "primary" aliases for artist names from MusicBrainz. This eliminates some strange naming that could occur when the `languages` config option was set. Thanks to Filipe Fortes. - - +* The :ref:`modify-cmd` command now allows removing flexible attribute. For + example: ``beet modify artist:beatles oldies!`` deletes the ``oldies`` + flexible attribute from the database, for the matching items. Thanks to + brilnius. Fixes: diff --git a/docs/guides/advanced.rst b/docs/guides/advanced.rst index 8c9cdf689..937721fa2 100644 --- a/docs/guides/advanced.rst +++ b/docs/guides/advanced.rst @@ -111,9 +111,9 @@ Store any data you like The beets database keeps track of a long list of :ref:`built-in fields `, but you're not limited to just that list. Say, for example, -that you like to like to categorize your music by the setting where it should -be played. You can invent a new ``context`` attribute store this. Set the -field using the :ref:`modify-cmd` command:: +that you like to categorize your music by the setting where it should be +played. You can invent a new ``context`` attribute store this. Set the field +using the :ref:`modify-cmd` command:: beet modify context=party artist:'beastie boys' @@ -125,6 +125,10 @@ other field:: You can even use these fields in your filenames (see :ref:`path-format-config`). +And, unlike :ref:`built-in fields `, such fields can be removed:: + + beet modify context! artist:'beastie boys' + Read more than you ever wanted to know about the *flexible attributes* feature `on the beets blog`_. diff --git a/docs/reference/cli.rst b/docs/reference/cli.rst index bc2e9e029..b7d7e3b68 100644 --- a/docs/reference/cli.rst +++ b/docs/reference/cli.rst @@ -201,20 +201,21 @@ modify `````` :: - beet modify [-MWay] QUERY FIELD=VALUE... + beet modify [-MWay] QUERY [FIELD=VALUE...] [FIELD!...] Change the metadata for items or albums in the database. Supply a :doc:`query ` matching the things you want to change and a series of ``field=value`` pairs. For example, ``beet modify genius of love artist="Tom Tom Club"`` will change the artist for the track "Genius of Love." -The ``-a`` switch operates on albums instead of individual tracks. Items will -automatically be moved around when necessary if they're in your library -directory, but you can disable that with ``-M``. Tags will be written to the -files according to the settings you have for imports, but these can be -overridden with ``-w`` (write tags, the default) and ``-W`` (don't write tags). -Finally, this command politely asks for your permission before making any -changes, but you can skip that prompt with the ``-y`` switch. +For removing fields (only possible with flexible attributes), specify one or +more ``field!`` argument(s). The ``-a`` switch operates on albums instead of +individual tracks. Items will automatically be moved around when necessary if +they're in your library directory, but you can disable that with ``-M``. Tags +will be written to the files according to the settings you have for imports, +but these can be overridden with ``-w`` (write tags, the default) and ``-W`` +(don't write tags). Finally, this command politely asks for your permission +before making any changes, but you can skip that prompt with the ``-y`` switch. .. _move-cmd: diff --git a/test/test_ui.py b/test/test_ui.py index 2c6e3ff6d..c5c96b3ac 100644 --- a/test/test_ui.py +++ b/test/test_ui.py @@ -157,9 +157,10 @@ class ModifyTest(_common.TestCase): self.i.move(True) self.album = self.lib.add_album([self.i]) - def _modify(self, mods, query=(), write=False, move=False, album=False): + def _modify(self, mods, dels=(), query=(), write=False, move=False, + album=False): self.io.addinput('y') - commands.modify_items(self.lib, mods, query, + commands.modify_items(self.lib, mods, dels, query, write, move, album, True) def test_modify_item_dbdata(self):