diff --git a/beets/library.py b/beets/library.py index 945594307..fe4d9e0b5 100644 --- a/beets/library.py +++ b/beets/library.py @@ -414,17 +414,23 @@ class Item(LibModel): self.path = read_path - def write(self): - """Write the item's metadata to the associated file. + def write(self, path=None): + """Write the item's metadata to a media file. + + ``path`` defaults to the item's path property. Can raise either a `ReadError` or a `WriteError`. """ + if path is None: + path = self.path + else: + path = normpath(path) try: - f = MediaFile(syspath(self.path)) + f = MediaFile(syspath(path)) except (OSError, IOError) as exc: raise ReadError(self.path, exc) - plugins.send('write', item=self) + plugins.send('write', item=self, path=path) for key in ITEM_KEYS_WRITABLE: setattr(f, key, self[key]) diff --git a/beets/plugins.py b/beets/plugins.py index 35fc314d1..b6fb157ca 100755 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -17,6 +17,7 @@ import logging import traceback from collections import defaultdict +import inspect import beets @@ -339,4 +340,8 @@ def send(event, **arguments): Returns a list of return values from the handlers. """ log.debug('Sending event: %s' % event) - return [handler(**arguments) for handler in event_handlers()[event]] + for handler in event_handlers()[event]: + # Don't break legacy plugins if we want to pass more arguments + argspec = inspect.getargspec(handler).args + args = dict((k, v) for k, v in arguments.items() if k in argspec) + handler(**args) diff --git a/beetsplug/convert.py b/beetsplug/convert.py index c9645ae5a..031b4d839 100644 --- a/beetsplug/convert.py +++ b/beetsplug/convert.py @@ -174,13 +174,12 @@ def convert_item(dest_dir, keep_new, path_formats): encode(item.path, dest) # Write tags from the database to the converted file. - if not keep_new: - item.path = dest - item.write() + item.write(path=dest) - # If we're keeping the transcoded file, read it again (after - # writing) to get new bitrate, duration, etc. if keep_new: + # If we're keeping the transcoded file, read it again (after + # writing) to get new bitrate, duration, etc. + item.path = dest item.read() item.store() # Store new path and audio data. diff --git a/test/test_library.py b/test/test_library.py index 3fa3d1173..41585d50b 100644 --- a/test/test_library.py +++ b/test/test_library.py @@ -29,6 +29,7 @@ import beets.library from beets import util from beets import plugins from beets import config +from beets.mediafile import MediaFile TEMP_LIB = os.path.join(_common.RSRC, 'test_copy.blb') @@ -958,6 +959,20 @@ class WriteTest(_common.LibTestCase): self.i.path = path self.assertRaises(beets.library.WriteError, self.i.write) + def test_write_with_custom_path(self): + custom_path = os.path.join(self.temp_dir, 'file.mp3') + self.i.path = os.path.join(self.temp_dir, 'item_file.mp3') + shutil.copy(os.path.join(_common.RSRC, 'empty.mp3'), custom_path) + shutil.copy(os.path.join(_common.RSRC, 'empty.mp3'), self.i.path) + + self.i['artist'] = 'new artist' + self.assertNotEqual(MediaFile(custom_path).artist, 'new artist') + self.assertNotEqual(MediaFile(self.i.path).artist, 'new artist') + + self.i.write(custom_path) + self.assertEqual(MediaFile(custom_path).artist, 'new artist') + self.assertNotEqual(MediaFile(self.i.path).artist, 'new artist') + def suite(): return unittest.TestLoader().loadTestsFromName(__name__)