mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement save/restore of last read position
This commit is contained in:
parent
2eefa97a1d
commit
742f322f89
@ -7,15 +7,22 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
||||
import os
|
||||
from threading import Thread
|
||||
|
||||
from PyQt5.Qt import QDockWidget, Qt, pyqtSignal
|
||||
from PyQt5.Qt import QDockWidget, Qt, QTimer, pyqtSignal
|
||||
|
||||
from calibre.gui2 import error_dialog
|
||||
from calibre.gui2.main_window import MainWindow
|
||||
from calibre.gui2.viewer.convert_book import prepare_book
|
||||
from calibre.gui2.viewer.web_view import WebView, set_book_path
|
||||
from calibre.utils.config import JSONConfig
|
||||
from calibre.utils.ipc.simple_worker import WorkerError
|
||||
|
||||
|
||||
def viewer_data():
|
||||
if not hasattr(viewer_data, 'ans'):
|
||||
viewer_data.ans = JSONConfig('viewer-data')
|
||||
return viewer_data.ans
|
||||
|
||||
|
||||
class EbookViewer(MainWindow):
|
||||
|
||||
msg_from_anotherinstance = pyqtSignal(object)
|
||||
@ -23,6 +30,9 @@ class EbookViewer(MainWindow):
|
||||
|
||||
def __init__(self):
|
||||
MainWindow.__init__(self, None)
|
||||
self.current_book_data = {}
|
||||
self.save_cfi_debounce_timer = t = QTimer(self)
|
||||
t.setInterval(2000), t.timeout.connect(self.save_cfi)
|
||||
self.book_prepared.connect(self.load_finished, type=Qt.QueuedConnection)
|
||||
|
||||
def create_dock(title, name, area, areas=Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea):
|
||||
@ -35,6 +45,7 @@ class EbookViewer(MainWindow):
|
||||
self.toc_dock = create_dock(_('Table of Contents'), 'toc-dock', Qt.LeftDockWidgetArea)
|
||||
self.inspector_dock = create_dock(_('Inspector'), 'inspector', Qt.RightDockWidgetArea)
|
||||
self.web_view = WebView(self)
|
||||
self.web_view.cfi_changed.connect(self.cfi_changed)
|
||||
self.setCentralWidget(self.web_view)
|
||||
|
||||
def handle_commandline_arg(self, arg):
|
||||
@ -51,6 +62,9 @@ class EbookViewer(MainWindow):
|
||||
|
||||
def load_ebook(self, pathtoebook, open_at=None):
|
||||
# TODO: Implement open_at
|
||||
if self.save_cfi_debounce_timer.isActive():
|
||||
self.save_cfi()
|
||||
self.current_book_data = {}
|
||||
t = Thread(name='LoadBook', target=self._load_ebook_worker, args=(pathtoebook, open_at))
|
||||
t.daemon = True
|
||||
t.start()
|
||||
@ -73,4 +87,29 @@ class EbookViewer(MainWindow):
|
||||
det_msg=data['tb'], show=True)
|
||||
return
|
||||
set_book_path(data['base'])
|
||||
self.web_view.start_book_load()
|
||||
self.current_book_data = data
|
||||
self.web_view.start_book_load(initial_cfi=self.initial_cfi_for_current_book())
|
||||
|
||||
def initial_cfi_for_current_book(self):
|
||||
vd = viewer_data()
|
||||
lrp = vd.get('last-read-positions', {})
|
||||
return lrp.get('path', {}).get(self.current_book_data['pathtoebook'])
|
||||
|
||||
def cfi_changed(self, cfi):
|
||||
if not self.current_book_data:
|
||||
return
|
||||
self.current_book_data['last_known_cfi'] = cfi
|
||||
self.save_cfi_debounce_timer.start()
|
||||
|
||||
def save_cfi(self):
|
||||
self.save_cfi_debounce_timer.stop()
|
||||
vd = viewer_data()
|
||||
lrp = vd.get('last-read-positions', {})
|
||||
path = lrp.setdefault('path', {})
|
||||
path[self.current_book_data['pathtoebook']] = self.current_book_data['last_known_cfi']
|
||||
vd.set('last-read-positions', lrp)
|
||||
|
||||
def closeEvent(self, ev):
|
||||
if self.save_cfi_debounce_timer.isActive():
|
||||
self.save_cfi()
|
||||
return MainWindow.closeEvent(self, ev)
|
||||
|
@ -7,7 +7,8 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
||||
import os
|
||||
|
||||
from PyQt5.Qt import (
|
||||
QApplication, QBuffer, QByteArray, QHBoxLayout, QSize, QTimer, QUrl, QWidget
|
||||
QApplication, QBuffer, QByteArray, QHBoxLayout, QSize, QTimer, QUrl, QWidget,
|
||||
pyqtSignal
|
||||
)
|
||||
from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler
|
||||
from PyQt5.QtWebEngineWidgets import (
|
||||
@ -238,6 +239,8 @@ class Inspector(QWidget):
|
||||
|
||||
class WebView(RestartingWebEngineView):
|
||||
|
||||
cfi_changed = pyqtSignal(object)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
self._host_widget = None
|
||||
self.current_cfi = None
|
||||
@ -266,6 +269,7 @@ class WebView(RestartingWebEngineView):
|
||||
cfi = frag[len('bookpos='):]
|
||||
if cfi:
|
||||
self.current_cfi = cfi
|
||||
self.cfi_changed.emit(cfi)
|
||||
|
||||
@property
|
||||
def host_widget(self):
|
||||
@ -305,12 +309,12 @@ class WebView(RestartingWebEngineView):
|
||||
for func, args in iteritems(self.pending_bridge_ready_actions):
|
||||
getattr(self.bridge, func)(*args)
|
||||
|
||||
def start_book_load(self):
|
||||
def start_book_load(self, initial_cfi=None):
|
||||
key = (set_book_path.path,)
|
||||
if self.bridge.ready:
|
||||
self.bridge.start_book_load(key, vprefs['session_data'])
|
||||
self.bridge.start_book_load(key, vprefs['session_data'], initial_cfi)
|
||||
else:
|
||||
self.pending_bridge_ready_actions['start_book_load'] = key, vprefs['session_data']
|
||||
self.pending_bridge_ready_actions['start_book_load'] = key, vprefs['session_data'], initial_cfi
|
||||
|
||||
def set_session_data(self, key, val):
|
||||
if key == '*' and val is None:
|
||||
|
@ -367,7 +367,7 @@ class View:
|
||||
cfi = '/' + rest
|
||||
return name, cfi
|
||||
|
||||
def display_book(self, book):
|
||||
def display_book(self, book, initial_cfi):
|
||||
self.hide_overlays()
|
||||
self.iframe.focus()
|
||||
is_current_book = self.book and self.book.key == book.key
|
||||
@ -383,7 +383,7 @@ class View:
|
||||
pos = {'replace_history':True}
|
||||
unkey = username_key(get_interface_data().username)
|
||||
name = book.manifest.spine[0]
|
||||
cfi = None
|
||||
cfi = initial_cfi or None
|
||||
q = parse_url_params()
|
||||
if q.bookpos and q.bookpos.startswith('epubcfi(/'):
|
||||
cfi = q.bookpos
|
||||
|
@ -126,7 +126,7 @@ def show_error(title, msg, details):
|
||||
error_dialog(title, msg, details)
|
||||
|
||||
|
||||
def manifest_received(key, end_type, xhr, ev):
|
||||
def manifest_received(key, initial_cfi, end_type, xhr, ev):
|
||||
nonlocal book
|
||||
if end_type is 'load':
|
||||
book = new_book(key, {})
|
||||
@ -137,7 +137,7 @@ def manifest_received(key, end_type, xhr, ev):
|
||||
book.is_complete = True
|
||||
v'delete book.manifest["metadata"]'
|
||||
v'delete book.manifest["last_read_positions"]'
|
||||
view.display_book(book)
|
||||
view.display_book(book, initial_cfi)
|
||||
else:
|
||||
error_dialog(_('Could not open book'), _(
|
||||
'Failed to load book manifest, click "Show details" for more info'),
|
||||
@ -177,12 +177,12 @@ def create_session_data(prefs):
|
||||
|
||||
|
||||
@from_python
|
||||
def start_book_load(key, prefs):
|
||||
def start_book_load(key, prefs, initial_cfi):
|
||||
nonlocal view
|
||||
if view is None:
|
||||
create_session_data(prefs)
|
||||
view = View(document.getElementById('view'))
|
||||
xhr = ajax('manifest', manifest_received.bind(None, key), ok_code=0)
|
||||
xhr = ajax('manifest', manifest_received.bind(None, key, initial_cfi), ok_code=0)
|
||||
xhr.responseType = 'json'
|
||||
xhr.send()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user