From 32c3ea445c9d2ff29b22488e5aae58bb01cd0e11 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 7 Apr 2025 06:13:06 +0530 Subject: [PATCH] Preload viewer conversion worker for a slight speedup on first open --- src/calibre/gui2/viewer/convert_book.py | 42 ++++++++++++++++--------- src/calibre/gui2/viewer/main.py | 2 ++ src/calibre/gui2/viewer/ui.py | 3 +- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/calibre/gui2/viewer/convert_book.py b/src/calibre/gui2/viewer/convert_book.py index fd737af3a8..e581779421 100644 --- a/src/calibre/gui2/viewer/convert_book.py +++ b/src/calibre/gui2/viewer/convert_book.py @@ -2,6 +2,7 @@ # License: GPL v3 Copyright: 2018, Kovid Goyal +import atexit import errno import json import os @@ -12,7 +13,6 @@ from itertools import count from calibre import walk from calibre.constants import cache_dir, iswindows -from calibre.ptempfile import TemporaryFile from calibre.srv.render_book import RENDER_VERSION from calibre.utils.filenames import rmtree from calibre.utils.ipc.simple_worker import start_pipe_worker @@ -163,32 +163,46 @@ class ConversionFailure(ValueError): self, f'Failed to convert book: {book_path} with error:\n{worker_output}') +preloaded_worker = None running_workers = [] +def create_worker(): + import subprocess + return start_pipe_worker('from calibre.srv.render_book import viewer_main; viewer_main()', stderr=subprocess.STDOUT) + + def clean_running_workers(): + global preloaded_worker + if preloaded_worker is not None: + preloaded_worker.kill() + preloaded_worker = None for p in running_workers: if p.poll() is None: p.kill() del running_workers[:] +def initialize_worker(): + global preloaded_worker + if preloaded_worker is None: + preloaded_worker = create_worker() + atexit.register(clean_running_workers) + + def do_convert(path, temp_path, key, instance): + global preloaded_worker tdir = os.path.join(temp_path, instance['path']) - p = None + p = preloaded_worker or create_worker() + preloaded_worker = create_worker() + running_workers.append(p) + data = msgpack_dumps(( + path, tdir, {'size': instance['file_size'], 'mtime': instance['file_mtime'], 'hash': key}, + )) try: - with TemporaryFile('log.txt') as logpath: - with open(logpath, 'w+b') as logf: - p = start_pipe_worker('from calibre.srv.render_book import viewer_main; viewer_main()', stdout=logf, stderr=logf) - running_workers.append(p) - p.stdin.write(msgpack_dumps(( - path, tdir, {'size': instance['file_size'], 'mtime': instance['file_mtime'], 'hash': key}, - ))) - p.stdin.close() - if p.wait() != 0: - with open(logpath, 'rb') as logf: - worker_output = logf.read().decode('utf-8', 'replace') - raise ConversionFailure(path, worker_output) + stdout, _ = p.communicate(data) + if p.returncode != 0: + raise ConversionFailure(path, stdout.decode('utf-8', 'replace')) finally: try: running_workers.remove(p) diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index 555d3e99c8..856264570d 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -146,6 +146,8 @@ View an e-book. def run_gui(app, opts, args, internal_book_data, listener=None): + from calibre.gui2.viewer.convert_book import initialize_worker + initialize_worker() acc = EventAccumulator(app) app.file_event_hook = acc app.load_builtin_fonts() diff --git a/src/calibre/gui2/viewer/ui.py b/src/calibre/gui2/viewer/ui.py index 8c8e5e677f..069d9b7c55 100644 --- a/src/calibre/gui2/viewer/ui.py +++ b/src/calibre/gui2/viewer/ui.py @@ -42,7 +42,7 @@ from calibre.gui2.viewer import get_boss, get_current_book_data, performance_mon from calibre.gui2.viewer.annotations import AnnotationsSaveWorker, annotations_dir, parse_annotations from calibre.gui2.viewer.bookmarks import BookmarkManager from calibre.gui2.viewer.config import get_session_pref, load_reading_rates, save_reading_rates, vprefs -from calibre.gui2.viewer.convert_book import clean_running_workers, prepare_book +from calibre.gui2.viewer.convert_book import prepare_book from calibre.gui2.viewer.highlights import HighlightsPanel from calibre.gui2.viewer.integration import get_book_library_details, load_annotations_map_from_library from calibre.gui2.viewer.lookup import Lookup @@ -837,7 +837,6 @@ class EbookViewer(MainWindow): except Exception: import traceback traceback.print_exc() - clean_running_workers() self.shutdown_done = True return MainWindow.closeEvent(self, ev) # }}}