diff --git a/beets/library.py b/beets/library.py index 789b4c72f..d040e1c18 100644 --- a/beets/library.py +++ b/beets/library.py @@ -186,7 +186,7 @@ ITEM_FIELDS = [ ] ITEM_KEYS = [f[0] for f in ITEM_FIELDS] ITEM_KEYS_WRITABLE = set(MediaFile.fields()).intersection(ITEM_KEYS) -ITEM_KEYS_META = set(MediaFile.readable_fields()).intersection(ITEM_KEYS) + # Database fields for the "albums" table. # The third entry in each tuple indicates whether the field reflects an @@ -333,6 +333,13 @@ class Item(LibModel): _flex_table = 'item_attributes' _search_fields = ITEM_DEFAULT_FIELDS + media_fields = set(MediaFile.readable_fields()).intersection(ITEM_KEYS) + """Set of property names to read from ``MediaFile``. + + ``item.read()`` will read all properties in this set from + ``MediaFile`` and set them on the item. + """ + @classmethod def _getters(cls): return plugins.item_field_getters() @@ -383,8 +390,11 @@ class Item(LibModel): # Interaction with file metadata. def read(self, read_path=None): - """Read the metadata from the associated file. If read_path is - specified, read metadata from that file instead. + """Read the metadata from the associated file. + + If ``read_path`` is specified, read metadata from that file + instead. Updates all the properties in ``Item.media_fields`` + from the media file. Raises a `ReadError` if the file could not be read. """ @@ -393,20 +403,19 @@ class Item(LibModel): else: read_path = normpath(read_path) try: - f = MediaFile(syspath(read_path)) + mediafile = MediaFile(syspath(read_path)) except (OSError, IOError) as exc: raise ReadError(read_path, exc) - for key in list(ITEM_KEYS_META) + MediaFile.custom_fields: - value = getattr(f, key) + for key in list(self.media_fields): + value = getattr(mediafile, key) if isinstance(value, (int, long)): - # Filter values wider than 64 bits (in signed - # representation). SQLite cannot store them. - # py26: Post transition, we can use: + # Filter values wider than 64 bits (in signed representation). + # SQLite cannot store them. py26: Post transition, we can use: # value.bit_length() > 63 if abs(value) >= 2 ** 63: value = 0 - setattr(self, key, value) + self[key] = value # Database's mtime should now reflect the on-disk value. if read_path == self.path: @@ -417,6 +426,8 @@ class Item(LibModel): def write(self): """Write the item's metadata to the associated file. + Updates the mediafile with properties from itself. + Can raise either a `ReadError` or a `WriteError`. """ try: diff --git a/beets/mediafile.py b/beets/mediafile.py index 7c47e54f2..3f207ef20 100644 --- a/beets/mediafile.py +++ b/beets/mediafile.py @@ -1276,8 +1276,6 @@ class MediaFile(object): 'channels', 'format']: yield property - custom_fields = [] - @classmethod def add_field(cls, name, descriptor): """Add a field to store custom tags. @@ -1295,7 +1293,6 @@ class MediaFile(object): if name in cls.__dict__: raise ValueError( u'property "{0}" already exists on MediaField'.format(name)) - cls.custom_fields.append(name) setattr(cls, name, descriptor) def update(self, dict, id3v23=False): diff --git a/beets/ui/commands.py b/beets/ui/commands.py index d45192be1..024e3a3b7 100644 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -923,7 +923,7 @@ def update_items(lib, query, album, move, pretend): # Check for and display changes. changed = ui.show_model_changes(item, - fields=library.ITEM_KEYS_META) + fields=library.Item.media_fields) # Save changes. if not pretend: diff --git a/test/test_mediafile.py b/test/test_mediafile.py index 4334f3c7e..4f8a1b14c 100644 --- a/test/test_mediafile.py +++ b/test/test_mediafile.py @@ -272,9 +272,8 @@ class ExtendedFieldTestMixin(object): mediafile = MediaFile(mediafile.path) self.assertEqual(mediafile.initialkey, 'F#') delattr(MediaFile, 'initialkey') - MediaFile.custom_fields = [] - def test_extended_attribute_from_item(self): + def test_write_extended_tag_from_item(self): MediaFile.add_field('initialkey', field_extension) mediafile = self._mediafile_fixture('empty') @@ -284,11 +283,12 @@ class ExtendedFieldTestMixin(object): item.write() mediafile = MediaFile(mediafile.path) self.assertEqual(mediafile.initialkey, 'Gb') - delattr(MediaFile, 'initialkey') - MediaFile.custom_fields = [] - def test_flexible_attribute_from_file(self): + delattr(MediaFile, 'initialkey') + + def test_read_flexible_attribute_from_file(self): MediaFile.add_field('initialkey', field_extension) + Item.media_fields.add('initialkey') mediafile = self._mediafile_fixture('empty') mediafile.update({'initialkey': 'F#'}) @@ -297,7 +297,7 @@ class ExtendedFieldTestMixin(object): self.assertEqual(item['initialkey'], 'F#') delattr(MediaFile, 'initialkey') - MediaFile.custom_fields = [] + Item.media_fields.remove('initialkey') def test_invalid_descriptor(self): with self.assertRaises(ValueError) as cm: