mirror of
https://github.com/beetbox/beets.git
synced 2025-12-06 08:39:17 +01:00
Merge pull request #4823 from JOJ0/album_flex_streamline
Streamline album attributes modification behaviour and allow override via CLI
This commit is contained in:
commit
62859f4389
7 changed files with 43 additions and 17 deletions
|
|
@ -1369,22 +1369,27 @@ class Album(LibModel):
|
||||||
|
|
||||||
plugins.send('art_set', album=self)
|
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.
|
"""Update the database with the album information.
|
||||||
|
|
||||||
The album's tracks are also updated.
|
|
||||||
|
|
||||||
`fields` represents the fields to be stored. If not specified,
|
`fields` represents the fields to be stored. If not specified,
|
||||||
all fields will be.
|
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.
|
# Get modified track fields.
|
||||||
track_updates = {}
|
track_updates = {}
|
||||||
track_deletes = set()
|
track_deletes = set()
|
||||||
for key in self._dirty:
|
for key in self._dirty:
|
||||||
if key in self.item_keys:
|
if inherit:
|
||||||
track_updates[key] = self[key]
|
if key in self.item_keys: # is a fixed attribute
|
||||||
elif key not in self:
|
track_updates[key] = self[key]
|
||||||
track_deletes.add(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():
|
with self._db.transaction():
|
||||||
super().store(fields)
|
super().store(fields)
|
||||||
|
|
@ -1400,7 +1405,7 @@ class Album(LibModel):
|
||||||
del item[key]
|
del item[key]
|
||||||
item.store()
|
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.
|
"""Synchronize the album and its items with the database.
|
||||||
Optionally, also write any new tags into the files and update
|
Optionally, also write any new tags into the files and update
|
||||||
their paths.
|
their paths.
|
||||||
|
|
@ -1409,7 +1414,7 @@ class Album(LibModel):
|
||||||
`move` controls whether files (both audio and album art) are
|
`move` controls whether files (both audio and album art) are
|
||||||
moved.
|
moved.
|
||||||
"""
|
"""
|
||||||
self.store()
|
self.store(inherit=inherit)
|
||||||
for item in self.items():
|
for item in self.items():
|
||||||
item.try_sync(write, move)
|
item.try_sync(write, move)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1498,7 +1498,7 @@ default_commands.append(version_cmd)
|
||||||
|
|
||||||
# modify: Declaratively change metadata.
|
# 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
|
"""Modifies matching items according to user-specified assignments and
|
||||||
deletions.
|
deletions.
|
||||||
|
|
||||||
|
|
@ -1551,7 +1551,7 @@ def modify_items(lib, mods, dels, query, write, move, album, confirm):
|
||||||
# Apply changes to database and files
|
# Apply changes to database and files
|
||||||
with lib.transaction():
|
with lib.transaction():
|
||||||
for obj in changed:
|
for obj in changed:
|
||||||
obj.try_sync(write, move)
|
obj.try_sync(write, move, inherit)
|
||||||
|
|
||||||
|
|
||||||
def print_and_modify(obj, mods, dels):
|
def print_and_modify(obj, mods, dels):
|
||||||
|
|
@ -1594,7 +1594,8 @@ def modify_func(lib, opts, args):
|
||||||
if not mods and not dels:
|
if not mods and not dels:
|
||||||
raise ui.UserError('no modifications specified')
|
raise ui.UserError('no modifications specified')
|
||||||
modify_items(lib, mods, dels, query, ui.should_write(opts.write),
|
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(
|
modify_cmd = ui.Subcommand(
|
||||||
|
|
@ -1622,6 +1623,10 @@ modify_cmd.parser.add_option(
|
||||||
'-y', '--yes', action='store_true',
|
'-y', '--yes', action='store_true',
|
||||||
help='skip confirmation'
|
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
|
modify_cmd.func = modify_func
|
||||||
default_commands.append(modify_cmd)
|
default_commands.append(modify_cmd)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -296,4 +296,4 @@ class IPFSPlugin(BeetsPlugin):
|
||||||
self._log.info("Adding '{0}' to temporary library", album)
|
self._log.info("Adding '{0}' to temporary library", album)
|
||||||
new_album = tmplib.add_album(items)
|
new_album = tmplib.add_album(items)
|
||||||
new_album.ipfs = album.ipfs
|
new_album.ipfs = album.ipfs
|
||||||
new_album.store()
|
new_album.store(inherit=False)
|
||||||
|
|
|
||||||
|
|
@ -214,6 +214,11 @@ Bug fixes:
|
||||||
the highest number of likes
|
the highest number of likes
|
||||||
* :doc:`/plugins/lyrics`: Fix a crash with the Google backend when processing
|
* :doc:`/plugins/lyrics`: Fix a crash with the Google backend when processing
|
||||||
some web pages. :bug:`4875`
|
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:
|
For packagers:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
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
|
and ``beet modify title='$track $title'`` will add track numbers to their
|
||||||
title metadata.
|
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
|
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*
|
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
|
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
|
to avoid a confusing situation where the data for individual tracks conflicts
|
||||||
with the data for the whole album.
|
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
|
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
|
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
|
to the files according to the settings you have for imports, but these can be
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ class IPFSPluginTest(unittest.TestCase, TestHelper):
|
||||||
|
|
||||||
album = self.lib.add_album(items)
|
album = self.lib.add_album(items)
|
||||||
album.ipfs = "QmfM9ic5LJj7V6ecozFx1MkSoaaiq3PXfhJoFvyqzpLXSf"
|
album.ipfs = "QmfM9ic5LJj7V6ecozFx1MkSoaaiq3PXfhJoFvyqzpLXSf"
|
||||||
album.store()
|
album.store(inherit=False)
|
||||||
|
|
||||||
return album
|
return album
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1022,10 +1022,17 @@ class AlbumInfoTest(_common.TestCase):
|
||||||
self.assertEqual(i.albumartist, 'myNewArtist')
|
self.assertEqual(i.albumartist, 'myNewArtist')
|
||||||
self.assertNotEqual(i.artist, '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):
|
def test_albuminfo_change_artist_does_not_change_items(self):
|
||||||
ai = self.lib.get_album(self.i)
|
ai = self.lib.get_album(self.i)
|
||||||
ai.artist = 'myNewArtist'
|
ai.artist = 'myNewArtist'
|
||||||
ai.store()
|
ai.store(inherit=False)
|
||||||
i = self.lib.items()[0]
|
i = self.lib.items()[0]
|
||||||
self.assertNotEqual(i.artist, 'myNewArtist')
|
self.assertNotEqual(i.artist, 'myNewArtist')
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue