Merge pull request #4823 from JOJ0/album_flex_streamline

Streamline album attributes modification behaviour and allow override via CLI
This commit is contained in:
J0J0 Todos 2023-08-23 09:14:26 +02:00 committed by GitHub
commit 62859f4389
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 43 additions and 17 deletions

View file

@ -1369,22 +1369,27 @@ class Album(LibModel):
plugins.send('art_set', album=self)
def store(self, fields=None):
def store(self, fields=None, inherit=True):
"""Update the database with the album information.
The album's tracks are also updated.
`fields` represents the fields to be stored. If not specified,
all fields will be.
The album's tracks are also updated when the `inherit` flag is enabled.
This applies to fixed attributes as well as flexible ones. The `id`
attribute of the album will never be inherited.
"""
# Get modified track fields.
track_updates = {}
track_deletes = set()
for key in self._dirty:
if key in self.item_keys:
track_updates[key] = self[key]
elif key not in self:
track_deletes.add(key)
if inherit:
if key in self.item_keys: # is a fixed attribute
track_updates[key] = self[key]
elif key not in self: # is a fixed or a flexible attribute
track_deletes.add(key)
elif key != 'id': # is a flexible attribute
track_updates[key] = self[key]
with self._db.transaction():
super().store(fields)
@ -1400,7 +1405,7 @@ class Album(LibModel):
del item[key]
item.store()
def try_sync(self, write, move):
def try_sync(self, write, move, inherit=True):
"""Synchronize the album and its items with the database.
Optionally, also write any new tags into the files and update
their paths.
@ -1409,7 +1414,7 @@ class Album(LibModel):
`move` controls whether files (both audio and album art) are
moved.
"""
self.store()
self.store(inherit=inherit)
for item in self.items():
item.try_sync(write, move)

View file

@ -1498,7 +1498,7 @@ default_commands.append(version_cmd)
# modify: Declaratively change metadata.
def modify_items(lib, mods, dels, query, write, move, album, confirm):
def modify_items(lib, mods, dels, query, write, move, album, confirm, inherit):
"""Modifies matching items according to user-specified assignments and
deletions.
@ -1551,7 +1551,7 @@ def modify_items(lib, mods, dels, query, write, move, album, confirm):
# Apply changes to database and files
with lib.transaction():
for obj in changed:
obj.try_sync(write, move)
obj.try_sync(write, move, inherit)
def print_and_modify(obj, mods, dels):
@ -1594,7 +1594,8 @@ def modify_func(lib, opts, args):
if not mods and not dels:
raise ui.UserError('no modifications specified')
modify_items(lib, mods, dels, query, ui.should_write(opts.write),
ui.should_move(opts.move), opts.album, not opts.yes)
ui.should_move(opts.move), opts.album, not opts.yes,
opts.inherit)
modify_cmd = ui.Subcommand(
@ -1622,6 +1623,10 @@ modify_cmd.parser.add_option(
'-y', '--yes', action='store_true',
help='skip confirmation'
)
modify_cmd.parser.add_option(
'-I', '--noinherit', action='store_false', dest='inherit', default=True,
help="when modifying albums, don't also change item data"
)
modify_cmd.func = modify_func
default_commands.append(modify_cmd)

View file

@ -296,4 +296,4 @@ class IPFSPlugin(BeetsPlugin):
self._log.info("Adding '{0}' to temporary library", album)
new_album = tmplib.add_album(items)
new_album.ipfs = album.ipfs
new_album.store()
new_album.store(inherit=False)

View file

@ -214,6 +214,11 @@ Bug fixes:
the highest number of likes
* :doc:`/plugins/lyrics`: Fix a crash with the Google backend when processing
some web pages. :bug:`4875`
* Modifying flexible attributes of albums now cascade to the individual album
tracks, similar to how fixed album attributes have been cascading to tracks
already. A new option ``--noinherit/-I`` to :ref:`modify <modify-cmd>`
allows changing this behaviour.
:bug:`4822`
For packagers:

View file

@ -257,7 +257,7 @@ modify
``````
::
beet modify [-MWay] [-f FORMAT] QUERY [FIELD=VALUE...] [FIELD!...]
beet modify [-IMWay] [-f FORMAT] QUERY [FIELD=VALUE...] [FIELD!...]
Change the metadata for items or albums in the database.
@ -274,13 +274,17 @@ name into the artist field for all your tracks,
and ``beet modify title='$track $title'`` will add track numbers to their
title metadata.
The ``-a`` switch also operates on albums in addition to the individual tracks.
The ``-a`` option changes to querying album fields instead of track fields and
also enables to operate on albums in addition to the individual tracks.
Without this flag, the command will only change *track-level* data, even if all
the tracks belong to the same album. If you want to change an *album-level*
field, such as ``year`` or ``albumartist``, you'll want to use the ``-a`` flag
to avoid a confusing situation where the data for individual tracks conflicts
with the data for the whole album.
Modifications issued using ``-a`` by default cascade to individual tracks. To
prevent this behavior, use ``-I``/``--noinherit``.
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

View file

@ -87,7 +87,7 @@ class IPFSPluginTest(unittest.TestCase, TestHelper):
album = self.lib.add_album(items)
album.ipfs = "QmfM9ic5LJj7V6ecozFx1MkSoaaiq3PXfhJoFvyqzpLXSf"
album.store()
album.store(inherit=False)
return album

View file

@ -1022,10 +1022,17 @@ class AlbumInfoTest(_common.TestCase):
self.assertEqual(i.albumartist, 'myNewArtist')
self.assertNotEqual(i.artist, 'myNewArtist')
def test_albuminfo_change_artist_does_change_items(self):
ai = self.lib.get_album(self.i)
ai.artist = 'myNewArtist'
ai.store(inherit=True)
i = self.lib.items()[0]
self.assertEqual(i.artist, 'myNewArtist')
def test_albuminfo_change_artist_does_not_change_items(self):
ai = self.lib.get_album(self.i)
ai.artist = 'myNewArtist'
ai.store()
ai.store(inherit=False)
i = self.lib.items()[0]
self.assertNotEqual(i.artist, 'myNewArtist')