From c8e78749eb261c664cb75d3e656adfa6d5daaab7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Mar 2019 13:15:49 +0530 Subject: [PATCH] Start work on migrating get books internal browser to web engine --- src/calibre/constants.py | 1 + src/calibre/gui2/store/web_control.py | 136 --------------------- src/calibre/gui2/store/web_store.py | 112 +++++++++++++++++ src/calibre/gui2/store/web_store_dialog.py | 69 ++++------- src/calibre/gui2/store/web_store_dialog.ui | 110 ----------------- src/calibre/gui_launch.py | 7 ++ src/calibre/utils/ipc/worker.py | 3 + 7 files changed, 147 insertions(+), 291 deletions(-) delete mode 100644 src/calibre/gui2/store/web_control.py create mode 100644 src/calibre/gui2/store/web_store.py delete mode 100644 src/calibre/gui2/store/web_store_dialog.ui diff --git a/src/calibre/constants.py b/src/calibre/constants.py index 4010761ff5..d8bbf2d2c2 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -43,6 +43,7 @@ FAKE_PROTOCOL, FAKE_HOST = 'clbr', 'internal.invalid' VIEWER_APP_UID = 'com.calibre-ebook.viewer' EDITOR_APP_UID = 'com.calibre-ebook.edit-book' MAIN_APP_UID = 'com.calibre-ebook.main-gui' +STORE_DIALOG_APP_UID = 'com.calibre-ebook.store-dialog' try: preferred_encoding = locale.getpreferredencoding() codecs.lookup(preferred_encoding) diff --git a/src/calibre/gui2/store/web_control.py b/src/calibre/gui2/store/web_control.py deleted file mode 100644 index 88c760e828..0000000000 --- a/src/calibre/gui2/store/web_control.py +++ /dev/null @@ -1,136 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, division, print_function, unicode_literals - - -__license__ = 'GPL 3' -__copyright__ = '2011, John Schember ' -__docformat__ = 'restructuredtext en' - -import os - -from PyQt5.Qt import QNetworkCookieJar, QNetworkProxy -from PyQt5.QtWebKitWidgets import QWebView, QWebPage - -from calibre import USER_AGENT, get_proxies -from calibre.ebooks import BOOK_EXTENSIONS -from calibre.gui2 import choose_save_file, NO_URL_FORMATTING -from calibre.gui2.ebook_download import show_download_info -from calibre.ptempfile import PersistentTemporaryFile -from calibre.utils.filenames import ascii_filename -from calibre.web import get_download_filename -from polyglot.builtins import unicode_type -from polyglot.urllib import urlparse - - -class NPWebView(QWebView): - - def __init__(self, *args): - QWebView.__init__(self, *args) - self.gui = None - self.tags = '' - self.create_browser = None - - self._page = NPWebPage() - self.setPage(self._page) - self.cookie_jar = QNetworkCookieJar() - self.page().networkAccessManager().setCookieJar(self.cookie_jar) - - http_proxy = get_proxies().get('http', None) - if http_proxy: - proxy_parts = urlparse(http_proxy) - proxy = QNetworkProxy() - proxy.setType(QNetworkProxy.HttpProxy) - if proxy_parts.username: - proxy.setUser(proxy_parts.username) - if proxy_parts.password: - proxy.setPassword(proxy_parts.password) - if proxy_parts.hostname: - proxy.setHostName(proxy_parts.hostname) - if proxy_parts.port: - proxy.setPort(proxy_parts.port) - self.page().networkAccessManager().setProxy(proxy) - - self.page().setForwardUnsupportedContent(True) - self.page().unsupportedContent.connect(self.start_download) - self.page().downloadRequested.connect(self.start_download) - self.page().networkAccessManager().sslErrors.connect(self.ignore_ssl_errors) - - def createWindow(self, type): - if type == QWebPage.WebBrowserWindow: - return self - else: - return None - - def set_gui(self, gui): - self.gui = gui - - def set_tags(self, tags): - self.tags = tags - - def start_download(self, request): - if not self.gui: - return - - url = unicode_type(request.url().toString(NO_URL_FORMATTING)) - cf = self.get_cookies() - - filename = get_download_filename(url, cf) - ext = os.path.splitext(filename)[1][1:].lower() - filename = ascii_filename(filename[:60] + '.' + ext) - if ext not in BOOK_EXTENSIONS: - if ext == 'acsm': - from calibre.gui2.dialogs.confirm_delete import confirm - if not confirm('

' + _('This e-book is a DRMed EPUB file. ' - 'You will be prompted to save this file to your ' - 'computer. Once it is saved, open it with ' - '' - 'Adobe Digital Editions (ADE).

ADE, in turn ' - 'will download the actual e-book, which will be a ' - '.epub file. You can add this book to calibre ' - 'using "Add Books" and selecting the file from ' - 'the ADE library folder.'), - 'acsm_download', self): - return - name = choose_save_file(self, 'web-store-download-unknown', _('File is not a supported e-book type. Save to disk?'), initial_filename=filename) - if name: - self.gui.download_ebook(url, cf, name, name, False, create_browser=self.create_browser) - else: - show_download_info(filename, self) - self.gui.download_ebook(url, cf, filename, tags=self.tags, create_browser=self.create_browser) - - def ignore_ssl_errors(self, reply, errors): - reply.ignoreSslErrors(errors) - - def get_cookies(self): - ''' - Writes QNetworkCookies to Mozilla cookie .txt file. - - :return: The file path to the cookie file. - ''' - cf = PersistentTemporaryFile(suffix='.txt') - - cf.write('# Netscape HTTP Cookie File\n\n') - - for c in self.page().networkAccessManager().cookieJar().allCookies(): - cookie = [] - domain = unicode_type(c.domain()) - - cookie.append(domain) - cookie.append('TRUE' if domain.startswith('.') else 'FALSE') - cookie.append(unicode_type(c.path())) - cookie.append('TRUE' if c.isSecure() else 'FALSE') - cookie.append(unicode_type(c.expirationDate().toTime_t())) - cookie.append(unicode_type(c.name())) - cookie.append(unicode_type(c.value())) - - cf.write('\t'.join(cookie)) - cf.write('\n') - - cf.close() - return cf.name - - -class NPWebPage(QWebPage): - - def userAgentForUrl(self, url): - return USER_AGENT diff --git a/src/calibre/gui2/store/web_store.py b/src/calibre/gui2/store/web_store.py new file mode 100644 index 0000000000..8c5ee5ddcd --- /dev/null +++ b/src/calibre/gui2/store/web_store.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python2 +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2019, Kovid Goyal + +from __future__ import absolute_import, division, print_function, unicode_literals + +import json +import os +from base64 import standard_b64decode, standard_b64encode + +from PyQt5.Qt import ( + QHBoxLayout, QProgressBar, QPushButton, QVBoxLayout, QWidget, pyqtSignal +) +from PyQt5.QtWebEngineWidgets import QWebEngineView + +from calibre import url_slash_cleaner +from calibre.constants import STORE_DIALOG_APP_UID, islinux, iswindows +from calibre.gui2 import Application, set_app_uid +from calibre.gui2.main_window import MainWindow +from calibre.ptempfile import reset_base_dir + + +class View(QWebEngineView): + pass + + +class Central(QWidget): + + home = pyqtSignal() + + def __init__(self, parent=None): + QWidget.__init__(self, parent) + self.l = l = QVBoxLayout(self) + self.view = v = View(self) + v.loadStarted.connect(self.load_started) + v.loadProgress.connect(self.load_progress) + v.loadFinished.connect(self.load_finished) + l.addWidget(v) + self.h = h = QHBoxLayout() + l.addLayout(h) + self.home_button = b = QPushButton(_('Home')) + b.clicked.connect(self.home) + h.addWidget(b) + self.back_button = b = QPushButton(_('Back')) + b.clicked.connect(v.back) + h.addWidget(b) + self.forward_button = b = QPushButton(_('Forward')) + b.clicked.connect(v.forward) + h.addWidget(b) + + self.progress_bar = b = QProgressBar(self) + h.addWidget(b) + + def load_started(self): + self.progress_bar.setValue(0) + + def load_progress(self, amt): + self.progress_bar.setValue(amt) + + def load_finished(self, ok): + self.progress_bar.setValue(100) + + +class Main(MainWindow): + + def __init__(self, data): + MainWindow.__init__(self, None) + self.data = data + self.central = c = Central(self) + c.home.connect(self.go_home) + self.setCentralWidget(c) + + @property + def view(self): + return self.central.view + + def go_home(self): + self.go_to() + + def go_to(self, url=None): + url = url or self.data['base_url'] + url = url_slash_cleaner(url) + self.view.load(url) + + +def main(args): + # Ensure we can continue to function if GUI is closed + os.environ.pop('CALIBRE_WORKER_TEMP_DIR', None) + reset_base_dir() + if iswindows: + # Ensure that all instances are grouped together in the task bar. This + # prevents them from being grouped with viewer/editor process when + # launched from within calibre, as both use calibre-parallel.exe + set_app_uid(STORE_DIALOG_APP_UID) + + data = args[-1] + data = json.loads(standard_b64decode(data)) + override = 'calibre-ebook-viewer' if islinux else None + app = Application(args, override_program_name=override) + app.exec_() + + +if __name__ == '__main__': + sample_data = standard_b64encode( + json.dumps({ + u'window_title': u'MobileRead', + u'base_url': u'https://www.mobileread.com/', + u'detail_url': u'http://www.mobileread.com/forums/showthread.php?t=54477', + u'tags': u'' + }) + ) + main([sample_data]) diff --git a/src/calibre/gui2/store/web_store_dialog.py b/src/calibre/gui2/store/web_store_dialog.py index 1b7cbffa42..f15c4ec62c 100644 --- a/src/calibre/gui2/store/web_store_dialog.py +++ b/src/calibre/gui2/store/web_store_dialog.py @@ -1,57 +1,36 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python2 +# vim:fileencoding=utf-8 +# License: GPLv3 Copyright: 2019, Kovid Goyal + from __future__ import absolute_import, division, print_function, unicode_literals - -__license__ = 'GPL 3' -__copyright__ = '2011, John Schember ' -__docformat__ = 'restructuredtext en' - -from PyQt5.Qt import QDialog, QUrl - -from calibre import url_slash_cleaner -from calibre.gui2.store.web_store_dialog_ui import Ui_Dialog +import json +from base64 import standard_b64encode -class WebStoreDialog(QDialog, Ui_Dialog): +class WebStoreDialog(object): def __init__(self, gui, base_url, parent=None, detail_url=None, create_browser=None): - QDialog.__init__(self, parent=parent) - self.setupUi(self) - self.gui = gui self.base_url = base_url + self.detail_url = detail_url + self.create_browser = create_browser + self.window_title = None + self.tags = None - self.view.set_gui(self.gui) - self.view.create_browser = create_browser - self.view.loadStarted.connect(self.load_started) - self.view.loadProgress.connect(self.load_progress) - self.view.loadFinished.connect(self.load_finished) - self.home.clicked.connect(self.go_home) - self.reload.clicked.connect(self.view.reload) - self.back.clicked.connect(self.view.back) - - self.go_home(detail_url=detail_url) + def setWindowTitle(self, title): + self.window_title = title def set_tags(self, tags): - self.view.set_tags(tags) + self.tags = tags - def load_started(self): - self.progress.setValue(0) - - def load_progress(self, val): - self.progress.setValue(val) - - def load_finished(self, ok=True): - self.progress.setValue(100) - - def go_home(self, checked=False, detail_url=None): - if detail_url: - url = detail_url - else: - url = self.base_url - - # Reduce redundant /'s because some stores - # (Feedbooks) and server frameworks (cherrypy) - # choke on them. - url = url_slash_cleaner(url) - self.view.load(QUrl(url)) + def exec_(self): + data = {'base_url': self.base_url, 'detail_url': self.detail_url, 'window_title': self.window_title, 'tags': self.tags} + data = json.dumps(data) + if not isinstance(data, bytes): + data = data.encode('utf-8') + data = standard_b64encode(data) + if isinstance(data, bytes): + data = data.decode('ascii') + args = ['store-dialog', data] + self.gui.job_manager.launch_gui_app(args[0], kwargs={'args':args}) diff --git a/src/calibre/gui2/store/web_store_dialog.ui b/src/calibre/gui2/store/web_store_dialog.ui deleted file mode 100644 index fbc09e0e5f..0000000000 --- a/src/calibre/gui2/store/web_store_dialog.ui +++ /dev/null @@ -1,110 +0,0 @@ - - - Dialog - - - - 0 - 0 - 962 - 656 - - - - - - - true - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - - - - about:blank - - - - - - - - - - - Home - - - - - - - Reload - - - - - - - 0 - - - %p% - - - - - - - Back - - - - - - - &Close - - - - - - - - NPWebView - QWidget -

calibre/gui2/store/web_control.h
- - - - - - close - clicked() - Dialog - accept() - - - 917 - 635 - - - 480 - 327 - - - - - diff --git a/src/calibre/gui_launch.py b/src/calibre/gui_launch.py index 8a29c5a4b8..f8b35ee2fa 100644 --- a/src/calibre/gui_launch.py +++ b/src/calibre/gui_launch.py @@ -81,6 +81,13 @@ def ebook_viewer(args=sys.argv): main(args) +def store_dialog(args=sys.argv): + detach_gui() + init_dbus() + from calibre.gui2.store.web_store import main + main(args) + + def gui_ebook_edit(path=None, notify=None): ' For launching the editor from inside calibre ' init_dbus() diff --git a/src/calibre/utils/ipc/worker.py b/src/calibre/utils/ipc/worker.py index 981b2d4f45..7c1b1c0f12 100644 --- a/src/calibre/utils/ipc/worker.py +++ b/src/calibre/utils/ipc/worker.py @@ -29,6 +29,9 @@ PARALLEL_FUNCS = { 'ebook-edit' : ('calibre.gui_launch', 'gui_ebook_edit', None), + 'store-dialog' : + ('calibre.gui_launch', 'store_dialog', None), + 'render_pages' : ('calibre.ebooks.comic.input', 'render_pages', 'notification'),