%PDF- %PDF-
| Direktori : /proc/self/root/usr/lib/calibre/calibre/gui2/store/ |
| Current File : //proc/self/root/usr/lib/calibre/calibre/gui2/store/web_store.py |
#!/usr/bin/env python3
# License: GPL v3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>
import json
import os
import shutil
from qt.core import (
QApplication, QHBoxLayout, QIcon, QLabel, QProgressBar, QPushButton, QSize, QUrl,
QVBoxLayout, QWidget, pyqtSignal
)
from qt.webengine import QWebEngineProfile, QWebEngineView, QWebEngineDownloadItem
from calibre import random_user_agent, url_slash_cleaner
from calibre.constants import STORE_DIALOG_APP_UID, cache_dir, islinux, iswindows
from calibre.ebooks import BOOK_EXTENSIONS
from calibre.gui2 import (
Application, choose_save_file, error_dialog, gprefs, info_dialog, set_app_uid
)
from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.gui2.listener import send_message_in_process
from calibre.gui2.main_window import MainWindow
from calibre.ptempfile import PersistentTemporaryDirectory, reset_base_dir
from polyglot.binary import as_base64_bytes, from_base64_bytes
from polyglot.builtins import string_or_bytes
class DownloadItem(QWidget):
def __init__(self, download_id, filename, parent=None):
QWidget.__init__(self, parent)
self.l = l = QHBoxLayout(self)
self.la = la = QLabel(f'{filename}:\xa0')
la.setMaximumWidth(400)
l.addWidget(la)
self.pb = pb = QProgressBar(self)
pb.setRange(0, 0)
l.addWidget(pb)
self.download_id = download_id
def __call__(self, done, total):
self.pb.setRange(0, total)
self.pb.setValue(done)
class DownloadProgress(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setVisible(False)
self.l = QVBoxLayout(self)
self.items = {}
def add_item(self, download_id, filename):
self.setVisible(True)
item = DownloadItem(download_id, filename, self)
self.l.addWidget(item)
self.items[download_id] = item
def update_item(self, download_id, done, total):
item = self.items.get(download_id)
if item is not None:
item(done, total)
def remove_item(self, download_id):
item = self.items.pop(download_id, None)
if item is not None:
self.l.removeWidget(item)
item.setVisible(False)
item.setParent(None)
item.deleteLater()
if not self.items:
self.setVisible(False)
class Central(QWidget):
home = pyqtSignal()
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.l = l = QVBoxLayout(self)
self.view = v = QWebEngineView(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.download_progress = d = DownloadProgress(self)
h.addWidget(d)
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)
self.reload_button = b = QPushButton(_('Reload'))
b.clicked.connect(v.reload)
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.setWindowIcon(QIcon(I('store.png')))
self.setWindowTitle(data['window_title'])
self.download_data = {}
profile = QWebEngineProfile.defaultProfile()
profile.setCachePath(os.path.join(cache_dir(), 'web_store', 'hc'))
profile.setPersistentStoragePath(os.path.join(cache_dir(), 'web_store', 'ps'))
profile.setHttpUserAgent(random_user_agent(allow_ie=False))
profile.downloadRequested.connect(self.download_requested)
self.data = data
self.central = c = Central(self)
c.home.connect(self.go_home)
self.setCentralWidget(c)
geometry = gprefs.get('store_dialog_main_window_geometry')
if geometry is not None:
QApplication.instance().safe_restore_geometry(self, geometry)
self.go_to(data['detail_url'] or None)
def sizeHint(self):
return QSize(1024, 740)
def closeEvent(self, e):
gprefs.set('store_dialog_main_window_geometry', bytearray(self.saveGeometry()))
MainWindow.closeEvent(self, e)
@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(QUrl(url))
def download_requested(self, download_item):
path = download_item.path()
fname = os.path.basename(path)
download_id = download_item.id()
tdir = PersistentTemporaryDirectory()
self.download_data[download_id] = download_item
path = os.path.join(tdir, fname)
download_item.setPath(path)
connect_lambda(download_item.downloadProgress, self, lambda self, done, total: self.download_progress(download_id, done, total))
connect_lambda(download_item.finished, self, lambda self: self.download_finished(download_id))
download_item.accept()
self.central.download_progress.add_item(download_id, fname)
def download_progress(self, download_id, done, total):
self.central.download_progress.update_item(download_id, done, total)
def download_finished(self, download_id):
self.central.download_progress.remove_item(download_id)
download_item = self.download_data.pop(download_id)
path = download_item.path()
fname = os.path.basename(path)
if download_item.state() == QWebEngineDownloadItem.DownloadState.DownloadInterrupted:
error_dialog(self, _('Download failed'), _(
'Download of {0} failed with error: {1}').format(fname, download_item.interruptReasonString()), show=True)
return
ext = fname.rpartition('.')[-1].lower()
if ext not in BOOK_EXTENSIONS:
if ext == 'acsm':
if not confirm('<p>' + _(
'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 '
'<a href="https://www.adobe.com/solutions/ebook/digital-editions.html">'
'Adobe Digital Editions</a> (ADE).<p>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=fname)
if name:
shutil.copyfile(path, name)
os.remove(path)
return
tags = self.data['tags']
if isinstance(tags, string_or_bytes):
tags = list(filter(None, [x.strip() for x in tags.split(',')]))
data = json.dumps({'path': path, 'tags': tags})
if not isinstance(data, bytes):
data = data.encode('utf-8')
try:
send_message_in_process(b'web-store:' + data)
except Exception as err:
error_dialog(self, _('Could not contact calibre'), _(
'No running calibre instance found. Please start calibre before trying to'
' download books.'), det_msg=str(err), show=True)
return
info_dialog(self, _('Download completed'), _(
'Download of {0} has been completed, the book was added to'
' your calibre library').format(fname), show=True)
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(from_base64_bytes(data))
override = 'calibre-gui' if islinux else None
app = Application(args, override_program_name=override)
m = Main(data)
m.show(), m.raise_()
app.exec()
del m
del app
if __name__ == '__main__':
sample_data = as_base64_bytes(
json.dumps({
'window_title': 'MobileRead',
'base_url': 'https://www.mobileread.com/',
'detail_url': 'http://www.mobileread.com/forums/showthread.php?t=54477',
'id':1,
'tags': '',
})
)
main(['store-dialog', sample_data])