Merge pull request #4899 from fracai/update-field-exclusions

Support excluding fields with update command
This commit is contained in:
Adrian Sampson 2023-09-12 11:18:29 -04:00 committed by GitHub
commit cd0f2b1aa3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 47 additions and 14 deletions

View file

@ -1196,19 +1196,37 @@ default_commands.append(list_cmd)
# update: Update library contents according to on-disk tags.
def update_items(lib, query, album, move, pretend, fields):
def update_items(lib, query, album, move, pretend, fields,
exclude_fields=None):
"""For all the items matched by the query, update the library to
reflect the item's embedded tags.
:param fields: The fields to be stored. If not specified, all fields will
be.
:param exclude_fields: The fields to not be stored. If not specified, all
fields will be.
"""
with lib.transaction():
items, _ = _do_query(lib, query, album)
if move and fields is not None and 'path' not in fields:
# Special case: if an item needs to be moved, the path field has to
# updated; otherwise the new path will not be reflected in the
# database.
fields.append('path')
items, _ = _do_query(lib, query, album)
if fields is None:
# no fields were provided, update all media fields
item_fields = fields or library.Item._media_fields
if move and 'path' not in item_fields:
# move is enabled, add 'path' to the list of fields to update
item_fields.add('path')
else:
# fields was provided, just update those
item_fields = fields
# get all the album fields to update
album_fields = fields or library.Album._fields.keys()
if exclude_fields:
# remove any excluded fields from the item and album sets
item_fields = [f for f in item_fields if f not in exclude_fields]
album_fields = [f for f in album_fields if f not in exclude_fields]
# Walk through the items and pick up their changes.
affected_albums = set()
@ -1248,7 +1266,7 @@ def update_items(lib, query, album, move, pretend, fields):
# Check for and display changes.
changed = ui.show_model_changes(
item,
fields=fields or library.Item._media_fields)
fields=item_fields)
# Save changes.
if not pretend:
@ -1257,14 +1275,14 @@ def update_items(lib, query, album, move, pretend, fields):
if move and lib.directory in ancestry(item.path):
item.move(store=False)
item.store(fields=fields)
item.store(fields=item_fields)
affected_albums.add(item.album_id)
else:
# The file's mtime was different, but there were no
# changes to the metadata. Store the new mtime,
# which is set in the call to read(), so we don't
# check this again in the future.
item.store(fields=fields)
item.store(fields=item_fields)
# Skip album changes while pretending.
if pretend:
@ -1283,7 +1301,7 @@ def update_items(lib, query, album, move, pretend, fields):
# Update album structure to reflect an item in it.
for key in library.Album.item_keys:
album[key] = first_item[key]
album.store(fields=fields)
album.store(fields=album_fields)
# Move album art (and any inconsistent items).
if move and lib.directory in ancestry(first_item.path):
@ -1293,9 +1311,9 @@ def update_items(lib, query, album, move, pretend, fields):
items = list(album.items())
for item in items:
item.move(store=False, with_album=False)
item.store(fields=fields)
item.store(fields=item_fields)
album.move(store=False)
album.store(fields=fields)
album.store(fields=album_fields)
def update_func(lib, opts, args):
@ -1306,7 +1324,7 @@ def update_func(lib, opts, args):
if not ui.input_yn("Are you sure you want to continue (y/n)?", True):
return
update_items(lib, decargs(args), opts.album, ui.should_move(opts.move),
opts.pretend, opts.fields)
opts.pretend, opts.fields, opts.exclude_fields)
update_cmd = ui.Subcommand(
@ -1330,6 +1348,11 @@ update_cmd.parser.add_option(
'-F', '--field', default=None, action='append', dest='fields',
help='list of fields to update'
)
update_cmd.parser.add_option(
'-e', '--exclude-field', default=None, action='append',
dest='exclude_fields',
help='list of fields to exclude from updates'
)
update_cmd.func = update_func
default_commands.append(update_cmd)

View file

@ -11,6 +11,7 @@ for Python 3.6).
New features:
* :ref:`update-cmd`: added ```-e``` flag for excluding fields from being updated.
* :doc:`/plugins/deezer`: Import rank and other attributes from Deezer during import and add a function to update the rank of existing items.
:bug:`4841`
* resolve transl-tracklisting relations for pseudo releases and merge data with the actual release

View file

@ -329,7 +329,7 @@ update
``````
::
beet update [-F] FIELD [-aM] QUERY
beet update [-F] FIELD [-e] EXCLUDE_FIELD [-aM] QUERY
Update the library (and, by default, move files) to reflect out-of-band metadata
changes and file deletions.
@ -347,8 +347,9 @@ on disk.
By default, all the changed metadata will be populated back to the database.
If you only want certain fields to be written, specify them with the ```-F```
flags (which can be used multiple times). For the list of supported fields,
please see ```beet fields```.
flags (which can be used multiple times). Alternatively, specify fields to *not*
write with ```-e``` flags (which can be used multiple times). For the list of
supported fields, please see ```beet fields```.
When an updated track is part of an album, the album-level fields of *all*
tracks from the album are also updated. (Specifically, the command copies

View file

@ -579,13 +579,13 @@ class UpdateTest(_common.TestCase):
util.remove(artfile)
def _update(self, query=(), album=False, move=False, reset_mtime=True,
fields=None):
fields=None, exclude_fields=None):
self.io.addinput('y')
if reset_mtime:
self.i.mtime = 0
self.i.store()
commands.update_items(self.lib, query, album, move, False,
fields=fields)
fields=fields, exclude_fields=exclude_fields)
def test_delete_removes_item(self):
self.assertTrue(list(self.lib.items()))
@ -729,6 +729,14 @@ class UpdateTest(_common.TestCase):
self.assertEqual(album.albumtype, correct_albumtype)
self.assertEqual(album.albumtypes, correct_albumtypes)
def test_modified_metadata_excluded(self):
mf = MediaFile(syspath(self.i.path))
mf.lyrics = 'new lyrics'
mf.save()
self._update(exclude_fields=['lyrics'])
item = self.lib.items().get()
self.assertNotEqual(item.lyrics, 'new lyrics')
class PrintTest(_common.TestCase):
def setUp(self):