mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement restore database in the GUI
This commit is contained in:
parent
ae929cf12b
commit
ad33d230e6
@ -16,7 +16,6 @@ from calibre.utils.config import prefs
|
|||||||
from calibre.gui2 import gprefs, warning_dialog, Dispatcher, error_dialog, \
|
from calibre.gui2 import gprefs, warning_dialog, Dispatcher, error_dialog, \
|
||||||
question_dialog, info_dialog
|
question_dialog, info_dialog
|
||||||
from calibre.gui2.actions import InterfaceAction
|
from calibre.gui2.actions import InterfaceAction
|
||||||
from calibre.gui2.dialogs.check_library import CheckLibraryDialog, DBCheck
|
|
||||||
|
|
||||||
class LibraryUsageStats(object): # {{{
|
class LibraryUsageStats(object): # {{{
|
||||||
|
|
||||||
@ -139,6 +138,12 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
None, None), attr='action_check_library')
|
None, None), attr='action_check_library')
|
||||||
ac.triggered.connect(self.check_library, type=Qt.QueuedConnection)
|
ac.triggered.connect(self.check_library, type=Qt.QueuedConnection)
|
||||||
self.maintenance_menu.addAction(ac)
|
self.maintenance_menu.addAction(ac)
|
||||||
|
ac = self.create_action(spec=(_('Restore database'), 'lt.png',
|
||||||
|
None, None),
|
||||||
|
attr='action_restore_database')
|
||||||
|
ac.triggered.connect(self.restore_database, type=Qt.QueuedConnection)
|
||||||
|
self.maintenance_menu.addAction(ac)
|
||||||
|
|
||||||
self.choose_menu.addMenu(self.maintenance_menu)
|
self.choose_menu.addMenu(self.maintenance_menu)
|
||||||
|
|
||||||
def pick_random(self, *args):
|
def pick_random(self, *args):
|
||||||
@ -267,7 +272,17 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
_('Metadata will be backed up while calibre is running, at the '
|
_('Metadata will be backed up while calibre is running, at the '
|
||||||
'rate of approximately 1 book every three seconds.'), show=True)
|
'rate of approximately 1 book every three seconds.'), show=True)
|
||||||
|
|
||||||
|
def restore_database(self):
|
||||||
|
from calibre.gui2.dialogs.restore_library import restore_database
|
||||||
|
m = self.gui.library_view.model()
|
||||||
|
m.stop_metadata_backup()
|
||||||
|
db = m.db
|
||||||
|
db.prefs.disable_setting = True
|
||||||
|
if restore_database(db, self.gui):
|
||||||
|
self.gui.library_moved(db.library_path, call_close=False)
|
||||||
|
|
||||||
def check_library(self):
|
def check_library(self):
|
||||||
|
from calibre.gui2.dialogs.check_library import CheckLibraryDialog, DBCheck
|
||||||
self.gui.library_view.save_state()
|
self.gui.library_view.save_state()
|
||||||
m = self.gui.library_view.model()
|
m = self.gui.library_view.model()
|
||||||
m.stop_metadata_backup()
|
m.stop_metadata_backup()
|
||||||
|
115
src/calibre/gui2/dialogs/restore_library.py
Normal file
115
src/calibre/gui2/dialogs/restore_library.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from PyQt4.Qt import QDialog, QLabel, QVBoxLayout, QDialogButtonBox, \
|
||||||
|
QProgressBar, QSize, QTimer, pyqtSignal, Qt
|
||||||
|
|
||||||
|
from calibre.library.restore import Restore
|
||||||
|
from calibre.gui2 import error_dialog, question_dialog, warning_dialog, \
|
||||||
|
info_dialog
|
||||||
|
|
||||||
|
class DBRestore(QDialog):
|
||||||
|
|
||||||
|
update_signal = pyqtSignal(object, object)
|
||||||
|
|
||||||
|
def __init__(self, parent, library_path):
|
||||||
|
QDialog.__init__(self, parent)
|
||||||
|
self.l = QVBoxLayout()
|
||||||
|
self.setLayout(self.l)
|
||||||
|
self.l1 = QLabel('<b>'+_('Restoring database from backups, do not'
|
||||||
|
' interrupt, this will happen in two stages')+'...')
|
||||||
|
self.setWindowTitle(_('Restoring database'))
|
||||||
|
self.l.addWidget(self.l1)
|
||||||
|
self.pb = QProgressBar(self)
|
||||||
|
self.l.addWidget(self.pb)
|
||||||
|
self.pb.setMaximum(0)
|
||||||
|
self.pb.setMinimum(0)
|
||||||
|
self.msg = QLabel('')
|
||||||
|
self.l.addWidget(self.msg)
|
||||||
|
self.msg.setWordWrap(True)
|
||||||
|
self.bb = QDialogButtonBox(QDialogButtonBox.Cancel)
|
||||||
|
self.l.addWidget(self.bb)
|
||||||
|
self.bb.rejected.connect(self.reject)
|
||||||
|
self.resize(self.sizeHint() + QSize(100, 50))
|
||||||
|
self.error = None
|
||||||
|
self.rejected = False
|
||||||
|
self.library_path = library_path
|
||||||
|
self.update_signal.connect(self.do_update, type=Qt.QueuedConnection)
|
||||||
|
|
||||||
|
self.restorer = Restore(library_path, self)
|
||||||
|
self.restorer.daemon = True
|
||||||
|
|
||||||
|
# Give the metadata backup thread time to stop
|
||||||
|
QTimer.singleShot(2000, self.start)
|
||||||
|
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.restorer.start()
|
||||||
|
QTimer.singleShot(10, self.update)
|
||||||
|
|
||||||
|
def reject(self):
|
||||||
|
self.rejected = True
|
||||||
|
self.restorer.progress_callback = lambda x, y: x
|
||||||
|
QDialog.rejecet(self)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
if self.restorer.is_alive():
|
||||||
|
QTimer.singleShot(10, self.update)
|
||||||
|
else:
|
||||||
|
self.restorer.progress_callback = lambda x, y: x
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
def __call__(self, msg, step):
|
||||||
|
self.update_signal.emit(msg, step)
|
||||||
|
|
||||||
|
def do_update(self, msg, step):
|
||||||
|
if msg is None:
|
||||||
|
self.pb.setMaximum(step)
|
||||||
|
else:
|
||||||
|
self.msg.setText(msg)
|
||||||
|
self.pb.setValue(step)
|
||||||
|
|
||||||
|
|
||||||
|
def restore_database(db, parent=None):
|
||||||
|
if not question_dialog(parent, _('Are you sure?'), '<p>'+
|
||||||
|
_('Your list of books, with all their metadata is '
|
||||||
|
'stored in a single file, called a database. '
|
||||||
|
'In addition, metadata for each individual '
|
||||||
|
'book is stored in that books\' folder, as '
|
||||||
|
'a backup.'
|
||||||
|
'<p>This operation will rebuild '
|
||||||
|
'the database from the individual book '
|
||||||
|
'metadata. This is useful if the '
|
||||||
|
'database has been corrupted and you get a '
|
||||||
|
'blank list of books. Note that restoring only '
|
||||||
|
'restores books, not any settings stored in the '
|
||||||
|
'database, or any custom recipes.'
|
||||||
|
'<p>Do you want to restore the database?')):
|
||||||
|
return False
|
||||||
|
db.conn.close()
|
||||||
|
d = DBRestore(parent, db.library_path)
|
||||||
|
d.exec_()
|
||||||
|
r = d.restorer
|
||||||
|
d.restorer = None
|
||||||
|
if d.rejected:
|
||||||
|
return True
|
||||||
|
if r.tb is not None:
|
||||||
|
error_dialog(parent, _('Failed'),
|
||||||
|
_('Restoring database failed, click Show details to see details'),
|
||||||
|
det_msg=r.tb, show=True)
|
||||||
|
else:
|
||||||
|
if r.errors_occurred:
|
||||||
|
warning_dialog(parent, _('Success'),
|
||||||
|
_('Restoring the database succeeded with some warnings',
|
||||||
|
' click Show details to see the details.'),
|
||||||
|
det_msg=r.report, show=True)
|
||||||
|
else:
|
||||||
|
info_dialog(parent, _('Success'),
|
||||||
|
_('Restoring database was successful'), show=True,
|
||||||
|
show_copy_button=False)
|
||||||
|
return True
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user