From ef3463aa356eb1a16d64ddd5c919f8d0d6a4074c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 27 Sep 2016 11:41:00 +0530 Subject: [PATCH] Windows: Run library restore ina separate process as on some windows machines, running it in the main process causes something in the system to lock the db file. --- src/calibre/gui2/dialogs/restore_library.py | 10 +++---- src/calibre/gui2/main.py | 33 +++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/calibre/gui2/dialogs/restore_library.py b/src/calibre/gui2/dialogs/restore_library.py index f3ae0fbaab..dbdeae8523 100644 --- a/src/calibre/gui2/dialogs/restore_library.py +++ b/src/calibre/gui2/dialogs/restore_library.py @@ -17,7 +17,7 @@ class DBRestore(QDialog): update_signal = pyqtSignal(object, object) - def __init__(self, parent, library_path): + def __init__(self, parent, library_path, wait_time=2): QDialog.__init__(self, parent) self.l = QVBoxLayout() self.setLayout(self.l) @@ -46,7 +46,7 @@ class DBRestore(QDialog): self.restorer.daemon = True # Give the metadata backup thread time to stop - QTimer.singleShot(2000, self.start) + QTimer.singleShot(wait_time * 1000, self.start) def start(self): self.restorer.start() @@ -117,14 +117,14 @@ def restore_database(db, parent=None): _show_success_msg(r, parent=parent) return True -def repair_library_at(library_path, parent=None): - d = DBRestore(parent, library_path) +def repair_library_at(library_path, parent=None, wait_time=2): + d = DBRestore(parent, library_path, wait_time=wait_time) d.exec_() if d.rejected: return False r = d.restorer if r.tb is not None: - error_dialog(parent, _('Failed'), + error_dialog(parent, _('Failed to repair library'), _('Restoring database failed, click Show details to see details'), det_msg=r.tb, show=True) return False diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index bc212fc636..0220a74352 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -151,6 +151,29 @@ def repair_library(library_path): from calibre.gui2.dialogs.restore_library import repair_library_at return repair_library_at(library_path) +def windows_repair(library_path=None): + from binascii import hexlify, unhexlify + import cPickle, subprocess + if library_path: + library_path = hexlify(cPickle.dumps(library_path, -1)) + winutil.prepare_for_restart() + os.environ['CALIBRE_REPAIR_CORRUPTED_DB'] = library_path + subprocess.Popen([sys.executable]) + else: + try: + app = Application([]) + from calibre.gui2.dialogs.restore_library import repair_library_at + library_path = cPickle.loads(unhexlify(os.environ.pop('CALIBRE_REPAIR_CORRUPTED_DB'))) + done = repair_library_at(library_path, wait_time=4) + except Exception: + done = False + error_dialog(None, _('Failed to repair library'), _( + 'Could not repair library. Click "Show details" for more information.'), det_msg=traceback.format_exc(), show=True) + if done: + subprocess.Popen([sys.executable]) + app.quit() + + class EventAccumulator(object): def __init__(self): @@ -266,6 +289,13 @@ class GuiRunner(QObject): det_msg=traceback.format_exc() ) if repair: + if iswindows: + # On some windows systems the existing db file gets locked + # by something when running restore from the main process. + # So run the restore in a separate process. + windows_repair(self.library_path) + self.app.quit() + return if repair_library(self.library_path): db = LibraryDatabase(self.library_path) except: @@ -455,6 +485,9 @@ def create_listener(): return Listener(address=gui_socket_address()) def main(args=sys.argv): + if iswindows and 'CALIBRE_REPAIR_CORRUPTED_DB' in os.environ: + windows_repair() + return 0 gui_debug = None if args[0] == '__CALIBRE_GUI_DEBUG__': gui_debug = args[1]