%PDF- %PDF-
Direktori : /proc/thread-self/root/usr/lib/calibre/calibre/gui2/tweak_book/ |
Current File : //proc/thread-self/root/usr/lib/calibre/calibre/gui2/tweak_book/download.py |
#!/usr/bin/env python3 # License: GPLv3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net> from threading import Thread from qt.core import ( pyqtSignal, QWidget, QListWidget, QListWidgetItem, QLabel, Qt, QVBoxLayout, QScrollArea, QProgressBar, QGridLayout, QSize, QIcon, QDialogButtonBox) from calibre.gui2 import error_dialog, info_dialog, warning_dialog from calibre.gui2.tweak_book import current_container from calibre.gui2.tweak_book.widgets import Dialog from calibre.gui2.progress_indicator import WaitStack from calibre.ebooks.oeb.polish.download import get_external_resources, download_external_resources, replace_resources from polyglot.builtins import iteritems class ChooseResources(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.l = l = QVBoxLayout(self) self.la = la = QLabel(_('Choose the external resources to download')) la.setWordWrap(True) l.addWidget(la) self.items = i = QListWidget(self) l.addWidget(i) def __iter__(self): for i in range(self.items.count()): yield self.items.item(i) def select_none(self): for item in self: item.setCheckState(Qt.CheckState.Unchecked) def select_all(self): for item in self: item.setCheckState(Qt.CheckState.Checked) @property def resources(self): return {i.data(Qt.ItemDataRole.UserRole):self.original_resources[i.data(Qt.ItemDataRole.UserRole)] for i in self if i.checkState() == Qt.CheckState.Checked} @resources.setter def resources(self, resources): self.items.clear() self.original_resources = resources dc = 0 for url, matches in iteritems(resources): text = url num = len(matches) if text.startswith('data:'): dc += 1 text = _('Data URL #{}').format(dc) text += ' ({})'.format(ngettext('one instance', '{} instances', num).format(num)) i = QListWidgetItem(text, self.items) i.setData(Qt.ItemDataRole.UserRole, url) i.setCheckState(Qt.CheckState.Checked) i.setFlags(Qt.ItemFlag.ItemIsUserCheckable | Qt.ItemFlag.ItemIsEnabled) class DownloadStatus(QScrollArea): def __init__(self, parent=None): QScrollArea.__init__(self, parent) self.setWidgetResizable(True) self.w = QWidget(self) self.l = QGridLayout(self.w) self.setWidget(self.w) def __call__(self, resources): self.url_map = {} self.labels = [] for url in resources: p = self.url_map[url] = QProgressBar(self.w) p.setRange(0, 0) self.l.addWidget(p, self.l.rowCount(), 0) la = QLabel('\xa0' + url) self.labels.append(la) self.l.addWidget(la, self.l.rowCount()-1, 1) self.l.addWidget(QLabel('')) self.l.setRowStretch(self.l.rowCount()-1, 10) def progress(self, url, done, total): p = self.url_map.get(url) if p is not None: if total > 0: p.setRange(0, total) p.setValue(done) else: p.setRange(0, 0) class DownloadResources(Dialog): get_done = pyqtSignal(object, object) progress = pyqtSignal(object, object, object) download_done = pyqtSignal(object, object) replace_done = pyqtSignal(object, object) def __init__(self, parent=None): self.resources_replaced = False self.show_diff = False Dialog.__init__(self, _('Download external resources'), 'download-external-resources', parent) self.state = 0 self.get_done.connect(self._get_done) self.download_done.connect(self._download_done) self.replace_done.connect(self._replace_done) self.progress.connect(self.download_status.progress, type=Qt.ConnectionType.QueuedConnection) def setup_ui(self): self.setWindowIcon(QIcon(I('download-metadata.png'))) self.choose_resources = cr = ChooseResources(self) self.download_status = ds = DownloadStatus(self) self.success = s = QLabel('') s.setWordWrap(True) self.l = l = QVBoxLayout(self) self.wait = WaitStack(_('Searching for external resources...'), cr, self) self.wait.addWidget(ds), self.wait.addWidget(s) self.wait.start() for t, f in ((_('Select &none'), cr.select_none), (_('Select &all'), cr.select_all)): b = self.bb.addButton(t, QDialogButtonBox.ButtonRole.ActionRole) b.clicked.connect(f), b.setAutoDefault(False) self.bb.setVisible(False) l.addWidget(self.wait), l.addWidget(self.bb) t = Thread(name='GetResources', target=self.get_resources) t.daemon = True t.start() def get_resources(self): tb = None try: ret = get_external_resources(current_container()) except Exception as err: import traceback ret, tb = err, traceback.format_exc() self.get_done.emit(ret, tb) def _get_done(self, x, tb): if not self.isVisible(): return self.reject() if tb is not None: error_dialog(self, _('Scan failed'), _( 'Failed to scan for external resources, click "Show details" for more information.'), det_msg=tb, show=True) self.reject() else: self.wait.stop() self.state = 1 resources = x if not resources: info_dialog(self, _('No external resources found'), _( 'No external resources were found in this book.'), show=True) self.reject() return self.choose_resources.resources = resources self.bb.setVisible(True) def download_resources(self, resources): tb = None try: ret = download_external_resources(current_container(), resources, progress_report=self.progress.emit) except Exception as err: import traceback ret, tb = err, traceback.format_exc() self.download_done.emit(ret, tb) def _download_done(self, ret, tb): if not self.isVisible(): return self.reject() if tb is not None: error_dialog(self, _('Download failed'), _( 'Failed to download external resources, click "Show details" for more information.'), det_msg=tb, show=True) self.reject() else: replacements, failures = ret if failures: tb = [f'{url}\n\t{err}\n' for url, err in iteritems(failures)] if not replacements: error_dialog(self, _('Download failed'), _( 'Failed to download external resources, click "Show details" for more information.'), det_msg='\n'.join(tb), show=True) self.reject() return else: warning_dialog(self, _('Some downloads failed'), _( 'Failed to download some external resources, click "Show details" for more information.'), det_msg='\n'.join(tb), show=True) self.state = 2 self.wait.msg = _('Updating resources in book...') self.wait.start() t = ngettext( 'Successfully processed the external resource', 'Successfully processed {} external resources', len(replacements)).format(len(replacements)) if failures: t += '<br>' + ngettext('Could not download one image', 'Could not download {} images', len(failures)).format(len(failures)) self.success.setText('<p style="text-align:center">' + t) resources = self.choose_resources.resources t = Thread(name='ReplaceResources', target=self.replace_resources, args=(resources, replacements)) t.daemon = True t.start() def replace_resources(self, resources, replacements): tb = None try: ret = replace_resources(current_container(), resources, replacements) except Exception as err: import traceback ret, tb = err, traceback.format_exc() self.replace_done.emit(ret, tb) def _replace_done(self, ret, tb): if tb is not None: error_dialog(self, _('Replace failed'), _( 'Failed to replace external resources, click "Show details" for more information.'), det_msg=tb, show=True) Dialog.reject(self) else: self.wait.setCurrentIndex(3) self.state = 3 self.bb.clear() self.resources_replaced = True self.bb.setStandardButtons(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Close) b = self.bb.button(QDialogButtonBox.StandardButton.Ok) b.setText(_('See what &changed')) b.setIcon(QIcon(I('diff.png'))) connect_lambda(b.clicked, self, lambda self: setattr(self, 'show_diff', True)) self.bb.setVisible(True) def accept(self): if self.state == 0: return self.reject() if self.state == 1: resources = self.choose_resources.resources self.download_status(resources) self.wait.setCurrentIndex(2) self.bb.setVisible(False) t = Thread(name='DownloadResources', target=self.download_resources, args=(resources,)) t.daemon = True t.start() return if self.state == 2: return self.wait.stop() Dialog.accept(self) def reject(self): if self.state == 2: return self.wait.stop() return Dialog.reject(self) def sizeHint(self): return QSize(800, 500) if __name__ == '__main__': from calibre.gui2 import Application import sys app = Application([]) from calibre.gui2.tweak_book import set_current_container from calibre.gui2.tweak_book.boss import get_container set_current_container(get_container(sys.argv[-1])) d = DownloadResources() d.exec() print(d.show_diff) del d, app