diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py
index ab284b3c58..eb44ec3123 100644
--- a/src/calibre/gui2/dialogs/metadata_bulk.py
+++ b/src/calibre/gui2/dialogs/metadata_bulk.py
@@ -23,6 +23,8 @@ from calibre.utils.icu import sort_key, capitalize
from calibre.utils.config import prefs, tweaks
from calibre.utils.magick.draw import identify_data
from calibre.utils.date import qt_to_dt
+from calibre.ptempfile import SpooledTemporaryFile
+from calibre.db import SPOOL_SIZE
def get_cover_data(stream, ext): # {{{
from calibre.ebooks.metadata.meta import get_metadata
@@ -134,11 +136,12 @@ class MyBlockingBusy(QDialog): # {{{
do_autonumber, do_remove_format, remove_format, do_swap_ta, \
do_remove_conv, do_auto_author, series, do_series_restart, \
series_start_value, do_title_case, cover_action, clear_series, \
- pubdate, adddate, do_title_sort, languages, clear_languages = self.args
+ pubdate, adddate, do_title_sort, languages, clear_languages, \
+ restore_original = self.args
- # first loop: do author and title. These will commit at the end of each
- # operation, because each operation modifies the file system. We want to
+ # first loop: All changes that modify the filesystem and commit
+ # immediately. We want to
# try hard to keep the DB and the file system in sync, even in the face
# of exceptions or forced exits.
if self.current_phase == 1:
@@ -196,6 +199,27 @@ class MyBlockingBusy(QDialog): # {{{
if covers:
self.db.set_cover(id, covers[-1][0])
covers = []
+
+ if do_remove_format:
+ self.db.remove_format(id, remove_format, index_is_id=True,
+ notify=False, commit=True)
+
+ if restore_original:
+ formats = self.db.formats(id, index_is_id=True)
+ formats = formats.split(',') if formats else []
+ originals = [x.upper() for x in formats if
+ x.upper().startswith('ORIGINAL_')]
+ for ofmt in originals:
+ fmt = ofmt.replace('ORIGINAL_', '')
+ with SpooledTemporaryFile(SPOOL_SIZE) as stream:
+ self.db.copy_format_to(id, ofmt, stream,
+ index_is_id=True)
+ stream.seek(0)
+ self.db.add_format(id, fmt, stream, index_is_id=True,
+ notify=False)
+ self.db.remove_format(id, ofmt, index_is_id=True,
+ notify=False, commit=True)
+
elif self.current_phase == 2:
# All of these just affect the DB, so we can tolerate a total rollback
if do_auto_author:
@@ -233,9 +257,6 @@ class MyBlockingBusy(QDialog): # {{{
num = next if do_autonumber and series else 1.0
self.db.set_series_index(id, num, notify=False, commit=False)
- if do_remove_format:
- self.db.remove_format(id, remove_format, index_is_id=True, notify=False, commit=False)
-
if do_remove_conv:
self.db.delete_conversion_options(id, 'PIPE', commit=False)
@@ -936,6 +957,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
do_title_case = self.change_title_to_title_case.isChecked()
do_title_sort = self.update_title_sort.isChecked()
clear_languages = self.clear_languages.isChecked()
+ restore_original = self.restore_original.isChecked()
languages = self.languages.lang_codes
pubdate = adddate = None
if self.apply_pubdate.isChecked():
@@ -955,7 +977,8 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
do_autonumber, do_remove_format, remove_format, do_swap_ta,
do_remove_conv, do_auto_author, series, do_series_restart,
series_start_value, do_title_case, cover_action, clear_series,
- pubdate, adddate, do_title_sort, languages, clear_languages)
+ pubdate, adddate, do_title_sort, languages, clear_languages,
+ restore_original)
bb = MyBlockingBusy(_('Applying changes to %d books.\nPhase {0} {1}%%.')
%len(self.ids), args, self.db, self.ids,
diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui
index 716e010814..9309e9a41d 100644
--- a/src/calibre/gui2/dialogs/metadata_bulk.ui
+++ b/src/calibre/gui2/dialogs/metadata_bulk.ui
@@ -14,7 +14,7 @@
Edit Meta information
-
+
:/images/edit_input.png:/images/edit_input.png
@@ -210,7 +210,7 @@
Open Tag Editor
-
+
:/images/chapters.png:/images/chapters.png
@@ -381,7 +381,7 @@ from the value in the box
...
-
+
:/images/trash.png:/images/trash.png
@@ -429,7 +429,7 @@ from the value in the box
...
-
+
:/images/trash.png:/images/trash.png
@@ -443,7 +443,30 @@ from the value in the box
- -
+
-
+
+
+ &Languages:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ languages
+
+
+
+ -
+
+
+ -
+
+
+ Remove &all
+
+
+
+ -
Remove &format:
@@ -453,17 +476,44 @@ from the value in the box
- -
-
-
-
- 120
- 16777215
-
-
-
+
-
+
+
-
+
+
+
+ 120
+ 16777215
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ When doing a same format to same format conversion, for e.g., EPUB to EPUB, calibre saves the original EPUB as ORIGINAL_EPUB. This option tells calibre to restore the EPUB from ORIGINAL_EPUB. Useful if you did a bulk conversion of a large number of books and something went wrong.
+
+
+ Restore pre conversion &originals, if available
+
+
+
+
- -
+
-
Qt::Vertical
@@ -479,7 +529,7 @@ from the value in the box
- -
+
-
-
@@ -529,7 +579,7 @@ Future conversion of these books will use the default settings.
- -
+
-
Change &cover
@@ -559,7 +609,7 @@ Future conversion of these books will use the default settings.
- -
+
-
Qt::Vertical
@@ -572,29 +622,6 @@ Future conversion of these books will use the default settings.
- -
-
-
- &Languages:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- languages
-
-
-
- -
-
-
- -
-
-
- Remove &all
-
-
-
@@ -1199,13 +1226,13 @@ not multiple and the destination field is multiple
languages
clear_languages
remove_format
+ restore_original
change_title_to_title_case
update_title_sort
remove_conversion_settings
cover_generate
cover_remove
cover_from_fmt
- starting_from
multiple_separator
test_text
test_result
@@ -1229,9 +1256,10 @@ not multiple and the destination field is multiple
destination_field
search_for
case_sensitive
+ starting_from
-
+