Exemplo n.º 1
0
 def download_metadata(self, ids=None, ensure_fields=None):
     if ids is None:
         rows = self.gui.library_view.selectionModel().selectedRows()
         if not rows or len(rows) == 0:
             return error_dialog(self.gui,
                                 _('Cannot download metadata'),
                                 _('No books selected'),
                                 show=True)
         db = self.gui.library_view.model().db
         ids = [db.id(row.row()) for row in rows]
     from calibre.gui2.metadata.bulk_download import start_download
     start_download(self.gui,
                    ids,
                    Dispatcher(self.metadata_downloaded),
                    ensure_fields=ensure_fields)
Exemplo n.º 2
0
    def parse(self):
        # get currently selected books
        rows = self.gui.library_view.selectionModel().selectedRows()
        if not rows or len(rows) == 0:
            return
        self.ids = list(map(self.gui.library_view.model().id, rows))
        if len(self.ids) == 0:
            return

        job = ThreadedJob('Generating Word Wise', 'Generating Word Wise',
                          do_job, (self.gui.current_db.new_api,
                                   self.ids, self.plugin_path), {},
                          Dispatcher(self.done))

        self.gui.job_manager.run_threaded_job(job)
        self.gui.status_bar.show_message("Generating Word Wise")
Exemplo n.º 3
0
 def download_ebook(self,
                    url='',
                    cookie_file=None,
                    filename='',
                    save_loc='',
                    add_to_lib=True,
                    tags=[]):
     if tags:
         if isinstance(tags, basestring):
             tags = tags.split(',')
     start_ebook_download(Dispatcher(self.downloaded_ebook),
                          self.job_manager, self, cookie_file, url,
                          filename, save_loc, add_to_lib, tags)
     self.status_bar.show_message(
         _('Downloading') + ' ' + filename.decode('utf-8', 'ignore')
         if filename else url.decode('utf-8', 'ignore'), 3000)
Exemplo n.º 4
0
    def scan_for_isbns(self):
        rows = self.gui.library_view.selectionModel().selectedRows()
        if not rows or len(rows) == 0:
            return error_dialog(self.gui, 'No rows selected',
                                'You must select one or more books to perform this action.', show=True)
        book_ids = self.gui.library_view.get_selected_ids()
        db = self.gui.library_view.model().db

        c = cfg.plugin_prefs[cfg.STORE_NAME]
        worker_threshold = c.get(cfg.KEY_WORKER_THRESHOLD, cfg.DEFAULT_STORE_VALUES[cfg.KEY_WORKER_THRESHOLD])
        if len(book_ids) > worker_threshold:
            # Run the extraction as a background job with workers
            QueueProgressDialog(self.gui, book_ids, self._queue_job, db)
        else:
            # For performance reasons, still do single book extraction as a threaded
            # job in-process
            start_extract_threaded(self.gui, book_ids, Dispatcher(self._scan_for_isbns_complete))
Exemplo n.º 5
0
 def do_polish(self, book_id_map):
     d = Polish(self.gui.library_view.model().db,
                book_id_map,
                parent=self.gui)
     if d.exec_() == d.Accepted and d.jobs:
         show_reports = bool(d.show_reports.isChecked())
         for desc, data, book_id, base, is_orig in reversed(d.jobs):
             job = self.gui.job_manager.run_job(Dispatcher(
                 self.book_polished),
                                                'gui_polish',
                                                args=(data, ),
                                                description=desc)
             job.polish_args = (book_id, base, data['files'], show_reports,
                                is_orig)
         if d.jobs:
             self.gui.jobs_pointer.start()
             self.gui.status_bar.show_message(
                 _('Start polishing of %d book(s)') % len(d.jobs), 2000)
Exemplo n.º 6
0
 def download_ebook(self,
                    url='',
                    cookie_file=None,
                    filename='',
                    save_loc='',
                    add_to_lib=True,
                    tags=[],
                    create_browser=None):
     if tags:
         if isinstance(tags, string_or_bytes):
             tags = tags.split(',')
     start_ebook_download(Dispatcher(self.downloaded_ebook),
                          self.job_manager, self, cookie_file, url,
                          filename, save_loc, add_to_lib, tags,
                          create_browser)
     self.status_bar.show_message(
         _('Downloading') + ' ' +
         as_unicode(filename or url, errors='replace'), 3000)
Exemplo n.º 7
0
    def save_to_disk(self, checked, single_dir=False, single_format=None,
            rows=None, write_opf=None, save_cover=None):
        if rows is None:
            rows = self.gui.current_view().selectionModel().selectedRows()
        if not rows or len(rows) == 0:
            return error_dialog(self.gui, _('Cannot save to disk'),
                    _('No books selected'), show=True)
        path = choose_dir(self.gui, 'save to disk dialog',
                _('Choose destination directory'))
        if not path:
            return
        dpath = os.path.abspath(path).replace('/', os.sep)+os.sep
        lpath = self.gui.library_view.model().db.library_path.replace('/',
                os.sep)+os.sep
        if dpath.startswith(lpath):
            return error_dialog(self.gui, _('Not allowed'),
                    _('You are trying to save files into the calibre '
                      'library. This can cause corruption of your '
                      'library. Save to disk is meant to export '
                      'files from your calibre library elsewhere.'), show=True)

        if self.gui.current_view() is self.gui.library_view:
            from calibre.gui2.save import Saver
            from calibre.library.save_to_disk import config
            opts = config().parse()
            if single_format is not None:
                opts.formats = single_format
                # Special case for Kindle annotation files
                if single_format.lower() in ['mbp','pdr','tan']:
                    opts.to_lowercase = False
                    opts.save_cover = False
                    opts.write_opf = False
                    opts.template = opts.send_template
            opts.single_dir = single_dir
            if write_opf is not None:
                opts.write_opf = write_opf
            if save_cover is not None:
                opts.save_cover = save_cover
            book_ids = set(map(self.gui.library_view.model().id, rows))
            Saver(book_ids, self.gui.current_db, opts, path, parent=self.gui, pool=self.gui.spare_pool())
        else:
            paths = self.gui.current_view().model().paths(rows)
            self.gui.device_manager.save_books(
                    Dispatcher(self.books_saved), paths, path)
Exemplo n.º 8
0
    def __init__(self):
        QAbstractTableModel.__init__(self)
        SearchQueryParser.__init__(self, ['all'])

        self.wait_icon = (QIcon(I('jobs.png')))
        self.running_icon = (QIcon(I('exec.png')))
        self.error_icon = (QIcon(I('dialog_error.png')))
        self.done_icon = (QIcon(I('ok.png')))

        self.jobs = []
        self.add_job = Dispatcher(self._add_job)
        self.server = Server(limit=int(config['worker_limit'] / 2.0),
                             enforce_cpu_limit=config['enforce_cpu_limit'])
        self.threaded_server = ThreadedJobServer()
        self.changed_queue = Queue()

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update, type=Qt.QueuedConnection)
        self.timer.start(1000)
Exemplo n.º 9
0
class ParseBook():
    def __init__(self, gui):
        self.gui = gui
        self.metadata_list = []

    def parse(self, create_ww=True, create_x=True):
        # get currently selected books
        rows = self.gui.library_view.selectionModel().selectedRows()
        if not rows or len(rows) == 0:
            return
        ids = list(map(self.gui.library_view.model().id, rows))
        if len(ids) == 0:
            return

        for book_id in ids:
            if (data := check_metadata(self.gui.current_db.new_api,
                                       book_id)) is None:
                continue
            self.metadata_list.append(data)

        if len(self.metadata_list) == 0:
            return

        for data in self.metadata_list:
            if data[-1]['wiki'] != 'en':
                create_ww = False
            title = data[-2].get('title')
            notif = []
            if create_ww:
                notif.append('Word Wise')
            if create_x:
                notif.append('X-Ray')
            notif = ' and '.join(notif)

            job = ThreadedJob(
                "WordDumb's dumb job", f'Generating {notif} for {title}',
                do_job, (data, create_ww, create_x), {},
                Dispatcher(partial(self.done, data=data,
                                   notif=f'{notif} generated for {title}')))
            self.gui.job_manager.run_threaded_job(job)

        self.gui.jobs_pointer.start()
Exemplo n.º 10
0
    def _view_books(self, rows):
        if not rows or len(rows) == 0:
            self._launch_viewer()
            return

        if not self._view_check(len(rows)):
            return

        if self.gui.current_view() is self.gui.library_view:
            ids = list(map(self.gui.library_view.model().id, rows))
            self._view_calibre_books(ids)
        else:
            paths = self.gui.current_view().model().paths(rows)
            for path in paths:
                pt = PersistentTemporaryFile('_viewer_'+\
                        os.path.splitext(path)[1])
                self.persistent_files.append(pt)
                pt.close()
                self.gui.device_manager.view_book(\
                        Dispatcher(self.book_downloaded_for_viewing),
                                              path, pt.name)
Exemplo n.º 11
0
    def convert_vtt_files(self):
        main_lang = self.main_lang_combo.currentData()
        if main_lang == '-':
            QMessageBox.about(self, 'Information',
                              'Select the main language before conversion.')
            return

        sub_lang = str(self.sub_lang_combo.currentData())
        # if user does not select sub_lang, set it to main_lang, so that when converting, it won't generate sub language.
        if sub_lang == '-':
            sub_lang = main_lang

        # convert to html
        if hasattr(self, 'cover_file_path'):
            cover_file_path = self.cover_file_path
        else:
            cover_file_path = None

        self.book_id = self.convert_to_html_add_to_library(
            self.vtt_dir, main_lang, sub_lang, cover_file_path)

        # add html to epub conversion job
        self.jobs, changed, bad = convert_single_ebook(
            self.gui,
            self.gui.library_view.model().db, [self.book_id], True,
            self.outputFmt)
        func, args, desc, fmt, id, temp_files = self.jobs[0]

        core_usage = 1
        plugin = plugin_for_input_format('html')
        if plugin is not None:
            core_usage = plugin.core_usage

        self.gui.job_manager.run_job(Dispatcher(self.converted_func),
                                     func,
                                     args=args,
                                     description=desc,
                                     core_usage=core_usage)

        self.close()
Exemplo n.º 12
0
    def queue_convert_jobs(self,
                           jobs,
                           changed,
                           bad,
                           rows,
                           previous,
                           converted_func,
                           extra_job_args=[],
                           rows_are_ids=False):
        for func, args, desc, fmt, id, temp_files in jobs:
            func, _, parts = func.partition(':')
            parts = {x for x in parts.split(';')}
            input_file = args[0]
            input_fmt = os.path.splitext(input_file)[1]
            core_usage = 1
            if input_fmt:
                input_fmt = input_fmt[1:]
                plugin = plugin_for_input_format(input_fmt)
                if plugin is not None:
                    core_usage = plugin.core_usage

            if id not in bad:
                job = self.gui.job_manager.run_job(Dispatcher(converted_func),
                                                   func,
                                                   args=args,
                                                   description=desc,
                                                   core_usage=core_usage)
                job.conversion_of_same_fmt = 'same_fmt' in parts
                job.manually_fine_tune_toc = 'manually_fine_tune_toc' in parts
                args = [temp_files, fmt, id] + extra_job_args
                self.conversion_jobs[job] = tuple(args)

        if changed:
            m = self.gui.library_view.model()
            if rows_are_ids:
                m.refresh_ids(rows)
            else:
                m.refresh_rows(rows)
            current = self.gui.library_view.currentIndex()
            self.gui.library_view.model().current_changed(current, previous)
    def enqueue(self, account, downloader):
        prefs = self.prefs

        self.notify("Account: '%s'" % account[prefs.USERNAME])
        # downloader.login(account)

        func = 'arbitrary_n'
        # func = 'arbitrary'
        cpus = self.gui.job_manager.server.pool_size
        print "CPUs: %s" % (cpus)
        args = [
            'calibre_plugins.beam_ebooks_downloader.jobs',
            'do_obtain_new_books', (cpus, account)
        ]
        desc = 'Beam EBooks Downloader'
        job = self.gui.job_manager.run_job(Dispatcher(self._done),
                                           func,
                                           args=args,
                                           description=desc)
        print "Job: %s" % (job)

        self.notify("  Start parsing OPDS catalog")
    def _add_ebooks(self, payload):
        print "Done Downloading, Step 2"
        print "Self: %s" % (self)
        # print "Payload: %s" % (payload)
        # Printing the complete payload at once gives [IOError 12] - Out of space (memory, that is...)
        self.notify("  Finished adding books...")
        for entry in payload:
            print "Entry: %s" % entry

            # TODO allow for checkbox list of entries to download
            func = 'arbitrary_n'
            # func = 'arbitrary'
            cpus = self.gui.job_manager.server.pool_size
            print "CPUs: %s" % (cpus)
            args = [
                'calibre_plugins.beam_ebooks_downloader.jobs',
                'do_download_book', (cpus, entry)
            ]
            desc = 'Beam EBooks Downloader'
            job = self.gui.job_manager.run_job(Dispatcher(self._done_2),
                                               func,
                                               args=args,
                                               description=desc)
            print "Job: %s" % (job)
Exemplo n.º 15
0
    def build_menus(self):
        if os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
            return
        db = self.gui.library_view.model().db
        locations = list(self.stats.locations(db))

        for ac in self.switch_actions:
            ac.setVisible(False)
        self.quick_menu.clear()
        self.rename_menu.clear()
        self.delete_menu.clear()
        quick_actions, rename_actions, delete_actions = [], [], []
        for name, loc in locations:
            is_prev_lib = name == self.prev_lname
            name = name.replace('&', '&&')
            ac = self.quick_menu.addAction(
                name, Dispatcher(partial(self.switch_requested, loc)))
            ac.setStatusTip(_('Switch to: %s') % loc)
            if is_prev_lib:
                f = ac.font()
                f.setBold(True)
                ac.setFont(f)
            quick_actions.append(ac)
            ac = self.rename_menu.addAction(
                name, Dispatcher(partial(self.rename_requested, name, loc)))
            rename_actions.append(ac)
            ac.setStatusTip(_('Rename: %s') % loc)
            ac = self.delete_menu.addAction(
                name, Dispatcher(partial(self.delete_requested, name, loc)))
            delete_actions.append(ac)
            ac.setStatusTip(_('Remove: %s') % loc)
            if is_prev_lib:
                ac.setFont(f)

        qs_actions = []
        locations_by_frequency = locations
        if len(locations) >= tweaks['many_libraries']:
            locations_by_frequency = list(
                self.stats.locations(db, limit=sys.maxsize))
        for i, x in enumerate(
                locations_by_frequency[:len(self.switch_actions)]):
            name, loc = x
            name = name.replace('&', '&&')
            ac = self.switch_actions[i]
            ac.setText(name)
            ac.setStatusTip(_('Switch to: %s') % loc)
            ac.setVisible(True)
            qs_actions.append(ac)
        self.qs_locations = [i[1] for i in locations_by_frequency]

        self.quick_menu_action.setVisible(bool(locations))
        self.rename_menu_action.setVisible(bool(locations))
        self.delete_menu_action.setVisible(bool(locations))
        self.gui.location_manager.set_switch_actions(quick_actions,
                                                     rename_actions,
                                                     delete_actions,
                                                     qs_actions,
                                                     self.action_choose)
        # Allow the cloned actions in the OS X global menubar to update
        for a in (self.qaction, self.menuless_qaction):
            a.changed.emit()
Exemplo n.º 16
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 == 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 before sending via '
                          'email?'), 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_()
Exemplo n.º 17
0
    def do_copy(self, ids, db, loc, delete_after, add_duplicates=False):
        aname = _('Moving to') if delete_after else _('Copying to')
        dtitle = '%s %s' % (aname, os.path.basename(loc))
        self.pd = ProgressDialog(dtitle,
                                 min=0,
                                 max=len(ids) - 1,
                                 parent=self.gui,
                                 cancelable=True,
                                 icon='lt.png')

        def progress(idx, title):
            self.pd.set_msg(title)
            self.pd.set_value(idx)

        self.worker = Worker(ids, db, loc, Dispatcher(progress),
                             Dispatcher(self.pd.accept), delete_after,
                             add_duplicates)
        self.worker.start()
        self.pd.canceled_signal.connect(self.worker.cancel_processing)

        self.pd.exec_()
        self.pd.canceled_signal.disconnect()

        if self.worker.left_after_cancel:
            msg = _(
                'The copying process was interrupted. {} books were copied.'
            ).format(len(self.worker.processed))
            if delete_after:
                msg += ' ' + _('No books were deleted from this library.')
            msg += ' ' + _(
                'The best way to resume this operation is to re-copy all the books with the option to'
                ' "Check for duplicates when copying to library" in Preferences->Import/export->Adding books turned on.'
            )
            warning_dialog(self.gui, _('Canceled'), msg, show=True)
            return

        if self.worker.error is not None:
            e, tb = self.worker.error
            error_dialog(self.gui,
                         _('Failed'),
                         _('Could not copy books: ') + e,
                         det_msg=tb,
                         show=True)
            return

        if delete_after:
            donemsg = ngettext('Moved the book to {loc}',
                               'Moved {num} books to {loc}',
                               len(self.worker.processed))
        else:
            donemsg = ngettext('Copied the book to {loc}',
                               'Copied {num} books to {loc}',
                               len(self.worker.processed))

        self.gui.status_bar.show_message(
            donemsg.format(num=len(self.worker.processed), loc=loc), 2000)
        if self.worker.auto_merged_ids:
            books = '\n'.join(self.worker.auto_merged_ids.itervalues())
            info_dialog(self.gui,
                        _('Auto merged'),
                        _('Some books were automatically merged into existing '
                          'records in the target library. Click "Show '
                          'details" to see which ones. This behavior is '
                          'controlled by the Auto-merge option in '
                          'Preferences->Import/export->Adding books.'),
                        det_msg=books,
                        show=True)
        if delete_after and self.worker.processed:
            v = self.gui.library_view
            ci = v.currentIndex()
            row = None
            if ci.isValid():
                row = ci.row()

            v.model().delete_books_by_id(self.worker.processed, permanent=True)
            self.gui.iactions['Remove Books'].library_ids_deleted(
                self.worker.processed, row)

        if self.worker.failed_books:

            def fmt_err(book_id):
                err, tb = self.worker.failed_books[book_id]
                title = db.title(book_id, index_is_id=True)
                return _('Copying: {0} failed, with error:\n{1}').format(
                    title, tb)

            title, msg = _('Failed to copy some books'), _(
                'Could not copy some books, click "Show Details" for more information.'
            )
            tb = '\n\n'.join(map(fmt_err, self.worker.failed_books))
            tb = ngettext('Failed to copy a book, see below for details',
                          'Failed to copy {} books, see below for details',
                          len(self.worker.failed_books)).format(
                              len(self.worker.failed_books)) + '\n\n' + tb
            if len(ids) == len(self.worker.failed_books):
                title, msg = _('Failed to copy books'), _(
                    'Could not copy any books, click "Show Details" for more information.'
                )
            error_dialog(self.gui, title, msg, det_msg=tb, show=True)
        return self.worker.duplicate_ids
Exemplo n.º 18
0
        for book_id in ids:
            if (data := check_metadata(self.gui.current_db.new_api,
                                       book_id)) is None:
                continue
            self.metadata_list.append(data)

        if (books := len(self.metadata_list)) == 0:
            return

        if books == 1:
            self.create_jobs(install=True)
        else:
            job = ThreadedJob("WordDumb's dumb job",
                              'Install dependencies', install_libs, (), {},
                              Dispatcher(self.create_jobs))
            self.gui.job_manager.run_threaded_job(job)

        self.gui.jobs_pointer.start()
        self.gui.status_bar.show_message(f'Generating Word Wise for {books} '
                                         f'{"books" if books > 1 else "book"}')

    def create_jobs(self, job=None, install=False):
        if self.job_failed(job):
            return

        for data in self.metadata_list:
            title = data[-1].get('title')
            job = ThreadedJob(
                "WordDumb's dumb job", f'Generating Word Wise for {title}',
                do_job, (data, install), {},
Exemplo n.º 19
0
 def __call__(self, name, user_text, callback, function, *args, **kwargs):
     ' Run a job that blocks the GUI providing some feedback to the user '
     self.set_msg(user_text)
     job = LongJob(name, user_text, Dispatcher(partial(self.job_done, callback)), function, *args, **kwargs)
     job.start()
     self.start()
Exemplo n.º 20
0
    def do_copy(self, ids, db, loc, delete_after, add_duplicates=False):
        aname = _('Moving to') if delete_after else _('Copying to')
        dtitle = '%s %s' % (aname, os.path.basename(loc))
        self.pd = ProgressDialog(dtitle,
                                 min=0,
                                 max=len(ids) - 1,
                                 parent=self.gui,
                                 cancelable=False,
                                 icon='lt.png')

        def progress(idx, title):
            self.pd.set_msg(title)
            self.pd.set_value(idx)

        self.worker = Worker(ids, db, loc, Dispatcher(progress),
                             Dispatcher(self.pd.accept), delete_after,
                             add_duplicates)
        self.worker.start()

        self.pd.exec_()

        donemsg = _('Copied %(num)d books to %(loc)s')
        if delete_after:
            donemsg = _('Moved %(num)d books to %(loc)s')

        if self.worker.error is not None:
            e, tb = self.worker.error
            error_dialog(self.gui,
                         _('Failed'),
                         _('Could not copy books: ') + e,
                         det_msg=tb,
                         show=True)
            return

        self.gui.status_bar.show_message(
            donemsg % dict(num=len(self.worker.processed), loc=loc), 2000)
        if self.worker.auto_merged_ids:
            books = '\n'.join(self.worker.auto_merged_ids.itervalues())
            info_dialog(self.gui,
                        _('Auto merged'),
                        _('Some books were automatically merged into existing '
                          'records in the target library. Click Show '
                          'details to see which ones. This behavior is '
                          'controlled by the Auto merge option in '
                          'Preferences->Adding books.'),
                        det_msg=books,
                        show=True)
        if delete_after and self.worker.processed:
            v = self.gui.library_view
            ci = v.currentIndex()
            row = None
            if ci.isValid():
                row = ci.row()

            v.model().delete_books_by_id(self.worker.processed, permanent=True)
            self.gui.iactions['Remove Books'].library_ids_deleted(
                self.worker.processed, row)

        if self.worker.failed_books:

            def fmt_err(book_id):
                err, tb = self.worker.failed_books[book_id]
                title = db.title(book_id, index_is_id=True)
                return _('Copying: {0} failed, with error:\n{1}').format(
                    title, tb)

            title, msg = _('Failed to copy some books'), _(
                'Could not copy some books, click "Show Details" for more information.'
            )
            tb = '\n\n'.join(map(fmt_err, self.worker.failed_books))
            tb = _('Failed to copy {0} book(s), see below for details').format(
                len(self.worker.failed_books)) + '\n\n' + tb
            if len(ids) == len(self.worker.failed_books):
                title, msg = _('Failed to copy books'), _(
                    'Could not copy any books, click "Show Details" for more information.'
                )
            error_dialog(self.gui, title, msg, det_msg=tb, show=True)
        return self.worker.duplicate_ids
Exemplo n.º 21
0
    def copy_to_library(self, loc, delete_after=False):
        rows = self.gui.library_view.selectionModel().selectedRows()
        if not rows or len(rows) == 0:
            return error_dialog(self.gui,
                                _('Cannot copy'),
                                _('No books selected'),
                                show=True)
        ids = list(map(self.gui.library_view.model().id, rows))
        db = self.gui.library_view.model().db
        if not db.exists_at(loc):
            return error_dialog(self.gui,
                                _('No library'),
                                _('No library found at %s') % loc,
                                show=True)

        self.pd = ProgressDialog(_('Copying'),
                                 min=0,
                                 max=len(ids) - 1,
                                 parent=self.gui,
                                 cancelable=False)

        def progress(idx, title):
            self.pd.set_msg(_('Copying') + ' ' + title)
            self.pd.set_value(idx)

        self.worker = Worker(ids, db, loc, Dispatcher(progress),
                             Dispatcher(self.pd.accept), delete_after)
        self.worker.start()

        self.pd.exec_()

        if self.worker.error is not None:
            e, tb = self.worker.error
            error_dialog(self.gui,
                         _('Failed'),
                         _('Could not copy books: ') + e,
                         det_msg=tb,
                         show=True)
        else:
            self.gui.status_bar.show_message(
                _('Copied %(num)d books to %(loc)s') %
                dict(num=len(ids), loc=loc), 2000)
            if self.worker.auto_merged_ids:
                books = '\n'.join(self.worker.auto_merged_ids.itervalues())
                info_dialog(
                    self.gui,
                    _('Auto merged'),
                    _('Some books were automatically merged into existing '
                      'records in the target library. Click Show '
                      'details to see which ones. This behavior is '
                      'controlled by the Auto merge option in '
                      'Preferences->Adding books.'),
                    det_msg=books,
                    show=True)
            if delete_after and self.worker.processed:
                v = self.gui.library_view
                ci = v.currentIndex()
                row = None
                if ci.isValid():
                    row = ci.row()

                v.model().delete_books_by_id(self.worker.processed,
                                             permanent=True)
                self.gui.iactions['Remove Books'].library_ids_deleted(
                    self.worker.processed, row)
Exemplo n.º 22
0
 def refresh_gui(self, gui):
     gui.content_server = self.server
     if gui.content_server is not None:
         gui.content_server.state_callback = \
             Dispatcher(gui.iactions['Connect Share'].content_server_state_changed)
         gui.content_server.state_callback(gui.content_server.is_running)
Exemplo n.º 23
0
    def build_menus(self):
        if os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
            return
        db = self.gui.library_view.model().db
        lname = self.stats.library_used(db)
        self.vl_to_apply_action.setText(
            _('Apply Virtual library when %s is opened') % lname)
        locations = list(self.stats.locations(db))

        for ac in self.switch_actions:
            ac.setVisible(False)
        self.quick_menu.clear()
        self.rename_menu.clear()
        self.delete_menu.clear()
        quick_actions, rename_actions, delete_actions = [], [], []
        for name, loc in locations:
            is_prev_lib = name == self.prev_lname
            ic = library_qicon(name)
            name = name.replace('&', '&&')
            ac = self.quick_menu.addAction(
                ic, name, Dispatcher(partial(self.switch_requested, loc)))
            ac.setStatusTip(_('Switch to: %s') % loc)
            if is_prev_lib:
                f = ac.font()
                f.setBold(True)
                ac.setFont(f)
            quick_actions.append(ac)
            ac = self.rename_menu.addAction(
                name, Dispatcher(partial(self.rename_requested, name, loc)))
            rename_actions.append(ac)
            ac.setStatusTip(_('Rename: %s') % loc)
            ac = self.delete_menu.addAction(
                name, Dispatcher(partial(self.delete_requested, name, loc)))
            delete_actions.append(ac)
            ac.setStatusTip(_('Remove: %s') % loc)
            if is_prev_lib:
                ac.setFont(f)

        qs_actions = []
        locations_by_frequency = locations
        if len(locations) >= tweaks['many_libraries']:
            locations_by_frequency = list(
                self.stats.locations(db, limit=sys.maxsize))
        for i, x in enumerate(
                locations_by_frequency[:len(self.switch_actions)]):
            name, loc = x
            ic = library_qicon(name)
            name = name.replace('&', '&&')
            ac = self.switch_actions[i]
            ac.setText(name)
            ac.setIcon(ic)
            ac.setStatusTip(_('Switch to: %s') % loc)
            ac.setVisible(True)
            qs_actions.append(ac)
        self.qs_locations = [i[1] for i in locations_by_frequency]

        self.quick_menu_action.setVisible(bool(locations))
        self.rename_menu_action.setVisible(bool(locations))
        self.delete_menu_action.setVisible(bool(locations))
        self.gui.location_manager.set_switch_actions(quick_actions,
                                                     rename_actions,
                                                     delete_actions,
                                                     qs_actions,
                                                     self.action_choose)
        # VL at startup
        self.vl_to_apply_menu.clear()
        restrictions = sorted(db.prefs['virtual_libraries'], key=sort_key)
        # check that the virtual library choice still exists
        vl_at_startup = db.prefs['virtual_lib_on_startup']
        if vl_at_startup and vl_at_startup not in restrictions:
            vl_at_startup = db.prefs['virtual_lib_on_startup'] = ''
        restrictions.insert(0, '')
        for vl in restrictions:
            if vl == vl_at_startup:
                self.vl_to_apply_menu.addAction(
                    QIcon(I('ok.png')), vl if vl else _('No Virtual library'),
                    Dispatcher(partial(self.change_vl_at_startup_requested,
                                       vl)))
            else:
                self.vl_to_apply_menu.addAction(
                    vl if vl else _('No Virtual library'),
                    Dispatcher(partial(self.change_vl_at_startup_requested,
                                       vl)))
        # Allow the cloned actions in the OS X global menubar to update
        for a in (self.qaction, self.menuless_qaction):
            a.changed.emit()