Пример #1
0
    def catalog_generated(self, job):
        if job.result:
            # Problems during catalog generation
            # jobs.results is a list - the first entry is the intended title for the dialog
            # Subsequent strings are error messages
            dialog_title = job.result.pop(0)
            if re.search('warning', job.result[0].lower()):
                msg = _("Catalog generation complete, with warnings.")
                warning_dialog(self.gui,
                               dialog_title,
                               msg,
                               det_msg='\n'.join(job.result),
                               show=True)
            else:
                job.result.append("Catalog generation terminated.")
                error_dialog(self.gui,
                             dialog_title,
                             '\n'.join(job.result),
                             show=True)
                return

        if job.failed:
            return self.gui.job_exception(job)
        if dynamic.get('catalog_add_to_library', True):
            id = self.gui.library_view.model().add_catalog(
                job.catalog_file_path, job.catalog_title)
            self.gui.library_view.model().beginResetModel(
            ), self.gui.library_view.model().endResetModel()
            if job.catalog_sync:
                sync = dynamic.get('catalogs_to_be_synced', set())
                sync.add(id)
                dynamic.set('catalogs_to_be_synced', sync)
        self.gui.status_bar.show_message(_('Catalog generated.'), 3000)
        self.gui.sync_catalogs()
        if not dynamic.get('catalog_add_to_library', True) or job.fmt not in {
                'EPUB', 'MOBI', 'AZW3'
        }:
            export_dir = choose_dir(
                self.gui, _('Export catalog folder'),
                _('Select destination for %(title)s.%(fmt)s') %
                dict(title=job.catalog_title, fmt=job.fmt.lower()))
            if export_dir:
                destination = os.path.join(
                    export_dir, '%s.%s' %
                    (sanitize_file_name(job.catalog_title), job.fmt.lower()))
                try:
                    shutil.copyfile(job.catalog_file_path, destination)
                except OSError as err:
                    if getattr(err, 'errno',
                               None) == errno.EACCES:  # Permission denied
                        import traceback
                        error_dialog(
                            self.gui,
                            _('Permission denied'),
                            _('Could not open %s. Is it being used by another'
                              ' program?') % destination,
                            det_msg=traceback.format_exc(),
                            show=True)
                        return
                    raise
Пример #2
0
 def choose_save_dir(self, default_dir):
     savedir = None
     askagain = True
     no_save_dir = False
     if default_dir:
         no_save_dir = True
     title = _('Choose destination directory for scrambled ebook')
     while askagain:
         savedir = choose_dir(window=self,
                              name='',
                              title=title,
                              default_dir=default_dir,
                              no_save_dir=no_save_dir)
         askagain = False
         if savedir is not None:
             savedir = os.path.normpath(savedir)
             if savedir.startswith(tuple(self.calibre_libpaths)):
                 askagain = True
                 msg = []
                 msg.append(
                     'You have selected a destination inside your Calibre library.'
                 )
                 msg.append(savedir)
                 msg.append('\nThis is NOT recommended. Try again.')
                 msg.append('\nPlease avoid the following:')
                 [
                     msg.append(path)
                     for path in sorted(self.calibre_libpaths)
                 ]
                 warning_dialog(self,
                                'Calibre library chosen',
                                '\n'.join(msg),
                                show=True,
                                show_copy_button=True)
     return savedir
Пример #3
0
    def dispatch_button_click(self, button):
        '''
        BUTTON_ROLES = ['AcceptRole', 'RejectRole', 'DestructiveRole', 'ActionRole',
                        'HelpRole', 'YesRole', 'NoRole', 'ApplyRole', 'ResetRole']
        '''
        self._log_location()
        if self.bb.buttonRole(button) == QDialogButtonBox.AcceptRole:
            requested_name = str(self.calibre_destination_le.text())

            if requested_name in self.get_custom_column_names():
                self._log("'%s' already in use" % requested_name)
                warning_dialog(
                    self.gui,
                    _("Already in use"),
                    _("<p>'{0}' is an existing custom column.</p><p>Pick a different name.</p>"
                      ).format(requested_name),
                    show=True,
                    show_copy_button=False)

                self.calibre_destination_le.selectAll()
                self.calibre_destination_le.setFocus()

            else:
                source = self.column_type
                #profile = self.FIELDS[source]
                self.profile['source'] = source
                if button.objectName() == 'add_button':
                    self.custom_column_add(requested_name, self.profile)

                elif button.objectName() == 'rename_button':
                    self.custom_column_rename(requested_name, self.profile)
                self.accept()

        elif self.bb.buttonRole(button) == QDialogButtonBox.RejectRole:
            self.close()
    def dispatch_button_click(self, button):
        '''
        BUTTON_ROLES = ['AcceptRole', 'RejectRole', 'DestructiveRole', 'ActionRole',
                        'HelpRole', 'YesRole', 'NoRole', 'ApplyRole', 'ResetRole']
        '''
        self._log_location()
        if self.bb.buttonRole(button) == QDialogButtonBox.AcceptRole:
            requested_name = str(self.calibre_destination_le.text())

            if requested_name in self.get_custom_column_names():
                self._log("'%s' already in use" % requested_name)
                warning_dialog(self.gui,
                               "Already in use",
                               "<p>'%s' is an existing custom column.</p><p>Pick a different name.</p>" % requested_name,
                               show=True, show_copy_button=False)

                self.calibre_destination_le.selectAll()
                self.calibre_destination_le.setFocus()

            else:
                source = self.column_type
                #profile = self.FIELDS[source]
                self.profile['source'] = source
                if button.objectName() == 'add_button':
                    self.custom_column_add(requested_name, self.profile)

                elif button.objectName() == 'rename_button':
                    self.custom_column_rename(requested_name, self.profile)
                self.accept()

        elif self.bb.buttonRole(button) == QDialogButtonBox.RejectRole:
            self.close()
Пример #5
0
    def _files_added(self, paths=[], names=[], infos=[], on_card=None):
        self.gui.tags_view.disable_recounting = False
        if paths:
            self.gui.upload_books(paths, list(map(ascii_filename, names)), infos, on_card=on_card)
            self.gui.status_bar.show_message(_("Uploading books to device."), 2000)
        if getattr(self._adder, "number_of_books_added", 0) > 0:
            self.gui.library_view.model().books_added(self._adder.number_of_books_added)
            self.gui.library_view.set_current_row(0)
            if hasattr(self.gui, "db_images"):
                self.gui.db_images.reset()
            self.gui.tags_view.recount()

        if getattr(self._adder, "merged_books", False):
            books = u"\n".join(
                [
                    x if isinstance(x, unicode) else x.decode(preferred_encoding, "replace")
                    for x in self._adder.merged_books
                ]
            )
            info_dialog(
                self.gui,
                _("Merged some books"),
                _(
                    "The following %d duplicate books were found and incoming "
                    "book formats were processed and merged into your "
                    "Calibre database according to your automerge "
                    "settings:"
                )
                % len(self._adder.merged_books),
                det_msg=books,
                show=True,
            )

        if getattr(self._adder, "number_of_books_added", 0) > 0 or getattr(self._adder, "merged_books", False):
            # The formats of the current book could have changed if
            # automerge is enabled
            current_idx = self.gui.library_view.currentIndex()
            if current_idx.isValid():
                self.gui.library_view.model().current_changed(current_idx, current_idx)

        if getattr(self._adder, "critical", None):
            det_msg = []
            for name, log in self._adder.critical.items():
                if isinstance(name, str):
                    name = name.decode(filesystem_encoding, "replace")
                det_msg.append(name + "\n" + log)

            warning_dialog(
                self.gui,
                _("Failed to read metadata"),
                _("Failed to read metadata from the following") + ":",
                det_msg="\n\n".join(det_msg),
                show=True,
            )

        if hasattr(self._adder, "cleanup"):
            self._adder.cleanup()
            self._adder.setParent(None)
            del self._adder
            self._adder = None
Пример #6
0
    def done(self, job):
        if job.result:
            # Problems during word wise generation
            # jobs.results is a list - the first entry is the intended
            # title for the dialog
            # Subsequent strings are error messages
            dialog_title = job.result.pop(0)
            if re.search('warning', job.result[0].lower()):
                msg = "Word Wise generation complete, with warnings."
                warning_dialog(self.gui, dialog_title, msg,
                               det_msg='\n'.join(job.result), show=True)
            else:
                job.result.append("Word Wise generation terminated.")
                error_dialog(self.gui, dialog_title,
                             '\n'.join(job.result), show=True)
                return
        if job.failed:
            self.gui.job_exception(job)
            return

        # send files to device
        for book_id in self.ids:
            data = check_metadata(self.gui.current_db.new_api,
                                  book_id, False)
            if data is None:
                continue
            send(self.gui, (book_id, ) + data)

        self.gui.status_bar.show_message("Word Wise generated.")
Пример #7
0
 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 = ['{}\n\t{}\n'.format(url, err) for url, err in failures.iteritems()]
             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()
Пример #8
0
    def catalog_generated(self, job):
        if job.result:
            # Problems during catalog generation
            # jobs.results is a list - the first entry is the intended title for the dialog
            # Subsequent strings are error messages
            dialog_title = job.result.pop(0)
            if re.match('warning:', job.result[0].lower()):
                msg = _("Catalog generation complete, with warnings.")
                warning_dialog(self.gui, dialog_title, msg, det_msg='\n'.join(job.result), show=True)
            else:
                job.result.append("Catalog generation terminated.")
                error_dialog(self.gui, dialog_title,'\n'.join(job.result),show=True)
                return

        if job.failed:
            return self.gui.job_exception(job)
        id = self.gui.library_view.model().add_catalog(job.catalog_file_path, job.catalog_title)
        self.gui.library_view.model().reset()
        if job.catalog_sync:
            sync = dynamic.get('catalogs_to_be_synced', set([]))
            sync.add(id)
            dynamic.set('catalogs_to_be_synced', sync)
        self.gui.status_bar.show_message(_('Catalog generated.'), 3000)
        self.gui.sync_catalogs()
        if job.fmt not in ['EPUB','MOBI']:
            export_dir = choose_dir(self.gui, _('Export Catalog Directory'),
                    _('Select destination for %(title)s.%(fmt)s') % dict(
                        title=job.catalog_title, fmt=job.fmt.lower()))
            if export_dir:
                destination = os.path.join(export_dir, '%s.%s' % (
                    sanitize_file_name_unicode(job.catalog_title), job.fmt.lower()))
                shutil.copyfile(job.catalog_file_path, destination)
Пример #9
0
 def show_no_results_found(self):
     if self.current_search:
         warning_dialog(self,
                        _('No matches found'),
                        _('No matches were found for: <b>{}</b>').format(
                            self.current_search.text),
                        show=True)
Пример #10
0
 def existing_pb_clicked(self, qitem):
     item = qitem.data(Qt.UserRole)
     if qitem.flags() & Qt.ItemIsEnabled:
         self.edit_format.setCurrentIndex(self.edit_format.findText(item[0]))
         self.edit_device.setCurrentIndex(self.edit_device.findText(item[1]))
     else:
         warning_dialog(self, "", _("The {0} device plugin is disabled.").format(item[1]), show=True)
Пример #11
0
 def cannot_do_dialog(self):
     warning_dialog(
         self.gui,
         _('Not allowed'),
         _('You cannot use other libraries while using the environment'
           ' variable CALIBRE_OVERRIDE_DATABASE_PATH.'),
         show=True)
Пример #12
0
 def start_content_server(self, check_started=True):
     from calibre.srv.embedded import Server
     if not gprefs.get('server3_warning_done', False):
         gprefs.set('server3_warning_done', True)
         if os.path.exists(os.path.join(config_dir, 'server.py')):
             try:
                 os.remove(os.path.join(config_dir, 'server.py'))
             except EnvironmentError:
                 pass
             warning_dialog(
                 self,
                 _('Content server changed!'),
                 _('calibre 3 comes with a completely re-written content server.'
                   ' As such any custom configuration you have for the content'
                   ' server no longer applies. You should check and refresh your'
                   ' settings in Preferences->Sharing->Sharing over the net'
                   ),
                 show=True)
     self.content_server = Server(
         self.library_broker, Dispatcher(self.handle_changes_from_server))
     self.content_server.state_callback = Dispatcher(
         self.iactions['Connect Share'].content_server_state_changed)
     if check_started:
         self.content_server.start_failure_callback = \
             Dispatcher(self.content_server_start_failed)
     self.content_server.start()
Пример #13
0
def show_config_widget(category, name, gui=None, show_restart_msg=False,
        parent=None, never_shutdown=False):
    '''
    Show the preferences plugin identified by category and name

    :param gui: gui instance, if None a hidden gui is created
    :param show_restart_msg: If True and the preferences plugin indicates a
    restart is required, show a message box telling the user to restart
    :param parent: The parent of the displayed dialog

    :return: True iff a restart is required for the changes made by the user to
    take effect
    '''
    from calibre.gui2 import gprefs
    pl = get_plugin(category, name)
    d = ConfigDialog(parent)
    d.resize(750, 550)
    conf_name = 'config_widget_dialog_geometry_%s_%s'%(category, name)
    geom = gprefs.get(conf_name, None)
    d.setWindowTitle(_('Configure ') + name)
    d.setWindowIcon(QIcon(I('config.png')))
    bb = QDialogButtonBox(d)
    bb.setStandardButtons(bb.Apply|bb.Cancel|bb.RestoreDefaults)
    bb.accepted.connect(d.accept)
    bb.rejected.connect(d.reject)
    w = pl.create_widget(d)
    d.set_widget(w)
    bb.button(bb.RestoreDefaults).clicked.connect(w.restore_defaults)
    bb.button(bb.RestoreDefaults).setEnabled(w.supports_restoring_to_defaults)
    bb.button(bb.Apply).setEnabled(False)
    bb.button(bb.Apply).clicked.connect(d.accept)
    def onchange():
        b = bb.button(bb.Apply)
        b.setEnabled(True)
        b.setDefault(True)
        b.setAutoDefault(True)
    w.changed_signal.connect(onchange)
    bb.button(bb.Cancel).setFocus(True)
    l = QVBoxLayout()
    d.setLayout(l)
    l.addWidget(w)
    l.addWidget(bb)
    mygui = gui is None
    if gui is None:
        gui = init_gui()
        mygui = True
    w.genesis(gui)
    w.initialize()
    if geom is not None:
        d.restoreGeometry(geom)
    d.exec_()
    geom = bytearray(d.saveGeometry())
    gprefs[conf_name] = geom
    rr = getattr(d, 'restart_required', False)
    if show_restart_msg and rr:
        from calibre.gui2 import warning_dialog
        warning_dialog(gui, 'Restart required', 'Restart required', show=True)
    if mygui and not never_shutdown:
        gui.shutdown()
    return rr
Пример #14
0
 def cannot_do_dialog(self):
     warning_dialog(
         self.gui,
         _("Not allowed"),
         _("You cannot use other libraries while using the environment" " variable CALIBRE_OVERRIDE_DATABASE_PATH."),
         show=True,
     )
Пример #15
0
 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 = ['{}\n\t{}\n'.format(url, err) 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()
Пример #16
0
 def do_one(self):
     try:
         i, book_ids, pd, only_fmts, errors = self.job_data
     except (TypeError, AttributeError):
         return
     if i >= len(book_ids) or pd.wasCanceled():
         pd.setValue(pd.maximum())
         pd.hide()
         self.pd_timer.stop()
         self.job_data = None
         self.gui.library_view.model().refresh_ids(book_ids)
         if errors:
             det_msg = [_('The {0} format of {1}:\n\n{2}\n').format(
                 (fmt or '').upper(), force_unicode(mi.title), force_unicode(tb)) for mi, fmt, tb in errors]
             warning_dialog(
                 self.gui, _('Failed for some files'), _(
                 'Failed to embed metadata into some book files. Click "Show details" for details.'),
                 det_msg='\n\n'.join(det_msg), show=True)
         return
     pd.setValue(i)
     db = self.gui.current_db.new_api
     def report_error(mi, fmt, tb):
         errors.append((mi, fmt, tb))
     db.embed_metadata((book_ids[i],), only_fmts=only_fmts, report_error=report_error)
     self.job_data = (i + 1, book_ids, pd, only_fmts, errors)
Пример #17
0
 def do_one(self):
     try:
         i, book_ids, pd, only_fmts, errors = self.job_data
     except (TypeError, AttributeError):
         return
     if i >= len(book_ids) or pd.wasCanceled():
         pd.setValue(pd.maximum())
         pd.hide()
         self.pd_timer.stop()
         self.job_data = None
         self.gui.library_view.model().refresh_ids(book_ids)
         if i > 0:
             self.gui.status_bar.show_message(_('Embedded metadata in %d books') % i, 5000)
         if errors:
             det_msg = [_('The {0} format of {1}:\n\n{2}\n').format(
                 (fmt or '').upper(), force_unicode(mi.title), force_unicode(tb)) for mi, fmt, tb in errors]
             warning_dialog(
                 self.gui, _('Failed for some files'), _(
                 'Failed to embed metadata into some book files. Click "Show details" for details.'),
                 det_msg='\n\n'.join(det_msg), show=True)
         return
     pd.setValue(i)
     db = self.gui.current_db.new_api
     def report_error(mi, fmt, tb):
         errors.append((mi, fmt, tb))
     db.embed_metadata((book_ids[i],), only_fmts=only_fmts, report_error=report_error)
     self.job_data = (i + 1, book_ids, pd, only_fmts, errors)
Пример #18
0
 def commit(self):
     ConfigWidgetBase.commit(self)
     warning_dialog(self,
                    _('Restart needed'),
                    _('You need to restart the server for changes to'
                      ' take effect'),
                    show=True)
     return False
Пример #19
0
 def show_no_results_found(self):
     has_hidden_text = self.last_hidden_text_warning is not None and self.last_hidden_text_warning[1] == self.current_search
     if self.current_search:
         if has_hidden_text:
             msg = _('No displayable matches were found for:')
         else:
             msg = _('No matches were found for:')
         warning_dialog(self, _('No matches found'), msg + '  <b>{}</b>'.format(self.current_search.text), show=True)
Пример #20
0
    def send_multiple_by_mail(self, recipients, delete_from_library):
        ids = set(self.library_view.model().id(r) for r in self.library_view.selectionModel().selectedRows())
        if not ids:
            return
        db = self.current_db
        db_fmt_map = {book_id: set((db.formats(book_id, index_is_id=True) or "").upper().split(",")) for book_id in ids}
        ofmts = {x.upper() for x in available_output_formats()}
        ifmts = {x.upper() for x in available_input_formats()}
        bad_recipients = {}
        auto_convert_map = defaultdict(list)

        for to, fmts, subject in recipients:
            rfmts = set(fmts)
            ok_ids = {book_id for book_id, bfmts in db_fmt_map.iteritems() if bfmts.intersection(rfmts)}
            convert_ids = ids - ok_ids
            self.send_by_mail(to, fmts, delete_from_library, subject=subject, send_ids=ok_ids, do_auto_convert=False)
            if not rfmts.intersection(ofmts):
                bad_recipients[to] = (convert_ids, True)
                continue
            outfmt = tuple(f for f in fmts if f in ofmts)[0]
            ok_ids = {book_id for book_id in convert_ids if db_fmt_map[book_id].intersection(ifmts)}
            bad_ids = convert_ids - ok_ids
            if bad_ids:
                bad_recipients[to] = (bad_ids, False)
            if ok_ids:
                auto_convert_map[outfmt].append((to, subject, ok_ids))

        if auto_convert_map:
            titles = {book_id for x in auto_convert_map.itervalues() for data in x for book_id in data[2]}
            titles = {db.title(book_id, index_is_id=True) for book_id in titles}
            if self.auto_convert_question(
                _("Auto convert the following books before sending via email?"), list(titles)
            ):
                for ofmt, data in auto_convert_map.iteritems():
                    ids = {bid for x in data for bid in x[2]}
                    data = [(to, subject) for to, subject, x in data]
                    self.iactions["Convert Books"].auto_convert_multiple_mail(ids, data, ofmt, delete_from_library)

        if bad_recipients:
            det_msg = []
            titles = {book_id for x in bad_recipients.itervalues() for book_id in x[0]}
            titles = {book_id: db.title(book_id, index_is_id=True) for book_id in titles}
            for to, (ids, nooutput) in bad_recipients.iteritems():
                msg = (
                    _("This recipient has no valid formats defined")
                    if nooutput
                    else _("These books have no suitable input formats for conversion")
                )
                det_msg.append("%s - %s" % (to, msg))
                det_msg.extend("\t" + titles[bid] for bid in ids)
                det_msg.append("\n")
            warning_dialog(
                self,
                _("Could not send"),
                _("Could not send books to some recipients. Click Show Details for more information"),
                det_msg="\n".join(det_msg),
                show=True,
            )
Пример #21
0
    def _files_added(self, paths=[], names=[], infos=[], on_card=None):
        self.gui.tags_view.disable_recounting = False
        if paths:
            self.gui.upload_books(paths,
                                list(map(ascii_filename, names)),
                                infos, on_card=on_card)
            self.gui.status_bar.show_message(
                    _('Uploading books to device.'), 2000)
        if getattr(self._adder, 'number_of_books_added', 0) > 0:
            self.gui.library_view.model().books_added(self._adder.number_of_books_added)
            self.gui.library_view.set_current_row(0)
            if hasattr(self.gui, 'db_images'):
                self.gui.db_images.reset()
            self.gui.tags_view.recount()

        if getattr(self._adder, 'merged_books', False):
            merged = defaultdict(list)
            for title, author in self._adder.merged_books:
                merged[author].append(title)
            lines = []
            for author in sorted(merged, key=sort_key):
                lines.append(author)
                for title in sorted(merged[author], key=sort_key):
                    lines.append('\t' + title)
                lines.append('')
            info_dialog(self.gui, _('Merged some books'),
                _('The following %d duplicate books were found and incoming '
                    'book formats were processed and merged into your '
                    'Calibre database according to your automerge '
                    'settings:')%len(self._adder.merged_books),
                    det_msg='\n'.join(lines), show=True)

        if getattr(self._adder, 'number_of_books_added', 0) > 0 or \
                getattr(self._adder, 'merged_books', False):
            # The formats of the current book could have changed if
            # automerge is enabled
            current_idx = self.gui.library_view.currentIndex()
            if current_idx.isValid():
                self.gui.library_view.model().current_changed(current_idx,
                        current_idx)

        if getattr(self._adder, 'critical', None):
            det_msg = []
            for name, log in self._adder.critical.items():
                if isinstance(name, str):
                    name = name.decode(filesystem_encoding, 'replace')
                det_msg.append(name+'\n'+log)

            warning_dialog(self.gui, _('Failed to read metadata'),
                    _('Failed to read metadata from the following')+':',
                    det_msg='\n\n'.join(det_msg), show=True)

        if hasattr(self._adder, 'cleanup'):
            self._adder.cleanup()
            self._adder.setParent(None)
            del self._adder
            self._adder = None
Пример #22
0
 def existing_pb_clicked(self, qitem):
     item = qitem.data(Qt.UserRole)
     if (qitem.flags() & Qt.ItemIsEnabled):
         self.edit_format.setCurrentIndex(self.edit_format.findText(item[0]))
         self.edit_device.setCurrentIndex(self.edit_device.findText(item[1]))
     else:
         warning_dialog(self, '',
              _('The {0} device plugin is disabled.').format(item[1]),
              show=True)
Пример #23
0
    def _files_added(self, paths=[], names=[], infos=[], on_card=None):
        self.gui.tags_view.disable_recounting = False
        if paths:
            self.gui.upload_books(paths,
                                list(map(ascii_filename, names)),
                                infos, on_card=on_card)
            self.gui.status_bar.show_message(
                    _('Uploading books to device.'), 2000)
        if getattr(self._adder, 'number_of_books_added', 0) > 0:
            self.gui.library_view.model().books_added(self._adder.number_of_books_added)
            self.gui.library_view.set_current_row(0)
            if hasattr(self.gui, 'db_images'):
                self.gui.db_images.reset()
            self.gui.tags_view.recount()

        if getattr(self._adder, 'merged_books', False):
            merged = defaultdict(list)
            for title, author in self._adder.merged_books:
                merged[author].append(title)
            lines = []
            for author in sorted(merged, key=sort_key):
                lines.append(author)
                for title in sorted(merged[author], key=sort_key):
                    lines.append('\t' + title)
                lines.append('')
            info_dialog(self.gui, _('Merged some books'),
                _('The following %d duplicate books were found and incoming '
                    'book formats were processed and merged into your '
                    'Calibre database according to your automerge '
                    'settings:')%len(self._adder.merged_books),
                    det_msg='\n'.join(lines), show=True)

        if getattr(self._adder, 'number_of_books_added', 0) > 0 or \
                getattr(self._adder, 'merged_books', False):
            # The formats of the current book could have changed if
            # automerge is enabled
            current_idx = self.gui.library_view.currentIndex()
            if current_idx.isValid():
                self.gui.library_view.model().current_changed(current_idx,
                        current_idx)

        if getattr(self._adder, 'critical', None):
            det_msg = []
            for name, log in self._adder.critical.items():
                if isinstance(name, str):
                    name = name.decode(filesystem_encoding, 'replace')
                det_msg.append(name+'\n'+log)

            warning_dialog(self.gui, _('Failed to read metadata'),
                    _('Failed to read metadata from the following')+':',
                    det_msg='\n\n'.join(det_msg), show=True)

        if hasattr(self._adder, 'cleanup'):
            self._adder.cleanup()
            self._adder.setParent(None)
            del self._adder
            self._adder = None
Пример #24
0
 def commit(self):
     if not self.save_changes():
         raise AbortCommit()
     warning_dialog(self,
                    _('Restart needed'),
                    _('You need to restart the server for changes to'
                      ' take effect'),
                    show=True)
     return False
Пример #25
0
    def add(self, books):
        if isinstance(books, basestring):
            error_dialog(
                self.pd, _("Path error"), _("The specified directory could not be processed."), det_msg=books, show=True
            )
            return self.canceled()
        if not books:
            info_dialog(self.pd, _("No books"), _("No books found"), show=True)
            return self.canceled()
        books = [[b] if isinstance(b, basestring) else b for b in books]
        restricted = set()
        for i in xrange(len(books)):
            files = books[i]
            restrictedi = set(f for f in files if not os.access(f, os.R_OK))
            if restrictedi:
                files = [f for f in files if os.access(f, os.R_OK)]
                books[i] = files
            restricted |= restrictedi
        if restrictedi:
            det_msg = u"\n".join(restrictedi)
            warning_dialog(
                self.pd,
                _("No permission"),
                _(
                    "Cannot add some files as you do not have "
                    " permission to access them. Click Show"
                    " Details to see the list of such files."
                ),
                det_msg=det_msg,
                show=True,
            )
        books = list(filter(None, books))
        if not books:
            return self.canceled()
        self.rfind = None
        from calibre.ebooks.metadata.worker import read_metadata

        self.rq = Queue()
        tasks = []
        self.ids = {}
        self.nmap = {}
        self.duplicates = []
        for i, b in enumerate(books):
            tasks.append((i, b))
            self.ids[i] = b
            self.nmap[i] = os.path.basename(b[0])
        self.worker = read_metadata(tasks, self.rq, spare_server=self.spare_server)
        self.pd.set_min(0)
        self.pd.set_max(len(self.ids))
        self.pd.value = 0
        self.db_adder = DBAdder(self, self.db, self.ids, self.nmap)
        self.db_adder.start()
        self.last_added_at = time.time()
        self.entry_count = len(self.ids)
        self.continue_updating = True
        single_shot(self.update)
Пример #26
0
 def add(self, books):
     if isinstance(books, basestring):
         error_dialog(self.pd,
                      _('Path error'),
                      _('The specified directory could not be processed.'),
                      det_msg=books,
                      show=True)
         return self.canceled()
     if not books:
         info_dialog(self.pd, _('No books'), _('No books found'), show=True)
         return self.canceled()
     books = [[b] if isinstance(b, basestring) else b for b in books]
     restricted = set()
     for i in xrange(len(books)):
         files = books[i]
         restrictedi = set(f for f in files if not os.access(f, os.R_OK))
         if restrictedi:
             files = [f for f in files if os.access(f, os.R_OK)]
             books[i] = files
         restricted |= restrictedi
     if restrictedi:
         det_msg = u'\n'.join(restrictedi)
         warning_dialog(self.pd,
                        _('No permission'),
                        _('Cannot add some files as you do not have '
                          ' permission to access them. Click Show'
                          ' Details to see the list of such files.'),
                        det_msg=det_msg,
                        show=True)
     books = list(filter(None, books))
     if not books:
         return self.canceled()
     self.rfind = None
     from calibre.ebooks.metadata.worker import read_metadata
     self.rq = Queue()
     tasks = []
     self.ids = {}
     self.nmap = {}
     self.duplicates = []
     for i, b in enumerate(books):
         tasks.append((i, b))
         self.ids[i] = b
         self.nmap[i] = os.path.basename(b[0])
     self.worker = read_metadata(tasks,
                                 self.rq,
                                 spare_server=self.spare_server)
     self.pd.set_min(0)
     self.pd.set_max(len(self.ids))
     self.pd.value = 0
     self.db_adder = DBAdder(self, self.db, self.ids, self.nmap)
     self.db_adder.start()
     self.last_added_at = time.time()
     self.entry_count = len(self.ids)
     self.continue_updating = True
     single_shot(self.update)
Пример #27
0
 def commit(self):
     if not self.save_changes():
         raise AbortCommit()
     warning_dialog(
         self,
         _('Restart needed'),
         _('You need to restart the server for changes to'
           ' take effect'),
         show=True
     )
     return False
Пример #28
0
 def unapply_tags(self, node=None):
     nodes = self.applied_items_box.selectedItems() if node is None else [node]
     if len(nodes) == 0:
         warning_dialog(self, _('No items selected'),
                        _('You must select items to unapply'),
                        show=True, show_copy_button=False)
         return
     for node in nodes:
         index = self.all_items[node.data(Qt.ItemDataRole.UserRole)].index
         self.applied_items.remove(index)
     self.display_filtered_categories(None)
Пример #29
0
 def check_if_writer_disabled(self, format_name):
     if format_name in ['device_db', plugboard_any_format_value]:
         return
     show_message = True
     for writer in self.format_to_writers_map[format_name]:
         if not is_disabled(writer):
             show_message = False
     if show_message:
         warning_dialog(self, '',
                  _('That format has no metadata writers enabled. A plugboard '
                    'will probably have no effect.'),
                  show=True)
Пример #30
0
 def check_if_writer_disabled(self, format_name):
     if format_name in ['device_db', plugboard_any_format_value]:
         return
     show_message = True
     for writer in self.format_to_writers_map[format_name]:
         if not is_disabled(writer):
             show_message = False
     if show_message:
         warning_dialog(self, '',
                  _('That format has no metadata writers enabled. A plugboard '
                    'will probably have no effect.'),
                  show=True)
Пример #31
0
    def search_result_not_found(self, sr):
        self.results.search_result_not_found(sr)
        if self.results.count():
            now = monotonic()
            if self.last_hidden_text_warning is None or self.current_search != self.last_hidden_text_warning[1] or now - self.last_hidden_text_warning[0] > 5:
                self.last_hidden_text_warning = now, self.current_search
                warning_dialog(self, _('Hidden text'), _(
                    'Some search results were for hidden or non-reflowable text, they will be removed.'), show=True)
            elif self.last_hidden_text_warning is not None:
                self.last_hidden_text_warning = now, self.last_hidden_text_warning[1]

        if not self.results.count() and not self.spinner.is_running:
            self.show_no_results_found()
Пример #32
0
    def change_library_allowed(self):
        if os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
            warning_dialog(self.gui, _('Not allowed'),
                    _('You cannot change libraries while using the environment'
                        ' variable CALIBRE_OVERRIDE_DATABASE_PATH.'), show=True)
            return False
        if self.gui.job_manager.has_jobs():
            warning_dialog(self.gui, _('Not allowed'),
                    _('You cannot change libraries while jobs'
                        ' are running.'), show=True)
            return False

        return True
Пример #33
0
    def change_library_allowed(self):
        if os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
            warning_dialog(self.gui, _('Not allowed'),
                    _('You cannot change libraries while using the environment'
                        ' variable CALIBRE_OVERRIDE_DATABASE_PATH.'), show=True)
            return False
        if self.gui.job_manager.has_jobs():
            warning_dialog(self.gui, _('Not allowed'),
                    _('You cannot change libraries while jobs'
                        ' are running.'), show=True)
            return False

        return True
Пример #34
0
def _show_success_msg(restorer, parent=None):
    r = restorer
    olddb = _('The old database was saved as: %s')%force_unicode(r.olddb,
            filesystem_encoding)
    if r.errors_occurred:
        warning_dialog(parent, _('Success'),
                _('Restoring the database succeeded with some warnings'
                    ' click "Show details" to see the details. %s')%olddb,
                det_msg=r.report, show=True)
    else:
        info_dialog(parent, _('Success'),
                _('Restoring database was successful. %s')%olddb, show=True,
                show_copy_button=False)
Пример #35
0
def _show_success_msg(restorer, parent=None):
    r = restorer
    olddb = _('The old database was saved as: %s')%force_unicode(r.olddb,
            filesystem_encoding)
    if r.errors_occurred:
        warning_dialog(parent, _('Success'),
                _('Restoring the database succeeded with some warnings'
                    ' click Show details to see the details. %s')%olddb,
                det_msg=r.report, show=True)
    else:
        info_dialog(parent, _('Success'),
                _('Restoring database was successful. %s')%olddb, show=True,
                show_copy_button=False)
Пример #36
0
    def send_multiple_by_mail(self, recipients, delete_from_library):
        ids = set(self.library_view.model().id(r) for r in self.library_view.selectionModel().selectedRows())
        if not ids:
            return
        db = self.current_db
        db_fmt_map = {book_id:set((db.formats(book_id, index_is_id=True) or '').upper().split(',')) for book_id in ids}
        ofmts = {x.upper() for x in available_output_formats()}
        ifmts = {x.upper() for x in available_input_formats()}
        bad_recipients = {}
        auto_convert_map = defaultdict(list)

        for to, fmts, subject in recipients:
            rfmts = set(fmts)
            ok_ids = {book_id for book_id, bfmts in db_fmt_map.iteritems() if bfmts.intersection(rfmts)}
            convert_ids = ids - ok_ids
            self.send_by_mail(to, fmts, delete_from_library, subject=subject, send_ids=ok_ids, do_auto_convert=False)
            if not rfmts.intersection(ofmts):
                bad_recipients[to] = (convert_ids, True)
                continue
            outfmt = tuple(f for f in fmts if f in ofmts)[0]
            ok_ids = {book_id for book_id in convert_ids if db_fmt_map[book_id].intersection(ifmts)}
            bad_ids = convert_ids - ok_ids
            if bad_ids:
                bad_recipients[to] = (bad_ids, False)
            if ok_ids:
                auto_convert_map[outfmt].append((to, subject, ok_ids))

        if auto_convert_map:
            titles = {book_id for x in auto_convert_map.itervalues() for data in x for book_id in data[2]}
            titles = {db.title(book_id, index_is_id=True) for book_id in titles}
            if self.auto_convert_question(
                _('Auto convert the following books before sending via email?'), list(titles)):
                for ofmt, data in auto_convert_map.iteritems():
                    ids = {bid for x in data for bid in x[2]}
                    data = [(to, subject) for to, subject, x in data]
                    self.iactions['Convert Books'].auto_convert_multiple_mail(ids, data, ofmt, delete_from_library)

        if bad_recipients:
            det_msg = []
            titles = {book_id for x in bad_recipients.itervalues() for book_id in x[0]}
            titles = {book_id:db.title(book_id, index_is_id=True) for book_id in titles}
            for to, (ids, nooutput) in bad_recipients.iteritems():
                msg = _('This recipient has no valid formats defined') if nooutput else \
                        _('These books have no suitable input formats for conversion')
                det_msg.append('%s - %s' % (to, msg))
                det_msg.extend('\t' + titles[bid] for bid in ids)
                det_msg.append('\n')
            warning_dialog(self, _('Could not send'),
                           _('Could not send books to some recipients. Click Show Details for more information'),
                           det_msg='\n'.join(det_msg), show=True)
Пример #37
0
 def _remove_action(self, indices):
     names = self.current_actions.model().names(indices)
     if names:
         not_removed = self.current_actions.model().remove(indices)
         ns = {y.name for y in not_removed}
         removed = set(names) - ns
         self.all_actions.model().add(removed)
         if not_removed:
             warning_dialog(self, _('Cannot remove'),
                     _('Cannot remove the actions %s from this location') %
                     ','.join([a.action_spec[0] for a in not_removed]),
                     show=True)
         else:
             self.changed_signal.emit()
Пример #38
0
 def do_queue(self):
     self.hide()
     if len(self.bad):
         res = []
         for book_id, error in self.bad.iteritems():
             title = self.db.title(book_id, True)
             res.append('%s (%s)'%(title, error))
         msg = '%s' % '\n'.join(res)
         summary_msg = 'Could not analyse %d of %d books, for reasons shown in details below.'
         warning_dialog(self.gui, 'Page/word/statistics warnings',
             summary_msg % (len(res), len(self.book_ids)), msg).exec_()
     self.gui = None
     # Queue a job to process these books
     self.queue(self.tdir, self.books_to_scan, self.statistics_cols_map,
                self.pages_algorithm, self.use_goodreads)
Пример #39
0
 def remove_action(self, *args):
     x = self.current_actions.selectionModel().selectedIndexes()
     names = self.current_actions.model().names(x)
     if names:
         not_removed = self.current_actions.model().remove(x)
         ns = set([y.name for y in not_removed])
         removed = set(names) - ns
         self.all_actions.model().add(removed)
         if not_removed:
             warning_dialog(self, _('Cannot remove'),
                     _('Cannot remove the actions %s from this location') %
                     ','.join([a.action_spec[0] for a in not_removed]),
                     show=True)
         else:
             self.changed_signal.emit()
Пример #40
0
 def apply_tags(self, node=None):
     if self.current_cat_name is None:
         return
     nodes = self.available_items_box.selectedItems() if node is None else [node]
     if len(nodes) == 0:
         warning_dialog(self, _('No items selected'),
                        _('You must select items to apply'),
                        show=True, show_copy_button=False)
         return
     for node in nodes:
         index = self.all_items[node.data(Qt.ItemDataRole.UserRole)].index
         if index not in self.applied_items:
             self.applied_items.append(index)
     self.applied_items.sort(key=lambda x:sort_key(self.all_items[x].name))
     self.display_filtered_categories(None)
Пример #41
0
 def do_queue(self):
     if self.gui is None:
         # There is a nasty QT bug with the timers/logic above which can
         # result in the do_queue method being called twice
         return
     self.hide()
     if self.books_to_scan == []:
         warning_dialog(self.gui,
                        'Extract ISBN failed',
                        'Scan aborted as no books with formats found.',
                        show_copy_button=False).exec_()
     self.gui = None
     if self.books_to_scan:
         # Queue a job to process these books
         self.queue(self.books_to_scan, self.failed_ids, self.no_format_ids)
Пример #42
0
 def remove_action(self, *args):
     x = self.current_actions.selectionModel().selectedIndexes()
     names = self.current_actions.model().names(x)
     if names:
         not_removed = self.current_actions.model().remove(x)
         ns = set([y.name for y in not_removed])
         removed = set(names) - ns
         self.all_actions.model().add(removed)
         if not_removed:
             warning_dialog(self, _('Cannot remove'),
                     _('Cannot remove the actions %s from this location') %
                     ','.join([a.action_spec[0] for a in not_removed]),
                     show=True)
         else:
             self.changed_signal.emit()
Пример #43
0
    def do_queue(self):
        self.hide()
        if self.bad != []:
            res = []
            for id in self.bad:
                title = self.db.title(id, True)
                res.append('%s'%title)

            msg = '%s' % '\n'.join(res)
            warning_dialog(self.parent, _('Could not convert some books'),
                _('Could not convert %(num)d of %(tot)d books, because no suitable '
                'source format was found.') % dict(num=len(res), tot=len(self.book_ids)),
                msg).exec_()
        self.parent = None
        self.jobs.reverse()
        self.queue(self.jobs, self.changed, self.bad, *self.args)
Пример #44
0
    def do_queue(self):
        self.hide()
        if self.bad != []:
            res = []
            for id in self.bad:
                title = self.db.title(id, True)
                res.append('%s'%title)

            msg = '%s' % '\n'.join(res)
            warning_dialog(self.parent, _('Could not convert some books'),
                _('Could not convert %(num)d of %(tot)d books, because no suitable '
                'source format was found.') % dict(num=len(res), tot=len(self.book_ids)),
                msg).exec_()
        self.parent = None
        self.jobs.reverse()
        self.queue(self.jobs, self.changed, self.bad, *self.args)
Пример #45
0
 def create_button_clicked(self):
     self.changed_signal.emit()
     name = unicode(self.function_name.currentText())
     if name in self.funcs:
         error_dialog(self.gui, _('Template functions'),
                      _('Name %s already used')%(name,), show=True)
         return
     if self.argument_count.value() == 0:
         box = warning_dialog(self.gui, _('Template functions'),
                      _('Argument count should be -1 or greater than zero. '
                        'Setting it to zero means that this function cannot '
                        'be used in single function mode.'), det_msg = '',
                      show=False)
         box.bb.setStandardButtons(box.bb.standardButtons() | QDialogButtonBox.Cancel)
         box.det_msg_toggle.setVisible(False)
         if not box.exec_():
             return
     try:
         prog = unicode(self.program.toPlainText())
         cls = compile_user_function(name, unicode(self.documentation.toPlainText()),
                                     self.argument_count.value(), prog)
         self.funcs[name] = cls
         self.build_function_names_box(scroll_to=name)
     except:
         error_dialog(self.gui, _('Template functions'),
                      _('Exception while compiling function'), show=True,
                      det_msg=traceback.format_exc())
Пример #46
0
 def create_button_clicked(self):
     self.changed_signal.emit()
     name = unicode(self.function_name.currentText())
     if name in self.funcs:
         error_dialog(self.gui, _('Template functions'),
                      _('Name %s already used')%(name,), show=True)
         return
     if self.argument_count.value() == 0:
         box = warning_dialog(self.gui, _('Template functions'),
                      _('Argument count should be -1 or greater than zero. '
                        'Setting it to zero means that this function cannot '
                        'be used in single function mode.'), det_msg='',
                      show=False)
         box.bb.setStandardButtons(box.bb.standardButtons() | QDialogButtonBox.Cancel)
         box.det_msg_toggle.setVisible(False)
         if not box.exec_():
             return
     try:
         prog = unicode(self.program.toPlainText())
         cls = compile_user_function(name, unicode(self.documentation.toPlainText()),
                                     self.argument_count.value(), prog)
         self.funcs[name] = cls
         self.build_function_names_box(scroll_to=name)
     except:
         error_dialog(self.gui, _('Template functions'),
                      _('Exception while compiling function'), show=True,
                      det_msg=traceback.format_exc())
Пример #47
0
 def st_function_index_changed(self, txt):
     txt = str(txt)
     if self.st_current_program_name:
         if self.st_current_program_text != self.te_textbox.toPlainText():
             box = warning_dialog(
                 self.gui,
                 _('Template functions'),
                 _('Changes to the current template will be lost. OK?'),
                 det_msg='',
                 show=False,
                 show_copy_button=False)
             box.bb.setStandardButtons(
                 box.bb.standardButtons()
                 | QDialogButtonBox.StandardButton.Cancel)
             box.det_msg_toggle.setVisible(False)
             if not box.exec():
                 self.te_name.blockSignals(True)
                 dex = self.te_name.findText(self.st_current_program_name)
                 self.te_name.setCurrentIndex(dex)
                 self.te_name.blockSignals(False)
                 return
     self.st_create_button.setEnabled(False)
     self.st_current_program_name = txt
     if not txt:
         self.te_textbox.clear()
         self.template_editor.new_doc.clear()
         return
     func = self.st_funcs[txt]
     self.st_current_program_text = func.program_text
     self.template_editor.new_doc.setPlainText(func.doc)
     self.te_textbox.setPlainText(func.program_text)
     self.st_template_name_edited(txt)
Пример #48
0
    def _books_saved(self, path, failures, error):
        self._saver = None
        if error:
            return error_dialog(self.gui, _('Error while saving'),
                    _('There was an error while saving.'),
                    error, show=True)
        if failures:
            failures = [u'%s\n\t%s'%
                    (title, '\n\t'.join(err.splitlines())) for title, err in
                    failures]

            warning_dialog(self.gui, _('Could not save some books'),
            _('Could not save some books') + ', ' +
            _('Click the show details button to see which ones.'),
            u'\n\n'.join(failures), show=True)
        if gprefs['show_files_after_save']:
            open_local_file(path)
Пример #49
0
 def _add_action(self, indices):
     names = self.all_actions.model().names(indices)
     if names:
         not_added = self.current_actions.model().add(names)
         ns = {y.name for y in not_added}
         added = set(names) - ns
         self.all_actions.model().remove(indices, added)
         if not_added:
             warning_dialog(self, _('Cannot add'),
                     _('Cannot add the actions %s to this location') %
                     ','.join([a.action_spec[0] for a in not_added]),
                     show=True)
         if added:
             ca = self.current_actions
             idx = ca.model().index(ca.model().rowCount(None)-1)
             ca.scrollTo(idx)
             self.changed_signal.emit()
Пример #50
0
 def add_action(self, *args):
     x = self.all_actions.selectionModel().selectedIndexes()
     names = self.all_actions.model().names(x)
     if names:
         not_added = self.current_actions.model().add(names)
         ns = set([y.name for y in not_added])
         added = set(names) - ns
         self.all_actions.model().remove(x, added)
         if not_added:
             warning_dialog(self, _('Cannot add'),
                     _('Cannot add the actions %s to this location') %
                     ','.join([a.action_spec[0] for a in not_added]),
                     show=True)
         if added:
             ca = self.current_actions
             idx = ca.model().index(ca.model().rowCount(None)-1)
             ca.scrollTo(idx)
             self.changed_signal.emit()
Пример #51
0
    def catalog_generated(self, job):
        if job.result:
            # Problems during catalog generation
            # jobs.results is a list - the first entry is the intended title for the dialog
            # Subsequent strings are error messages
            dialog_title = job.result.pop(0)
            if re.search('warning', job.result[0].lower()):
                msg = _("Catalog generation complete, with warnings.")
                warning_dialog(self.gui, dialog_title, msg, det_msg='\n'.join(job.result), show=True)
            else:
                job.result.append("Catalog generation terminated.")
                error_dialog(self.gui, dialog_title,'\n'.join(job.result),show=True)
                return

        if job.failed:
            return self.gui.job_exception(job)
        if dynamic.get('catalog_add_to_library', True):
            id = self.gui.library_view.model().add_catalog(job.catalog_file_path, job.catalog_title)
            self.gui.library_view.model().beginResetModel(), self.gui.library_view.model().endResetModel()
            if job.catalog_sync:
                sync = dynamic.get('catalogs_to_be_synced', set([]))
                sync.add(id)
                dynamic.set('catalogs_to_be_synced', sync)
        self.gui.status_bar.show_message(_('Catalog generated.'), 3000)
        self.gui.sync_catalogs()
        if not dynamic.get('catalog_add_to_library', True) or job.fmt not in {'EPUB','MOBI', 'AZW3'}:
            export_dir = choose_dir(self.gui, _('Export Catalog Directory'),
                    _('Select destination for %(title)s.%(fmt)s') % dict(
                        title=job.catalog_title, fmt=job.fmt.lower()))
            if export_dir:
                destination = os.path.join(export_dir, '%s.%s' % (
                    sanitize_file_name(job.catalog_title), job.fmt.lower()))
                try:
                    shutil.copyfile(job.catalog_file_path, destination)
                except EnvironmentError as err:
                    if getattr(err, 'errno', None) == errno.EACCES:  # Permission denied
                        import traceback
                        error_dialog(self.gui, _('Permission denied'),
                                _('Could not open %s. Is it being used by another'
                                ' program?')%destination, det_msg=traceback.format_exc(),
                                show=True)
                        return
                    raise
Пример #52
0
    def change_library_allowed(self):
        if os.environ.get("CALIBRE_OVERRIDE_DATABASE_PATH", None):
            warning_dialog(
                self.gui,
                _("Not allowed"),
                _(
                    "You cannot change libraries while using the environment"
                    " variable CALIBRE_OVERRIDE_DATABASE_PATH."
                ),
                show=True,
            )
            return False
        if self.gui.job_manager.has_jobs():
            warning_dialog(
                self.gui, _("Not allowed"), _("You cannot change libraries while jobs" " are running."), show=True
            )
            return False

        return True
Пример #53
0
    def catalog_generated(self, job):
        if job.result:
            # Problems during catalog generation
            # jobs.results is a list - the first entry is the intended title for the dialog
            # Subsequent strings are error messages
            dialog_title = job.result.pop(0)
            if re.match('warning:', job.result[0].lower()):
                msg = _("Catalog generation complete, with warnings.")
                warning_dialog(self.gui,
                               dialog_title,
                               msg,
                               det_msg='\n'.join(job.result),
                               show=True)
            else:
                job.result.append("Catalog generation terminated.")
                error_dialog(self.gui,
                             dialog_title,
                             '\n'.join(job.result),
                             show=True)
                return

        if job.failed:
            return self.gui.job_exception(job)
        id = self.gui.library_view.model().add_catalog(job.catalog_file_path,
                                                       job.catalog_title)
        self.gui.library_view.model().reset()
        if job.catalog_sync:
            sync = dynamic.get('catalogs_to_be_synced', set([]))
            sync.add(id)
            dynamic.set('catalogs_to_be_synced', sync)
        self.gui.status_bar.show_message(_('Catalog generated.'), 3000)
        self.gui.sync_catalogs()
        if job.fmt not in ['EPUB', 'MOBI']:
            export_dir = choose_dir(
                self.gui, _('Export Catalog Directory'),
                _('Select destination for %(title)s.%(fmt)s') %
                dict(title=job.catalog_title, fmt=job.fmt.lower()))
            if export_dir:
                destination = os.path.join(
                    export_dir, '%s.%s' % (sanitize_file_name_unicode(
                        job.catalog_title), job.fmt.lower()))
                shutil.copyfile(job.catalog_file_path, destination)
Пример #54
0
 def start_content_server(self, check_started=True):
     from calibre.srv.embedded import Server
     if not gprefs.get('server3_warning_done', False):
         gprefs.set('server3_warning_done', True)
         if os.path.exists(os.path.join(config_dir, 'server.py')):
             try:
                 os.remove(os.path.join(config_dir, 'server.py'))
             except EnvironmentError:
                 pass
             warning_dialog(self, _('Content server changed!'), _(
                 'calibre 3 comes with a completely re-written content server.'
                 ' As such any custom configuration you have for the content'
                 ' server no longer applies. You should check and refresh your'
                 ' settings in Preferences->Sharing->Sharing over the net'), show=True)
     self.content_server = Server(self.library_broker, Dispatcher(self.handle_changes_from_server))
     self.content_server.state_callback = Dispatcher(
             self.iactions['Connect Share'].content_server_state_changed)
     if check_started:
         self.content_server.start_failure_callback = \
             Dispatcher(self.content_server_start_failed)
     self.content_server.start()
Пример #55
0
    def do_test(self):
        from calibre.ebooks.metadata.meta import metadata_from_filename

        fname = unicode(self.filename.text())
        ext = os.path.splitext(fname)[1][1:].lower()
        if ext not in BOOK_EXTENSIONS:
            return warning_dialog(
                self,
                _("Test name invalid"),
                _(
                    "The name <b>%r</b> does not appear to end with a"
                    " file extension. The name must end with a file "
                    " extension like .epub or .mobi"
                )
                % fname,
                show=True,
            )

        try:
            pat = self.pattern()
        except Exception as err:
            error_dialog(self, _("Invalid regular expression"), _("Invalid regular expression: %s") % err).exec_()
            return
        mi = metadata_from_filename(fname, pat)
        if mi.title:
            self.title.setText(mi.title)
        else:
            self.title.setText(_("No match"))
        if mi.authors:
            self.authors.setText(", ".join(mi.authors))
        else:
            self.authors.setText(_("No match"))

        if mi.series:
            self.series.setText(mi.series)
        else:
            self.series.setText(_("No match"))

        if mi.series_index is not None:
            self.series_index.setText(str(mi.series_index))
        else:
            self.series_index.setText(_("No match"))

        if mi.publisher:
            self.publisher.setText(mi.publisher)

        if mi.pubdate:
            self.pubdate.setText(mi.pubdate.strftime("%Y-%m-%d"))

        self.isbn.setText(_("No match") if mi.isbn is None else str(mi.isbn))
Пример #56
0
    def do_test(self):
        from calibre.ebooks.metadata import authors_to_string
        from calibre.ebooks.metadata.meta import metadata_from_filename
        fname = unicode_type(self.filename.text())
        ext = os.path.splitext(fname)[1][1:].lower()
        if ext not in BOOK_EXTENSIONS:
            return warning_dialog(self, _('Test file name invalid'),
                    _('The file name <b>%s</b> does not appear to end with a'
                        ' file extension. It must end with a file '
                        ' extension like .epub or .mobi')%fname, show=True)

        try:
            pat = self.pattern()
        except Exception as err:
            error_dialog(self, _('Invalid regular expression'),
                         _('Invalid regular expression: %s')%err).exec_()
            return
        mi = metadata_from_filename(fname, pat)
        if mi.title:
            self.title.setText(mi.title)
        else:
            self.title.setText(_('No match'))
        if mi.authors:
            self.authors.setText(authors_to_string(mi.authors))
        else:
            self.authors.setText(_('No match'))

        if mi.series:
            self.series.setText(mi.series)
        else:
            self.series.setText(_('No match'))

        if mi.series_index is not None:
            self.series_index.setText(str(mi.series_index))
        else:
            self.series_index.setText(_('No match'))

        if mi.publisher:
            self.publisher.setText(mi.publisher)
        else:
            self.publisher.setText(_('No match'))

        if mi.pubdate:
            self.pubdate.setText(strftime('%Y-%m-%d', mi.pubdate))
        else:
            self.pubdate.setText(_('No match'))

        self.isbn.setText(_('No match') if mi.isbn is None else str(mi.isbn))
        self.comments.setText(mi.comments if mi.comments else _('No match'))
Пример #57
0
    def commit(self, *args):
        try:
            must_restart = self.showing_widget.commit()
        except AbortCommit:
            return
        rc = self.showing_widget.restart_critical
        self.committed = True
        do_restart = False
        if must_restart:
            self.must_restart = True
            msg = _('Some of the changes you made require a restart.'
                    ' Please restart calibre as soon as possible.')
            if rc:
                msg = _('The changes you have made require calibre be '
                        'restarted immediately. You will not be allowed to '
                        'set any more preferences, until you restart.')


            d = warning_dialog(self, _('Restart needed'), msg,
                    show_copy_button=False)
            b = d.bb.addButton(_('Restart calibre now'), d.bb.AcceptRole)
            b.setIcon(QIcon(I('lt.png')))
            d.do_restart = False
            def rf():
                d.do_restart = True
            b.clicked.connect(rf)
            d.set_details('')
            d.exec_()
            b.clicked.disconnect()
            do_restart = d.do_restart
        self.showing_widget.refresh_gui(self.gui)
        self.hide_plugin()
        if self.close_after_initial or (must_restart and rc) or do_restart:
            self.close()
        if do_restart:
            self.gui.quit(restart=True)
Пример #58
0
    def do_user_config(self, parent=None):
        '''
        This method shows a configuration dialog for this plugin. It returns
        True if the user clicks OK, False otherwise. The changes are
        automatically applied.
        '''
        from PyQt5.Qt import QDialog, QDialogButtonBox, QVBoxLayout, \
                QLabel, Qt, QLineEdit
        from calibre.gui2 import gprefs

        prefname = 'plugin config dialog:'+self.type + ':' + self.name
        geom = gprefs.get(prefname, None)

        config_dialog = QDialog(parent)
        button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        v = QVBoxLayout(config_dialog)

        def size_dialog():
            if geom is None:
                config_dialog.resize(config_dialog.sizeHint())
            else:
                config_dialog.restoreGeometry(geom)

        button_box.accepted.connect(config_dialog.accept)
        button_box.rejected.connect(config_dialog.reject)
        config_dialog.setWindowTitle(_('Customize') + ' ' + self.name)
        try:
            config_widget = self.config_widget()
        except NotImplementedError:
            config_widget = None

        if isinstance(config_widget, tuple):
            from calibre.gui2 import warning_dialog
            warning_dialog(parent, _('Cannot configure'), config_widget[0],
                    det_msg=config_widget[1], show=True)
            return False

        if config_widget is not None:
            v.addWidget(config_widget)
            v.addWidget(button_box)
            size_dialog()
            config_dialog.exec_()

            if config_dialog.result() == QDialog.Accepted:
                if hasattr(config_widget, 'validate'):
                    if config_widget.validate():
                        self.save_settings(config_widget)
                else:
                    self.save_settings(config_widget)
        else:
            from calibre.customize.ui import plugin_customization, \
                customize_plugin
            help_text = self.customization_help(gui=True)
            help_text = QLabel(help_text, config_dialog)
            help_text.setWordWrap(True)
            help_text.setTextInteractionFlags(Qt.LinksAccessibleByMouse | Qt.LinksAccessibleByKeyboard)
            help_text.setOpenExternalLinks(True)
            v.addWidget(help_text)
            sc = plugin_customization(self)
            if not sc:
                sc = ''
            sc = sc.strip()
            sc = QLineEdit(sc, config_dialog)
            v.addWidget(sc)
            v.addWidget(button_box)
            size_dialog()
            config_dialog.exec_()

            if config_dialog.result() == QDialog.Accepted:
                sc = unicode(sc.text()).strip()
                customize_plugin(self, sc)

        geom = bytearray(config_dialog.saveGeometry())
        gprefs[prefname] = geom

        return config_dialog.result()
Пример #59
0
 def commit(self):
     ConfigWidgetBase.commit(self)
     warning_dialog(self, _('Restart needed'),
             _('You need to restart the server for changes to'
                 ' take effect'), show=True)
     return False
Пример #60
0
    def send_by_mail(self, to, fmts, delete_from_library, subject='', send_ids=None,
            do_auto_convert=True, specific_format=None):
        ids = [self.library_view.model().id(r) for r in self.library_view.selectionModel().selectedRows()] if send_ids is None else send_ids
        if not ids or len(ids) == 0:
            return

        files, _auto_ids = self.library_view.model().get_preferred_formats_from_ids(ids,
                                    fmts, set_metadata=True,
                                    specific_format=specific_format,
                                    exclude_auto=do_auto_convert,
                                    use_plugboard=plugboard_email_value,
                                    plugboard_formats=plugboard_email_formats)
        if do_auto_convert:
            nids = list(set(ids).difference(_auto_ids))
            ids = [i for i in ids if i in nids]
        else:
            _auto_ids = []

        full_metadata = self.library_view.model().metadata_for(ids,
                get_cover=False)

        bad, remove_ids, jobnames = [], [], []
        texts, subjects, attachments, attachment_names = [], [], [], []
        for f, mi, id in zip(files, full_metadata, ids):
            t = mi.title
            if not t:
                t = _('Unknown')
            if f is None:
                bad.append(t)
            else:
                remove_ids.append(id)
                jobnames.append(t)
                attachments.append(f)
                if not subject:
                    subjects.append(_('E-book:')+ ' '+t)
                else:
                    components = get_components(subject, mi, id)
                    if not components:
                        components = [mi.title]
                    subjects.append(os.path.join(*components))
                a = authors_to_string(mi.authors if mi.authors else
                        [_('Unknown')])
                texts.append(_('Attached, you will find the e-book') +
                        '\n\n' + t + '\n\t' + _('by') + ' ' + a + '\n\n' +
                        _('in the %s format.') %
                        os.path.splitext(f)[1][1:].upper())
                prefix = ascii_filename(t+' - '+a)
                if not isinstance(prefix, unicode):
                    prefix = prefix.decode(preferred_encoding, 'replace')
                attachment_names.append(prefix + os.path.splitext(f)[1])
        remove = remove_ids if delete_from_library else []

        to_s = list(repeat(to, len(attachments)))
        if attachments:
            send_mails(jobnames,
                    Dispatcher(partial(self.email_sent, remove=remove)),
                    attachments, to_s, subjects, texts, attachment_names,
                    self.job_manager)
            self.status_bar.show_message(_('Sending email to')+' '+to, 3000)

        auto = []
        if _auto_ids != []:
            for id in _auto_ids:
                if specific_format is None:
                    dbfmts = self.library_view.model().db.formats(id, index_is_id=True)
                    formats = [f.lower() for f in (dbfmts.split(',') if dbfmts else
                        [])]
                    if list(set(formats).intersection(available_input_formats())) != [] and list(set(fmts).intersection(available_output_formats())) != []:
                        auto.append(id)
                    else:
                        bad.append(self.library_view.model().db.title(id, index_is_id=True))
                else:
                    if specific_format in list(set(fmts).intersection(set(available_output_formats()))):
                        auto.append(id)
                    else:
                        bad.append(self.library_view.model().db.title(id, index_is_id=True))

        if auto != []:
            format = specific_format if specific_format in list(set(fmts).intersection(set(available_output_formats()))) else None
            if not format:
                for fmt in fmts:
                    if fmt in list(set(fmts).intersection(set(available_output_formats()))):
                        format = fmt
                        break
            if format is None:
                bad += auto
            else:
                autos = [self.library_view.model().db.title(id, index_is_id=True) for id in auto]
                if self.auto_convert_question(
                    _('Auto convert the following books to %s before sending via '
                        'email?') % format.upper(), autos):
                    self.iactions['Convert Books'].auto_convert_mail(to, fmts, delete_from_library, auto, format, subject)

        if bad:
            bad = '\n'.join('%s'%(i,) for i in bad)
            d = warning_dialog(self, _('No suitable formats'),
                _('Could not email the following books '
                'as no suitable formats were found:'), bad)
            d.exec_()