From 3a6ff39aef0d08211f4c8945f1db60f31b1aec97 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 11 Aug 2019 19:23:50 +0530 Subject: [PATCH 56/71] Speed up restoring original format by doing a rename rather than a copy and re-add. Fixes #1839733 [Restore pre conversion originals very slow](https://bugs.launchpad.net/calibre/+bug/1839733) --- src/calibre/db/backend.py | 10 ++++++++++ src/calibre/db/cache.py | 20 +++++++++++++------- src/calibre/db/tests/legacy.py | 14 -------------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/calibre/db/backend.py b/src/calibre/db/backend.py index 9e5ae66f1e..b7719d925f 100644 --- a/src/calibre/db/backend.py +++ b/src/calibre/db/backend.py @@ -1341,6 +1341,16 @@ class DB(object): def has_format(self, book_id, fmt, fname, path): return self.format_abspath(book_id, fmt, fname, path) is not None + def is_format_accessible(self, book_id, fmt, fname, path): + fpath = self.format_abspath(book_id, fmt, fname, path) + return fpath and os.access(fpath, os.R_OK | os.W_OK) + + def rename_format_file(self, book_id, src_fname, src_fmt, dest_fname, dest_fmt, path): + src_path = self.format_abspath(book_id, src_fmt, src_fname, path) + dest_path = self.format_abspath(book_id, dest_fmt, dest_fname, path) + atomic_rename(src_path, dest_path) + return os.path.getsize(dest_path) + def remove_formats(self, remove_map): paths = [] for book_id, removals in iteritems(remove_map): diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index e17787412c..11e02d083b 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -795,18 +795,24 @@ class Cache(object): nfmt = 'ORIGINAL_'+fmt return self.add_format(book_id, nfmt, fmtfile, run_hooks=False) - @api + @write_api def restore_original_format(self, book_id, original_fmt): ''' Restore the specified format from the previously saved ORIGINAL_FORMAT, if any. Return True on success. The ORIGINAL_FORMAT is deleted after a successful restore. ''' original_fmt = original_fmt.upper() - fmtfile = self.format(book_id, original_fmt, as_file=True) - if fmtfile is not None: - fmt = original_fmt.partition('_')[2] - with fmtfile: - self.add_format(book_id, fmt, fmtfile, run_hooks=False) - self.remove_formats({book_id:(original_fmt,)}) + fmt = original_fmt.partition('_')[2] + try: + ofmt_name = self.fields['formats'].format_fname(book_id, original_fmt) + path = self._field_for('path', book_id).replace('/', os.sep) + except Exception: + return False + if self.backend.is_format_accessible(book_id, original_fmt, ofmt_name, path): + self.add_format(book_id, fmt, BytesIO(), run_hooks=False) + fmt_name = self.fields['formats'].format_fname(book_id, fmt) + file_size = self.backend.rename_format_file(book_id, ofmt_name, original_fmt, fmt_name, fmt, path) + self.fields['formats'].table.update_fmt(book_id, fmt, fmt_name, file_size, self.backend) + self._remove_formats({book_id:(original_fmt,)}) return True return False diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index 6394c02fe1..bfe9cf61a3 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -793,20 +793,6 @@ class LegacyTest(BaseTest): self.assertEqual(ndb.new_api.field_for('#series_index', 1), 9) # }}} - def test_legacy_original_fmt(self): # {{{ - db, ndb = self.init_old(), self.init_legacy() - run_funcs(self, db, ndb, ( - ('original_fmt', 1, 'FMT1'), - ('save_original_format', 1, 'FMT1'), - ('original_fmt', 1, 'FMT1'), - ('restore_original_format', 1, 'ORIGINAL_FMT1'), - ('original_fmt', 1, 'FMT1'), - ('%formats', 1, True), - )) - db.close() - - # }}} - def test_legacy_saved_search(self): # {{{ ' Test legacy saved search API ' db, ndb = self.init_old(), self.init_legacy()