mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add an option in the bulk metadata edit dialog to restore the pre-conversion files for many books with a single click. Fixes #886116 ([Enhancement] Format Conversion Error)
This commit is contained in:
parent
a5fa43ad5b
commit
6d17b9323c
@ -23,6 +23,8 @@ from calibre.utils.icu import sort_key, capitalize
|
|||||||
from calibre.utils.config import prefs, tweaks
|
from calibre.utils.config import prefs, tweaks
|
||||||
from calibre.utils.magick.draw import identify_data
|
from calibre.utils.magick.draw import identify_data
|
||||||
from calibre.utils.date import qt_to_dt
|
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): # {{{
|
def get_cover_data(stream, ext): # {{{
|
||||||
from calibre.ebooks.metadata.meta import get_metadata
|
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_autonumber, do_remove_format, remove_format, do_swap_ta, \
|
||||||
do_remove_conv, do_auto_author, series, do_series_restart, \
|
do_remove_conv, do_auto_author, series, do_series_restart, \
|
||||||
series_start_value, do_title_case, cover_action, clear_series, \
|
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
|
# first loop: All changes that modify the filesystem and commit
|
||||||
# operation, because each operation modifies the file system. We want to
|
# immediately. We want to
|
||||||
# try hard to keep the DB and the file system in sync, even in the face
|
# try hard to keep the DB and the file system in sync, even in the face
|
||||||
# of exceptions or forced exits.
|
# of exceptions or forced exits.
|
||||||
if self.current_phase == 1:
|
if self.current_phase == 1:
|
||||||
@ -196,6 +199,27 @@ class MyBlockingBusy(QDialog): # {{{
|
|||||||
if covers:
|
if covers:
|
||||||
self.db.set_cover(id, covers[-1][0])
|
self.db.set_cover(id, covers[-1][0])
|
||||||
covers = []
|
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:
|
elif self.current_phase == 2:
|
||||||
# All of these just affect the DB, so we can tolerate a total rollback
|
# All of these just affect the DB, so we can tolerate a total rollback
|
||||||
if do_auto_author:
|
if do_auto_author:
|
||||||
@ -233,9 +257,6 @@ class MyBlockingBusy(QDialog): # {{{
|
|||||||
num = next if do_autonumber and series else 1.0
|
num = next if do_autonumber and series else 1.0
|
||||||
self.db.set_series_index(id, num, notify=False, commit=False)
|
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:
|
if do_remove_conv:
|
||||||
self.db.delete_conversion_options(id, 'PIPE', commit=False)
|
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_case = self.change_title_to_title_case.isChecked()
|
||||||
do_title_sort = self.update_title_sort.isChecked()
|
do_title_sort = self.update_title_sort.isChecked()
|
||||||
clear_languages = self.clear_languages.isChecked()
|
clear_languages = self.clear_languages.isChecked()
|
||||||
|
restore_original = self.restore_original.isChecked()
|
||||||
languages = self.languages.lang_codes
|
languages = self.languages.lang_codes
|
||||||
pubdate = adddate = None
|
pubdate = adddate = None
|
||||||
if self.apply_pubdate.isChecked():
|
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_autonumber, do_remove_format, remove_format, do_swap_ta,
|
||||||
do_remove_conv, do_auto_author, series, do_series_restart,
|
do_remove_conv, do_auto_author, series, do_series_restart,
|
||||||
series_start_value, do_title_case, cover_action, clear_series,
|
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}%%.')
|
bb = MyBlockingBusy(_('Applying changes to %d books.\nPhase {0} {1}%%.')
|
||||||
%len(self.ids), args, self.db, self.ids,
|
%len(self.ids), args, self.db, self.ids,
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<string>Edit Meta information</string>
|
<string>Edit Meta information</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowIcon">
|
<property name="windowIcon">
|
||||||
<iconset resource="../../../work/calibre/resources/images.qrc">
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
<normaloff>:/images/edit_input.png</normaloff>:/images/edit_input.png</iconset>
|
<normaloff>:/images/edit_input.png</normaloff>:/images/edit_input.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
@ -210,7 +210,7 @@
|
|||||||
<string>Open Tag Editor</string>
|
<string>Open Tag Editor</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../../../work/calibre/resources/images.qrc">
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
<normaloff>:/images/chapters.png</normaloff>:/images/chapters.png</iconset>
|
<normaloff>:/images/chapters.png</normaloff>:/images/chapters.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -381,7 +381,7 @@ from the value in the box</string>
|
|||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../../../work/calibre/resources/images.qrc">
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
<normaloff>:/images/trash.png</normaloff>:/images/trash.png</iconset>
|
<normaloff>:/images/trash.png</normaloff>:/images/trash.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -429,7 +429,7 @@ from the value in the box</string>
|
|||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../../../work/calibre/resources/images.qrc">
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
<normaloff>:/images/trash.png</normaloff>:/images/trash.png</iconset>
|
<normaloff>:/images/trash.png</normaloff>:/images/trash.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -443,7 +443,30 @@ from the value in the box</string>
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="13" column="0">
|
<item row="11" column="0">
|
||||||
|
<widget class="QLabel" name="label_11">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Languages:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>languages</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="1">
|
||||||
|
<widget class="LanguagesEdit" name="languages"/>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="2">
|
||||||
|
<widget class="QCheckBox" name="clear_languages">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove &all</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="12" column="0">
|
||||||
<widget class="QLabel" name="label_5">
|
<widget class="QLabel" name="label_5">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Remove &format:</string>
|
<string>Remove &format:</string>
|
||||||
@ -453,17 +476,44 @@ from the value in the box</string>
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="13" column="1">
|
<item row="12" column="1">
|
||||||
<widget class="QComboBox" name="remove_format">
|
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||||
<property name="maximumSize">
|
<item>
|
||||||
<size>
|
<widget class="QComboBox" name="remove_format">
|
||||||
<width>120</width>
|
<property name="maximumSize">
|
||||||
<height>16777215</height>
|
<size>
|
||||||
</size>
|
<width>120</width>
|
||||||
</property>
|
<height>16777215</height>
|
||||||
</widget>
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_4">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="restore_original">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>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.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Restore pre conversion &originals, if available</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="14" column="0">
|
<item row="13" column="0">
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
@ -479,7 +529,7 @@ from the value in the box</string>
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item row="15" column="0" colspan="3">
|
<item row="14" column="0" colspan="2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="change_title_to_title_case">
|
<widget class="QCheckBox" name="change_title_to_title_case">
|
||||||
@ -529,7 +579,7 @@ Future conversion of these books will use the default settings.</string>
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="16" column="0" colspan="3">
|
<item row="15" column="0" colspan="2">
|
||||||
<widget class="QGroupBox" name="groupBox">
|
<widget class="QGroupBox" name="groupBox">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Change &cover</string>
|
<string>Change &cover</string>
|
||||||
@ -559,7 +609,7 @@ Future conversion of these books will use the default settings.</string>
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="17" column="0">
|
<item row="16" column="0">
|
||||||
<spacer name="verticalSpacer_2">
|
<spacer name="verticalSpacer_2">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
@ -572,29 +622,6 @@ Future conversion of these books will use the default settings.</string>
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item row="11" column="0">
|
|
||||||
<widget class="QLabel" name="label_11">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Languages:</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>languages</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="11" column="1">
|
|
||||||
<widget class="LanguagesEdit" name="languages"/>
|
|
||||||
</item>
|
|
||||||
<item row="11" column="2">
|
|
||||||
<widget class="QCheckBox" name="clear_languages">
|
|
||||||
<property name="text">
|
|
||||||
<string>Remove &all</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab">
|
<widget class="QWidget" name="tab">
|
||||||
@ -1199,13 +1226,13 @@ not multiple and the destination field is multiple</string>
|
|||||||
<tabstop>languages</tabstop>
|
<tabstop>languages</tabstop>
|
||||||
<tabstop>clear_languages</tabstop>
|
<tabstop>clear_languages</tabstop>
|
||||||
<tabstop>remove_format</tabstop>
|
<tabstop>remove_format</tabstop>
|
||||||
|
<tabstop>restore_original</tabstop>
|
||||||
<tabstop>change_title_to_title_case</tabstop>
|
<tabstop>change_title_to_title_case</tabstop>
|
||||||
<tabstop>update_title_sort</tabstop>
|
<tabstop>update_title_sort</tabstop>
|
||||||
<tabstop>remove_conversion_settings</tabstop>
|
<tabstop>remove_conversion_settings</tabstop>
|
||||||
<tabstop>cover_generate</tabstop>
|
<tabstop>cover_generate</tabstop>
|
||||||
<tabstop>cover_remove</tabstop>
|
<tabstop>cover_remove</tabstop>
|
||||||
<tabstop>cover_from_fmt</tabstop>
|
<tabstop>cover_from_fmt</tabstop>
|
||||||
<tabstop>starting_from</tabstop>
|
|
||||||
<tabstop>multiple_separator</tabstop>
|
<tabstop>multiple_separator</tabstop>
|
||||||
<tabstop>test_text</tabstop>
|
<tabstop>test_text</tabstop>
|
||||||
<tabstop>test_result</tabstop>
|
<tabstop>test_result</tabstop>
|
||||||
@ -1229,9 +1256,10 @@ not multiple and the destination field is multiple</string>
|
|||||||
<tabstop>destination_field</tabstop>
|
<tabstop>destination_field</tabstop>
|
||||||
<tabstop>search_for</tabstop>
|
<tabstop>search_for</tabstop>
|
||||||
<tabstop>case_sensitive</tabstop>
|
<tabstop>case_sensitive</tabstop>
|
||||||
|
<tabstop>starting_from</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../work/calibre/resources/images.qrc"/>
|
<include location="../../../../resources/images.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user