| Server IP : 35.80.110.71 / Your IP : 216.73.216.221 Web Server : Apache/2.4.58 (Ubuntu) System : Linux ip-172-31-21-44 6.17.0-1019-aws #19~24.04.1-Ubuntu SMP Tue Jun 23 18:53:06 UTC 2026 x86_64 User : ubuntu ( 1000) PHP Version : 8.3.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /lib/calibre/calibre/gui2/store/ |
Upload File : |
#!/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 (
QWebEngineDownloadRequest, QWebEnginePage, QWebEngineProfile, QWebEngineView,
)
from calibre import random_user_agent, url_slash_cleaner
from calibre.constants import STORE_DIALOG_APP_UID, 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 calibre.startup import connect_lambda
from calibre.utils.webengine import setup_profile
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)
def create_profile():
ans = getattr(create_profile, 'ans', None)
if ans is None:
ans = create_profile.ans = setup_profile(QWebEngineProfile('web_store', QApplication.instance()))
ans.setHttpUserAgent(random_user_agent(allow_ie=False))
return ans
class Central(QWidget):
home = pyqtSignal()
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.l = l = QVBoxLayout(self)
self.view = v = QWebEngineView(self)
self._page = QWebEnginePage(create_profile(), v)
v.setPage(self._page)
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)
@property
def profile(self):
return self.view.page().profile()
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.ic('store.png'))
self.setWindowTitle(data['window_title'])
self.download_data = {}
self.data = data
self.central = c = Central(self)
c.home.connect(self.go_home)
c.profile.downloadRequested.connect(self.download_requested)
self.setCentralWidget(c)
self.restore_geometry(gprefs, 'store_dialog_main_window_geometry')
self.go_to(data['detail_url'] or None)
def sizeHint(self):
return QSize(1024, 740)
def closeEvent(self, e):
self.save_geometry(gprefs, 'store_dialog_main_window_geometry')
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):
fname = download_item.downloadFileName()
download_id = download_item.id()
tdir = PersistentTemporaryDirectory()
self.download_data[download_id] = download_item
download_item.setDownloadDirectory(tdir)
connect_lambda(download_item.receivedBytesChanged, self, lambda self: self.download_progress(download_id))
connect_lambda(download_item.totalBytesChanged, self, lambda self: self.download_progress(download_id))
connect_lambda(download_item.isFinishedChanged, 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):
download_item = self.download_data.get(download_id)
if download_item is not None:
self.central.download_progress.update_item(download_id, download_item.receivedBytes(), download_item.totalBytes())
def download_finished(self, download_id):
self.central.download_progress.remove_item(download_id)
download_item = self.download_data.pop(download_id)
fname = download_item.downloadFileName()
path = os.path.join(download_item.downloadDirectory(), fname)
if download_item.state() == QWebEngineDownloadRequest.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_and_focus()
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])