diff --git a/beets/importer.py b/beets/importer.py index eca578f6f..93d2bbed5 100644 --- a/beets/importer.py +++ b/beets/importer.py @@ -894,7 +894,7 @@ def manipulate_files(session): item.move(True) if config['import']['write'] and task.should_write_tags(): - ui.commands._item_write(item) + item.write() # Save new paths. with session.lib.transaction(): diff --git a/beets/library.py b/beets/library.py index 49ba4a9ca..100c0201b 100644 --- a/beets/library.py +++ b/beets/library.py @@ -371,15 +371,26 @@ class Item(LibModel): self.path = read_path def write(self): - """Writes the item's metadata to the associated file. + """Try to write the item's metadata to the associated file. + + Returns ``True`` if the write was successful. The method catches + file system read and write exceptions and logs an error message. + If any of 'write' event handlers returns a truthy value the + write will not be performed and an error message is logged. """ - plugins.send('write', item=self) + if any(plugins.send('write', item=self)): + log.error(u'plugin aborted writing {0}'.format( + util.displayable_path(item.path))) + return + try: f = MediaFile(syspath(self.path)) except (OSError, IOError) as exc: - raise util.FilesystemError(exc, 'read', (self.path,), - traceback.format_exc()) + log.error(u'could not read {0}: {1}'.format( + util.displayable_path(item.path), exc + )) + return for key in ITEM_KEYS_WRITABLE: setattr(f, key, self[key]) @@ -387,12 +398,15 @@ class Item(LibModel): try: f.save(id3v23=beets.config['id3v23'].get(bool)) except (OSError, IOError) as exc: - raise util.FilesystemError(exc, 'write', (self.path,), - traceback.format_exc()) + log.error(u'could not write {0}: {1}'.format( + util.displayable_path(item.path), exc + )) + return # The file has a new mtime. self.mtime = self.current_mtime() plugins.send('after_write', item=self) + return True # Files themselves. diff --git a/beets/plugins.py b/beets/plugins.py index da4588a5c..6a58777cd 100755 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -30,15 +30,6 @@ LASTFM_KEY = '2dc3914abf35f0d9c92d97d8f8e42b43' log = logging.getLogger('beets') -class BeforeWriteError(Exception): - """May be raised by plugins in a ``write`` event handler to abort - prevent writing the item's file. - - Beets will catch this exception during a call to ``item.write()`` - and display it to the user as an error. - """ - - # Managing the plugins themselves. class BeetsPlugin(object): @@ -362,10 +353,7 @@ def send(event, **arguments): name of the event to send, all other named arguments go to the event handler(s). - Returns the number of handlers called. + Returns a list of return values from the handlers. """ log.debug('Sending event: %s' % event) - handlers = event_handlers()[event] - for handler in handlers: - handler(**arguments) - return len(handlers) + return [handler(**arguments) for handler in event_handlers()[event]] diff --git a/beets/ui/commands.py b/beets/ui/commands.py index 75ad37b49..1fd18f7e0 100644 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -76,20 +76,6 @@ def _do_query(lib, query, album, also_items=True): return items, albums -def _item_write(item): - """Wrapper for ``item.write()`` that handles exceptions and logs - errors during user interaction. - """ - try: - item.write() - except (mediafile.UnreadableFileError, - util.FilesystemError, - plugins.BeforeWriteError) as exc: - log.error(u'could not write {0}: {1}'.format( - util.displayable_path(item.path), exc - )) - - # fields: Shows a list of available fields for queries and format strings. @@ -1148,7 +1134,7 @@ def modify_items(lib, mods, query, write, move, album, confirm): else: changed_items = changed for item in changed_items: - _item_write(item) + item.write() modify_cmd = ui.Subcommand('modify', help='change metadata fields', aliases=('mod',)) @@ -1245,7 +1231,7 @@ def write_items(lib, query, pretend): changed = ui.show_model_changes(item, clean_item, library.ITEM_KEYS_WRITABLE, always=True) if changed and not pretend: - _item_write(item) + item.write() write_cmd = ui.Subcommand('write', help='write tag information to files') write_cmd.parser.add_option('-p', '--pretend', action='store_true', diff --git a/docs/dev/plugins.rst b/docs/dev/plugins.rst index 418414806..408c002c3 100644 --- a/docs/dev/plugins.rst +++ b/docs/dev/plugins.rst @@ -142,8 +142,8 @@ currently available are: * *write*: called with an ``Item`` object just before a file's metadata is written to disk (i.e., just before the file on disk is opened). Event - handlers may raise ``plugins.BeforeWriteError`` to prevent beets from - writing the file and display an error to the user. + handlers may return a truthy value to prevent beets from writing the + file. In that case make sure that you log an appropriate message. * *after_write*: called with an ``Item`` object after a file's metadata is written to disk (i.e., just after the file on disk is closed).