From 3a6ff39aef0d08211f4c8945f1db60f31b1aec97 Mon Sep 17 00:00:00 2001
From: Kovid Goyal <kovid@kovidgoyal.net>
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()