diff --git a/beets/library.py b/beets/library.py index 945594307..82393644c 100644 --- a/beets/library.py +++ b/beets/library.py @@ -420,16 +420,14 @@ class Item(LibModel): Can raise either a `ReadError` or a `WriteError`. """ try: - f = MediaFile(syspath(self.path)) + mediafile = MediaFile(syspath(self.path)) except (OSError, IOError) as exc: raise ReadError(self.path, exc) plugins.send('write', item=self) - for key in ITEM_KEYS_WRITABLE: - setattr(f, key, self[key]) try: - f.save(id3v23=beets.config['id3v23'].get(bool)) + mediafile.update(self, id3v23=beets.config['id3v23'].get(bool)) except (OSError, IOError, MutagenError) as exc: raise WriteError(self.path, exc) diff --git a/beets/mediafile.py b/beets/mediafile.py index 3ef7d16f8..c01c32424 100644 --- a/beets/mediafile.py +++ b/beets/mediafile.py @@ -1219,6 +1219,7 @@ class MediaFile(object): if self.mgfile.tags is None: self.mgfile.add_tags() + # FIXME Deprecated. Use `update` instead def save(self, id3v23=False): """Write the object's tags back to the file. @@ -1264,6 +1265,25 @@ class MediaFile(object): if isinstance(descriptor, MediaField): yield property + def update(self, dict, id3v23=False): + """Update tags from the dictionary and write them to the file. + + For any key in ``dict`` that is also a field to store tags the + method retrieves the corresponding value from ``dict`` and + updates the ``MediaFile``. If any of the tags are changed, these + changes are written to the disk. + + By default, MP3 files are saved with ID3v2.4 tags. You can use + the older ID3v2.3 standard by specifying the `id3v23` option. + """ + updated = False + for field in self.fields(): + if field in dict and getattr(self, field) != dict[field]: + updated = True + setattr(self, field, dict[field]) + if updated: + self.save(id3v23) + # Field definitions. diff --git a/test/test_mediafile.py b/test/test_mediafile.py index c710bde1f..cca299ed6 100644 --- a/test/test_mediafile.py +++ b/test/test_mediafile.py @@ -353,6 +353,15 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin): mediafile = MediaFile(mediafile.path) self.assertTags(mediafile, tags) + def test_update_empty(self): + mediafile = self._mediafile_fixture('empty') + tags = self._generate_tags() + + mediafile.update(tags) + + mediafile = MediaFile(mediafile.path) + self.assertTags(mediafile, tags) + def test_overwrite_full(self): mediafile = self._mediafile_fixture('full') tags = self._generate_tags() @@ -369,6 +378,17 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin): mediafile = MediaFile(mediafile.path) self.assertTags(mediafile, tags) + def test_update_full(self): + mediafile = self._mediafile_fixture('full') + tags = self._generate_tags() + + mediafile.update(tags) + # Make sure the tags are already set when writing a second time + mediafile.update(tags) + + mediafile = MediaFile(mediafile.path) + self.assertTags(mediafile, tags) + def test_write_date_components(self): mediafile = self._mediafile_fixture('full') mediafile.year = 2001