diff --git a/beets/library.py b/beets/library.py index d8be2d62e..510b1ea0f 100644 --- a/beets/library.py +++ b/beets/library.py @@ -770,11 +770,12 @@ class Item(Model): """ mapping = super(Item, self)._formatted_mapping(for_path) - # Override album-level fields. + # Merge in album-level fields. album = self.get_album() if album: - for key in ALBUM_KEYS_ITEM: - mapping[key] = album._get_formatted(key, for_path) + for key in album.keys(True): + if key in ALBUM_KEYS_ITEM or key not in ITEM_KEYS: + mapping[key] = album._get_formatted(key, for_path) # Use the album artist if the track artist is not set and # vice-versa. diff --git a/docs/changelog.rst b/docs/changelog.rst index 6114fceac..9b8d80647 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -11,6 +11,8 @@ consequences for all users are: the :doc:`/plugins/inline` to define a field called ``era``, you can now filter your library based on that field by typing something like ``beet list era:goldenage``. +* Album-level flexible attributes and plugin-provided attributes can now be + used in path formats (and other item-level templates). For developers, the short version of the story is that Item and Album objects provide *uniform access* across fixed, flexible, and computed attributes. diff --git a/test/test_db.py b/test/test_db.py index 957a0f8da..444c02e78 100644 --- a/test/test_db.py +++ b/test/test_db.py @@ -1022,6 +1022,28 @@ class ImportTimeTest(_common.TestCase): self.assertGreater(self.singleton.added, 0) +class TemplateTest(_common.LibTestCase): + def album_fields_override_item_values(self): + self.album = self.lib.add_album([self.i]) + self.album.albumartist = 'album-level' + self.album.store() + self.i.albumartist = 'track-level' + self.i.store() + self.assertEqual(self.i.evaluate_template('$albumartist'), + 'album-level') + + def test_year_formatted_in_template(self): + self.i.year = 123 + self.i.store() + self.assertEqual(self.i.evaluate_template('$year'), '0123') + + def test_album_flexattr_appears_in_item_template(self): + self.album = self.lib.add_album([self.i]) + self.album.foo = 'baz' + self.album.store() + self.assertEqual(self.i.evaluate_template('$foo'), 'baz') + + def suite(): return unittest.TestLoader().loadTestsFromName(__name__)