Start work on threaded search for browser viewer

This commit is contained in:
Kovid Goyal 2021-05-16 15:09:22 +05:30
parent ebd5711134
commit a6f1bea259
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 145 additions and 3 deletions

View File

@ -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):

View File

@ -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

View File

@ -0,0 +1,102 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
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}')

View File

@ -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)