diff --git a/beets/config_default.yaml b/beets/config_default.yaml index 439a93f55f..942459738e 100644 --- a/beets/config_default.yaml +++ b/beets/config_default.yaml @@ -10,6 +10,7 @@ import: delete: no resume: ask incremental: no + from_scratch: no quiet_fallback: skip none_rec_action: ask timid: no diff --git a/beets/importer.py b/beets/importer.py index e91b356563..186d824b65 100644 --- a/beets/importer.py +++ b/beets/importer.py @@ -534,6 +534,10 @@ def imported_items(self): def apply_metadata(self): """Copy metadata from match info to the items. """ + if config['import']['from_scratch']: + for item in self.match.mapping: + item.clear() + autotag.apply_metadata(self.match.info, self.match.mapping) def duplicate_items(self, lib): diff --git a/beets/library.py b/beets/library.py index 597cfe625b..64035e6422 100644 --- a/beets/library.py +++ b/beets/library.py @@ -561,6 +561,11 @@ def update(self, values): if self.mtime == 0 and 'mtime' in values: self.mtime = values['mtime'] + def clear(self): + """Set all key/value pairs to None.""" + for key in self._media_fields: + setattr(self, key, None) + def get_album(self): """Get the Album object that this item belongs to, if any, or None if the item is a singleton or is not associated with a diff --git a/beets/ui/commands.py b/beets/ui/commands.py index c8beb11e22..3a1811cf3f 100644 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -1004,6 +1004,10 @@ def import_func(lib, opts, args): u'-I', u'--noincremental', dest='incremental', action='store_false', help=u'do not skip already-imported directories' ) +import_cmd.parser.add_option( + u'--from-scratch', dest='from_scratch', action='store_true', + help=u'erase existing metadata before applying new metadata' +) import_cmd.parser.add_option( u'--flat', dest='flat', action='store_true', help=u'import an entire tree as a single album' diff --git a/docs/reference/cli.rst b/docs/reference/cli.rst index 3e668f013d..39a4b3f10a 100644 --- a/docs/reference/cli.rst +++ b/docs/reference/cli.rst @@ -111,6 +111,12 @@ Optional command flags: time, when no subdirectories will be skipped. So consider enabling the ``incremental`` configuration option. +* When beets applies metadata to your music, it will retain the value of any + existing tags that weren't overwritten, and import them into the database. You + may prefer to only use existing metadata for finding matches, and to erase it + completely when new metadata is applied. You can enforce this behavior with + the ``--from-scratch`` option, or the ``from_scratch`` configuration option. + * By default, beets will proceed without asking if it finds a very close metadata match. To disable this and have the importer ask you every time, use the ``-t`` (for *timid*) option. diff --git a/docs/reference/config.rst b/docs/reference/config.rst index ce45a94ee6..a4ed966993 100644 --- a/docs/reference/config.rst +++ b/docs/reference/config.rst @@ -475,6 +475,15 @@ Either ``yes`` or ``no``, controlling whether imported directories are recorded and whether these recorded directories are skipped. This corresponds to the ``-i`` flag to ``beet import``. +.. _from_scratch: + +from_scratch +~~~~~~~~~~~~ + +Either ``yes`` or ``no`` (default), controlling whether existing metadata is +discarded when a match is applied. This corresponds to the ``-from_scratch`` +flag to ``beet import``. + quiet_fallback ~~~~~~~~~~~~~~ diff --git a/test/test_importer.py b/test/test_importer.py index c6b021f335..e30f5609c8 100644 --- a/test/test_importer.py +++ b/test/test_importer.py @@ -633,6 +633,17 @@ def test_apply_candidate_adds_album_path(self): self.assert_file_in_lib( b'Applied Artist', b'Applied Album', b'Applied Title 1.mp3') + def test_apply_from_scratch_removes_other_metadata(self): + config['import']['from_scratch'] = True + + for mediafile in self.import_media: + mediafile.genre = u'Tag Genre' + mediafile.save() + + self.importer.add_choice(importer.action.APPLY) + self.importer.run() + self.assertEqual(self.lib.items().get().genre, u'') + def test_apply_with_move_deletes_import(self): config['import']['move'] = True