diff --git a/src/pyj/read_book/db.pyj b/src/pyj/read_book/db.pyj index 75b5f87099..579d47cbd3 100644 --- a/src/pyj/read_book/db.pyj +++ b/src/pyj/read_book/db.pyj @@ -86,7 +86,7 @@ class DB: self.initialize_stage1() def show_error(self, title, msg, det_msg): - if is_reading_book(): + if not window? or is_reading_book(): self.show_read_book_error(title, msg, det_msg) else: error_dialog(title, msg, det_msg) @@ -347,6 +347,24 @@ class DB: proceed(result, name, mt, book) ) + def get_book_file(self, book_hash, stored_files, name, proceed): + stores = ['files'] + key = file_store_name({'book_hash': book_hash}, name) + transaction = self.idb.transaction(stores) + req = transaction.objectStore(stores[0]).get(key) + req.onsuccess = def(event): + if not req.result: + proceed({'ok': False, 'name': name, 'details': 'ENOENT'}) + return + fdata = stored_files[key] + mt = fdata.mimetype or 'application/octet-stream' + if fdata.encoded: + result = Blob([base64decode(result)], {'type':mt}) + proceed({'ok': True, 'result': result, 'name': name, 'mt': mt}) + req.onerror = def(event): + details = get_error_details(event) + proceed({'ok': False, 'name': name, 'details': details}) + def get_mathjax_files(self, proceed): c = self.idb.transaction('mathjax').objectStore('mathjax').openCursor() c.onerror = def(event): diff --git a/src/pyj/read_book/search.pyj b/src/pyj/read_book/search.pyj index ebc4faf74c..a5b874e5bb 100644 --- a/src/pyj/read_book/search.pyj +++ b/src/pyj/read_book/search.pyj @@ -3,14 +3,16 @@ from __python__ import bound_methods, hash_literals from elementmaker import E -from gettext import gettext as _ from book_list.theme import get_color from complete import create_search_bar from dom import add_extra_css, build_rule, svgicon +from gettext import gettext as _ from read_book.globals import ui_operations +from read_book.search_worker import worker_main from read_book.resources import text_from_serialized_html from read_book.shortcuts import shortcut_for_key_event +from worker import start_worker CLASS_NAME = 'book-search-container' @@ -42,6 +44,22 @@ class SearchOverlay: '\xa0', next_button, '\xa0', prev_button, '\xa0', close_button )) c.firstChild.addEventListener('keydown', self.onkeydown, {'passive': False}) + self._worker = None + self.request_counter = 0 + + @property + def worker(self): + if not self._worker: + self._worker = start_worker('read_book.search') + return self._worker + + def queue_search(self, book, current_name): + spine = book.manifest.spine + self.request_counter += 1 + self.worker.postMessage({ + 'type': 'search', 'book_hash': book.book_hash, 'spine': spine, 'current_name': current_name, + 'id': self.request_counter, 'stored_files': book.stored_files + }) def onkeydown(self, event): if event.key is 'Escape' or event.key is 'Esc': @@ -117,3 +135,6 @@ def find_in_spine(names, book, text, proceed): proceed(None) do_one() + + +main = worker_main diff --git a/src/pyj/read_book/search_worker.pyj b/src/pyj/read_book/search_worker.pyj new file mode 100644 index 0000000000..d80bfc403e --- /dev/null +++ b/src/pyj/read_book/search_worker.pyj @@ -0,0 +1,102 @@ +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2021, Kovid Goyal +from __python__ import bound_methods, hash_literals + +from read_book.db import DB + +GET_SPINE_FAILED = 1 +CONNECT_FAILED = 2 +UNHANDLED_ERROR = 3 +DB_ERROR = 4 + + +class Worker: + + def __init__(self): + self.db = None + self.connected_to_db = False + self.pending_search = None + self.searching = False + self.current_query = None + self.current_query_id = None + + @property + def initialize_error_msg(self): + return self.db?.initialize_error_msg + + +wc = Worker() + + +def send_search_complete(): + self.postMessage({'type': 'search_complete', 'id': wc.current_query_id}) + wc.current_query = wc.current_query_id = None + + +def queue_next_spine_item(spine_idx): + name = wc.current_query.spine[spine_idx] + if not name: + send_search_complete() + return + query = wc.current_query + wc.db.get_book_file(query.book_hash, query.stored_files, name, got_spine_item.bind(None, query.id, spine_idx)) + + +def got_spine_item(query_id, spine_idx, result): + if query_id is not wc.current_query_id: + return + if result.ok: + queue_next_spine_item(spine_idx + 1) + else: + if result.details is 'ENOENT': + queue_next_spine_item(spine_idx + 1) + else: + send_error(GET_SPINE_FAILED, result.details) + wc.current_query = wc.current_query_id = None + + +def perform_search(query): + wc.current_query = query + wc.current_query_id = query.id + if not query.spine?.length: + send_search_complete() + return + idx = query.spine.indexOf(query.current_name) + if idx < 0: + idx = 0 + queue_next_spine_item(idx) + + +def worker_connection_done(): + wc.connected_to_db = True + if not wc.initialize_error_msg: + if wc.pending_search: + s = wc.pending_search + wc.pending_search = None + perform_search(s) + + +def send_error(code, msg, error): + self.postMessage({'type': 'error', 'code': code, 'msg': msg, 'id': wc.current_query_id, 'error': error}) + + +def on_worker_db_error(title, msg, details): + send_error(DB_ERROR, msg, {'title': title, 'msg': msg, 'details': details}) + + +def worker_main(): + wc.db = DB(worker_connection_done, on_worker_db_error) + + self.onmessage = def(e): + if e.data.type is 'search': + wc.current_query_id = e.data.id + if wc.connected_to_db: + if wc.initialize_error_msg: + send_error(CONNECT_FAILED, wc.initialize_error_msg) + else: + perform_search(e.data) + else: + wc.pending_search = e.data + + self.onerror = def(e): + send_error(UNHANDLED_ERROR, f'{e.line}:{e.message}') diff --git a/src/pyj/srv.pyj b/src/pyj/srv.pyj index 845f745b52..b54bc77196 100644 --- a/src/pyj/srv.pyj +++ b/src/pyj/srv.pyj @@ -14,7 +14,7 @@ AUTO_UPDATE_THRESHOLD = 1000 # millisecs def worker_main(entry_point): - m = ρσ_get_module(window.iframe_entry_point) + m = ρσ_get_module(entry_point) main = m?.main if main: main() @@ -69,3 +69,4 @@ if document?: toplevel_main() elif self?: entry_point = self.location.hash[1:] + worker_main(entry_point)