diff --git a/src/calibre/gui2/viewer/convert_book.py b/src/calibre/gui2/viewer/convert_book.py index fc1885d496..2b53e09653 100644 --- a/src/calibre/gui2/viewer/convert_book.py +++ b/src/calibre/gui2/viewer/convert_book.py @@ -122,7 +122,7 @@ def save_metadata(metadata, f): f.seek(0), f.truncate(), f.write(as_bytes(json.dumps(metadata, indent=2))) -def prepare_book(path, convert_func=do_convert, max_age=30 * DAY): +def prepare_book(path, convert_func=do_convert, max_age=30 * DAY, force=False): st = os.stat(path) key = book_hash(path, st.st_size, st.st_mtime) finished_path = safe_makedirs(os.path.join(book_cache_dir(), 'f')) @@ -135,11 +135,15 @@ def prepare_book(path, convert_func=do_convert, max_age=30 * DAY): metadata = {'entries': {}, 'last_clear_at': 0} entries = metadata['entries'] instances = entries.setdefault(key, []) - for instance in instances: + for instance in tuple(instances): if instance['status'] == 'finished': - instance['atime'] = time.time() - save_metadata(metadata, f) - return os.path.join(finished_path, instance['path']) + if force: + robust_rmtree(os.path.join(finished_path, instance['path'])) + instances.remove(instance) + else: + instance['atime'] = time.time() + save_metadata(metadata, f) + return os.path.join(finished_path, instance['path']) instance = prepare_convert(temp_path, key, st) instances.append(instance) save_metadata(metadata, f) @@ -234,6 +238,12 @@ def find_tests(): third_path = prepare_book(book_src, convert_func=convert_mock) self.assertNotEqual(path, third_path) + # Test force reload + fourth_path = prepare_book(book_src, convert_func=convert_mock) + self.ae(third_path, fourth_path) + fourth_path = prepare_book(book_src, convert_func=convert_mock, force=True) + self.assertNotEqual(third_path, fourth_path) + # Test cache expiry open(book_src, 'wb').write(b'bcd') prepare_book(book_src, convert_func=convert_mock, max_age=-1000) diff --git a/src/calibre/gui2/viewer/ui.py b/src/calibre/gui2/viewer/ui.py index 5d5711bc6d..b3b9b9e006 100644 --- a/src/calibre/gui2/viewer/ui.py +++ b/src/calibre/gui2/viewer/ui.py @@ -59,6 +59,7 @@ class EbookViewer(MainWindow): self.inspector_dock = create_dock(_('Inspector'), 'inspector', Qt.RightDockWidgetArea) self.web_view = WebView(self) self.web_view.cfi_changed.connect(self.cfi_changed) + self.web_view.reload_book.connect(self.reload_book) self.setCentralWidget(self.web_view) def handle_commandline_arg(self, arg): @@ -76,18 +77,22 @@ class EbookViewer(MainWindow): self.load_ebook(path, open_at=open_at) self.raise_() - def load_ebook(self, pathtoebook, open_at=None): + def load_ebook(self, pathtoebook, open_at=None, reload_book=False): # TODO: Implement open_at self.web_view.show_preparing_message() self.save_annotations() self.current_book_data = {} - t = Thread(name='LoadBook', target=self._load_ebook_worker, args=(pathtoebook, open_at)) + t = Thread(name='LoadBook', target=self._load_ebook_worker, args=(pathtoebook, open_at, reload_book)) t.daemon = True t.start() - def _load_ebook_worker(self, pathtoebook, open_at): + def reload_book(self): + if self.current_book_data: + self.load_ebook(self.current_book_data['pathtoebook'], reload_book=True) + + def _load_ebook_worker(self, pathtoebook, open_at, reload_book): try: - ans = prepare_book(pathtoebook) + ans = prepare_book(pathtoebook, force=reload_book) except WorkerError as e: self.book_prepared.emit(False, {'exception': e, 'tb': e.orig_tb, 'pathtoebook': pathtoebook}) except Exception as e: diff --git a/src/calibre/gui2/viewer/web_view.py b/src/calibre/gui2/viewer/web_view.py index dfd8df0885..0bb0f1b638 100644 --- a/src/calibre/gui2/viewer/web_view.py +++ b/src/calibre/gui2/viewer/web_view.py @@ -167,6 +167,7 @@ def create_profile(): class ViewerBridge(Bridge): set_session_data = from_js(object, object) + reload_book = from_js() create_view = to_js() show_preparing_message = to_js() @@ -244,6 +245,7 @@ class Inspector(QWidget): class WebView(RestartingWebEngineView): cfi_changed = pyqtSignal(object) + reload_book = pyqtSignal() def __init__(self, parent=None): self._host_widget = None @@ -256,6 +258,7 @@ class WebView(RestartingWebEngineView): self._page = WebPage(self) self.bridge.bridge_ready.connect(self.on_bridge_ready) self.bridge.set_session_data.connect(self.set_session_data) + self.bridge.reload_book.connect(self.reload_book) self.pending_bridge_ready_actions = {} self.setPage(self._page) self.setAcceptDrops(False) diff --git a/src/pyj/read_book/globals.pyj b/src/pyj/read_book/globals.pyj index 2a59096a72..d52c1b7fa3 100644 --- a/src/pyj/read_book/globals.pyj +++ b/src/pyj/read_book/globals.pyj @@ -72,4 +72,6 @@ ui_operations = { 'update_url_state': None, 'update_last_read_time': None, 'show_error': None, + 'redisplay_book': None, + 'reload_book': None, } diff --git a/src/pyj/read_book/overlay.pyj b/src/pyj/read_book/overlay.pyj index 7da105ff40..e1329dae34 100644 --- a/src/pyj/read_book/overlay.pyj +++ b/src/pyj/read_book/overlay.pyj @@ -5,19 +5,21 @@ from __python__ import bound_methods, hash_literals from elementmaker import E from gettext import gettext as _ -from book_list.globals import get_read_ui from book_list.library_data import sync_library_books from book_list.router import home from book_list.theme import get_color from dom import add_extra_css, build_rule, clear, set_css, svgicon, unique_id from modals import error_dialog -from utils import full_screen_element, request_full_screen, safe_set_inner_html, is_ios +from read_book.globals import ui_operations, runtime from read_book.goto import create_goto_panel from read_book.prefs.font_size import create_font_size_panel from read_book.prefs.main import create_prefs_panel from read_book.toc import create_toc_panel from read_book.word_actions import create_word_actions_panel from session import get_device_uuid +from utils import ( + full_screen_element, is_ios, request_full_screen, safe_set_inner_html +) from widgets import create_button, create_spinner @@ -86,6 +88,10 @@ class DeleteBook: # {{{ pass # Dont allow panel to be closed by a click def delete_book(self): + if runtime.is_standalone_viewer: + if self.reload_book: + ui_operations.reload_book() + return self.show_working() view = self.overlay.view view.ui.db.delete_book(view.book, def(book, errmsg): @@ -94,7 +100,7 @@ class DeleteBook: # {{{ view.ui.show_error(_('Failed to delete book'), _('Failed to delete book from local storage, click "Show details" for more information.'), errmsg) else: if self.reload_book: - get_read_ui().reload_book() + ui_operations.reload_book() else: home() ) @@ -207,6 +213,14 @@ class MainOverlay: return E.li(icon, '\xa0', text, onclick=action, title=tooltip) + sync_action = ac(_('Sync'), _('Get last read position and annotations from the server'), self.overlay.sync_book, 'cloud-download') + delete_action = ac(_('Delete'), _('Delete this book from the device'), self.overlay.delete_book, 'trash') + reload_action = ac(_('Reload'), _('Reload this book from the {}').format( _('computer') if runtime.is_standalone_viewer else _('server')), self.overlay.reload_book, 'refresh') + if runtime.is_standalone_viewer: + reload_actions = E.ul(reload_action) + else: + reload_actions = E.ul(sync_action, delete_action, reload_action) + actions_div = E.div( # actions E.ul( ac(_('Home'), _('Return to list of books'), def(): home();, 'home'), @@ -219,11 +233,7 @@ class MainOverlay: ac(_('Go to'), _('Go to a specific location in the book'), self.overlay.show_goto, 'chevron-right'), ), - E.ul( - ac(_('Sync'), _('Get last read position and annotations from the server'), self.overlay.sync_book, 'cloud-download'), - ac(_('Delete'), _('Delete this book from the device'), self.overlay.delete_book, 'trash'), - ac(_('Reload'), _('Reload this book from the server'), self.overlay.reload_book, 'refresh') - ), + reload_actions, E.ul( ac(_('Table of Contents'), None, self.overlay.show_toc, 'TC', True), @@ -350,7 +360,7 @@ class PrefsOverlay: # {{{ def on_hide(self): if self.changes_occurred: self.changes_occurred = False - get_read_ui().redisplay_book() + ui_operations.redisplay_book() # }}} diff --git a/src/pyj/read_book/ui.pyj b/src/pyj/read_book/ui.pyj index 009b3e85eb..b355ccd57a 100644 --- a/src/pyj/read_book/ui.pyj +++ b/src/pyj/read_book/ui.pyj @@ -60,6 +60,8 @@ class ReadUI: ui_operations.update_url_state = self.update_url_state.bind(self) ui_operations.update_last_read_time = self.db.update_last_read_time ui_operations.show_error = self.show_error.bind(self) + ui_operations.redisplay_book = self.redisplay_book.bind(self) + ui_operations.reload_book = self.reload_book.bind(self) def on_resize(self): self.view.on_resize() diff --git a/src/pyj/viewer-main.pyj b/src/pyj/viewer-main.pyj index 30a0249757..502ecfecf0 100644 --- a/src/pyj/viewer-main.pyj +++ b/src/pyj/viewer-main.pyj @@ -206,12 +206,22 @@ def onerror(msg, script_url, line_number, column_number, error_object): return True +def redisplay_book(): + view.redisplay_book() + + +def reload_book(): + to_python.reload_book() + + if window is window.top: # main ui_operations.get_file = get_file ui_operations.get_mathjax_files = get_mathjax_files ui_operations.update_url_state = update_url_state ui_operations.show_error = show_error + ui_operations.redisplay_book = redisplay_book + ui_operations.reload_book = reload_book document.body.appendChild(E.div(id='view')) window.onerror = onerror create_modal_container()