Ensure metadata.opf is always written when deleting book even if it is not sequenced for backup. Fixes #2017217 [Trash Bin not working](https://bugs.launchpad.net/calibre/+bug/2017217)

This commit is contained in:
Kovid Goyal 2023-04-21 17:13:38 +05:30
parent 46406ca9a7
commit f7fb20e431
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 23 additions and 9 deletions

View File

@ -1515,6 +1515,15 @@ class Cache:
return random.choice(tuple(self.dirtied_cache)) return random.choice(tuple(self.dirtied_cache))
return None return None
def _metadata_as_object_for_dump(self, book_id):
mi = self._get_metadata(book_id)
# Always set cover to cover.jpg. Even if cover doesn't exist,
# no harm done. This way no need to call dirtied when
# cover is set/removed
mi.cover = 'cover.jpg'
mi.all_annotations = self._all_annotations_for_book(book_id)
return mi
@read_api @read_api
def get_metadata_for_dump(self, book_id): def get_metadata_for_dump(self, book_id):
mi = None mi = None
@ -1527,12 +1536,7 @@ class Cache:
# While a book is being created, the path is empty. Don't bother to # While a book is being created, the path is empty. Don't bother to
# try to write the opf, because it will go to the wrong folder. # try to write the opf, because it will go to the wrong folder.
if self._field_for('path', book_id): if self._field_for('path', book_id):
mi = self._get_metadata(book_id) mi = self._metadata_as_object_for_dump(book_id)
# Always set cover to cover.jpg. Even if cover doesn't exist,
# no harm done. This way no need to call dirtied when
# cover is set/removed
mi.cover = 'cover.jpg'
mi.all_annotations = self._all_annotations_for_book(book_id)
except: except:
# This almost certainly means that the book has been deleted while # This almost certainly means that the book has been deleted while
# the backup operation sat in the queue. # the backup operation sat in the queue.
@ -2052,9 +2056,15 @@ class Cache:
except Exception: except Exception:
path = None path = None
path_map[book_id] = path path_map[book_id] = path
# ensure metadata.opf is written so we can restore the book if not permanent and path:
if not permanent: # ensure metadata.opf is written and up-to-date so we can restore the book
self._dump_metadata(book_ids=tuple(bid for bid, path in path_map.items() if path)) try:
mi = self._metadata_as_object_for_dump(book_id)
raw = metadata_to_opf(mi)
self.backend.write_backup(path, raw)
except Exception:
import traceback
traceback.print_exc()
self.backend.remove_books(path_map, permanent=permanent) self.backend.remove_books(path_map, permanent=permanent)
for field in itervalues(self.fields): for field in itervalues(self.fields):
try: try:

View File

@ -7,10 +7,12 @@ __docformat__ = 'restructuredtext en'
import glob import glob
import os import os
from contextlib import suppress
from datetime import timedelta from datetime import timedelta
from io import BytesIO from io import BytesIO
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
from calibre.db.backend import METADATA_FILE_NAME
from calibre.db.tests.base import IMG, BaseTest from calibre.db.tests.base import IMG, BaseTest
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.date import UNDEFINED_DATE, now, utcnow from calibre.utils.date import UNDEFINED_DATE, now, utcnow
@ -298,6 +300,8 @@ class AddRemoveTest(BaseTest):
fm_before = cache.format_metadata(1, 'FMT1', allow_cache=False), cache.format_metadata(1, 'FMT2', allow_cache=False) fm_before = cache.format_metadata(1, 'FMT1', allow_cache=False), cache.format_metadata(1, 'FMT2', allow_cache=False)
os.mkdir(os.path.join(bookpath, 'xyz')) os.mkdir(os.path.join(bookpath, 'xyz'))
open(os.path.join(bookpath, 'xyz', 'abc'), 'w').close() open(os.path.join(bookpath, 'xyz', 'abc'), 'w').close()
with suppress(FileNotFoundError):
os.remove(os.path.join(bookpath, METADATA_FILE_NAME))
cache.remove_books((1,)) cache.remove_books((1,))
cache.move_book_from_trash(1) cache.move_book_from_trash(1)
b, f = cache.list_trash_entries() b, f = cache.list_trash_entries()