Cleanup previous PR

1) Use the passed in db object
2) Delete the temp dir after generating the catalog
3) Use the correct source dir when copying
4) Move copying logic into backend
5) Hold the db read lock while copying
This commit is contained in:
Kovid Goyal 2025-03-09 19:49:55 +05:30
parent fe1aea2938
commit 4915282ba1
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 46 additions and 35 deletions

View File

@ -1456,6 +1456,15 @@ class DB:
self.user_version = 1 self.user_version = 1
# }}} # }}}
def clone_for_readonly_access(self, dest_dir: str) -> str:
dbpath = os.path.abspath(self.conn.db_filename('main'))
clone_db_path = os.path.join(dest_dir, os.path.basename(dbpath))
shutil.copy2(dbpath, clone_db_path)
notes_dir = os.path.join(os.path.dirname(dbpath), NOTES_DIR_NAME)
if os.path.exists(notes_dir):
shutil.copytree(notes_dir, os.path.join(dest_dir, NOTES_DIR_NAME))
return clone_db_path
def normpath(self, path): def normpath(self, path):
path = os.path.abspath(os.path.realpath(path)) path = os.path.abspath(os.path.realpath(path))
if not self.is_case_sensitive: if not self.is_case_sensitive:

View File

@ -3513,6 +3513,10 @@ class Cache:
dest_value.extend(src_value) dest_value.extend(src_value)
self._set_field(field, {dest_id: dest_value}) self._set_field(field, {dest_id: dest_value})
@read_api
def clone_for_readonly_access(self, dest_dir: str) -> str:
return self.backend.clone_for_readonly_access(dest_dir)
def import_library(library_key, importer, library_path, progress=None, abort=None): def import_library(library_key, importer, library_path, progress=None, abort=None):
from calibre.db.backend import DB from calibre.db.backend import DB

View File

@ -41,7 +41,7 @@ def gui_convert_override(input, output, recommendations, notification=DummyRepor
def gui_catalog(library_path, temp_db_path, fmt, title, dbspec, ids, out_file_name, sync, fmt_options, connected_device, def gui_catalog(library_path, temp_db_path, fmt, title, dbspec, ids, out_file_name, sync, fmt_options, connected_device,
notification=DummyReporter(), log=None): notification=DummyReporter(), log=None):
if log is None: if log is None:
log = Log() log = Log()
from calibre.utils.config import prefs from calibre.utils.config import prefs
@ -50,31 +50,36 @@ def gui_catalog(library_path, temp_db_path, fmt, title, dbspec, ids, out_file_na
# Open the temp database created while still in the GUI thread # Open the temp database created while still in the GUI thread
from calibre.db.legacy import LibraryDatabase from calibre.db.legacy import LibraryDatabase
db = LibraryDatabase(library_path, temp_db_path=temp_db_path) db = LibraryDatabase(library_path, temp_db_path=temp_db_path)
db.catalog_plugin_on_device_temp_mapping = dbspec try:
db.catalog_plugin_on_device_temp_mapping = dbspec
# Create a minimal OptionParser that we can append to # Create a minimal OptionParser that we can append to
parser = OptionParser() parser = OptionParser()
args = [] args = []
parser.add_option('--verbose', action='store_true', dest='verbose', default=True) parser.add_option('--verbose', action='store_true', dest='verbose', default=True)
opts, args = parser.parse_args() opts, args = parser.parse_args()
# Populate opts # Populate opts
# opts.gui_search_text = something # opts.gui_search_text = something
opts.catalog_title = title opts.catalog_title = title
opts.connected_device = connected_device opts.connected_device = connected_device
opts.ids = ids opts.ids = ids
opts.search_text = None opts.search_text = None
opts.sort_by = None opts.sort_by = None
opts.sync = sync opts.sync = sync
# Extract the option dictionary to comma-separated lists # Extract the option dictionary to comma-separated lists
for option in fmt_options: for option in fmt_options:
if isinstance(fmt_options[option],list): if isinstance(fmt_options[option],list):
setattr(opts, option, ','.join(fmt_options[option])) setattr(opts, option, ','.join(fmt_options[option]))
else: else:
setattr(opts, option, fmt_options[option]) setattr(opts, option, fmt_options[option])
# Fetch and run the plugin for fmt # Fetch and run the plugin for fmt
# Returns 0 if successful, 1 if no catalog built # Returns 0 if successful, 1 if no catalog built
plugin = plugin_for_catalog_format(fmt) plugin = plugin_for_catalog_format(fmt)
return plugin.run(out_file_name, opts, db, notification=notification) return plugin.run(out_file_name, opts, db, notification=notification)
finally:
import shutil
db.close()
shutil.rmtree(os.path.dirname(temp_db_path))

View File

@ -10,7 +10,6 @@ Logic for setting up conversion jobs
''' '''
import os import os
import shutil
from qt.core import QDialog, QProgressDialog, QTimer from qt.core import QDialog, QProgressDialog, QTimer
@ -376,18 +375,12 @@ def generate_catalog(parent, dbspec, ids, device_manager, db): # {{{
# process. The backend looks for the notes directory (.calnotes) in the # process. The backend looks for the notes directory (.calnotes) in the
# directory containing the metadata.db file. Copy the one from the current # directory containing the metadata.db file. Copy the one from the current
# library. # library.
from calibre.gui2.ui import get_gui temp_db_directory = PersistentTemporaryDirectory('callib')
library_path = get_gui().current_db.library_path temp_db_path = db.new_api.clone_for_readonly_access(temp_db_directory)
temp_db_directory = PersistentTemporaryDirectory('_callib')
temp_db_path = os.path.join(temp_db_directory, 'metadata.db')
shutil.copy(os.path.join(library_path, 'metadata.db'), temp_db_directory)
notes_dir = os.path.join(library_path, '.calnotes')
if os.path.exists(notes_dir):
shutil.copytree(notes_dir, os.path.join(temp_db_directory, '.calnotes'))
# These args are passed inline to gui2.convert.gui_conversion:gui_catalog # These args are passed inline to gui2.convert.gui_conversion:gui_catalog
args = [ args = [
library_path, db.library_path,
temp_db_path, temp_db_path,
d.catalog_format, d.catalog_format,
d.catalog_title, d.catalog_title,