Exemplo n.º 1
0
    def add_books_from_device(self, view, paths=None):
        backloading_err = self.gui.device_manager.device.BACKLOADING_ERROR_MESSAGE
        if backloading_err is not None:
            return error_dialog(self.gui, _('Add to library'), backloading_err,
                    show=True)
        if paths is None:
            rows = view.selectionModel().selectedRows()
            if not rows or len(rows) == 0:
                d = error_dialog(self.gui, _('Add to library'), _('No book selected'))
                d.exec_()
                return
            paths = [p for p in view.model().paths(rows) if p is not None]
        ve = self.gui.device_manager.device.VIRTUAL_BOOK_EXTENSIONS

        def ext(x):
            ans = os.path.splitext(x)[1]
            ans = ans[1:] if len(ans) > 1 else ans
            return ans.lower()
        remove = {p for p in paths if ext(p) in ve}
        if remove:
            paths = [p for p in paths if p not in remove]
            vmsg = getattr(self.gui.device_manager.device, 'VIRTUAL_BOOK_EXTENSION_MESSAGE', None) or _(
                'The following books are virtual and cannot be added'
                ' to the calibre library:')
            info_dialog(self.gui,  _('Not Implemented'), vmsg, '\n'.join(remove), show=True)
            if not paths:
                return
        if not paths or len(paths) == 0:
            d = error_dialog(self.gui, _('Add to library'), _('No book files found'))
            d.exec_()
            return

        self.gui.device_manager.prepare_addable_books(self.Dispatcher(partial(
            self.books_prepared, view)), paths)
        self.bpd = ProgressDialog(_('Downloading books'),
                msg=_('Downloading books from device'), parent=self.gui,
                cancelable=False)
        QTimer.singleShot(1000, self.show_bpd)
Exemplo n.º 2
0
 def __init__(self, gui, ids, callback):
     from calibre.gui2.dialogs.progress import ProgressDialog
     QObject.__init__(self, gui)
     self.model = gui.library_view.model()
     self.ids = ids
     self.permanent = False
     if can_recycle and len(ids) > 100:
         if question_dialog(gui, _('Are you sure?'), '<p>'+
             _('You are trying to delete %d books. '
                 'Sending so many files to the Recycle'
                 ' Bin <b>can be slow</b>. Should calibre skip the'
                 ' Recycle Bin? If you click Yes the files'
                 ' will be <b>permanently deleted</b>.')%len(ids)):
             self.permanent = True
     self.gui = gui
     self.failures = []
     self.deleted_ids = []
     self.callback = callback
     single_shot(self.delete_one)
     self.pd = ProgressDialog(_('Deleting...'), parent=gui,
             cancelable=False, min=0, max=len(self.ids), icon='trash.png')
     self.pd.setModal(True)
     self.pd.show()
Exemplo n.º 3
0
    def apply_metadata_changes(self, id_map, title=None, msg='', callback=None,
            merge_tags=True, merge_comments=False, icon=None):
        '''
        Apply the metadata changes in id_map to the database synchronously
        id_map must be a mapping of ids to Metadata objects. Set any fields you
        do not want updated in the Metadata object to null. An easy way to do
        that is to create a metadata object as Metadata(_('Unknown')) and then
        only set the fields you want changed on this object.

        callback can be either None or a function accepting a single argument,
        in which case it is called after applying is complete with the list of
        changed ids.

        id_map can also be a mapping of ids to 2-tuple's where each 2-tuple
        contains the absolute paths to an OPF and cover file respectively. If
        either of the paths is None, then the corresponding metadata is not
        updated.
        '''
        if title is None:
            title = _('Applying changed metadata')
        self.apply_id_map = list(id_map.iteritems())
        self.apply_current_idx = 0
        self.apply_failures = []
        self.applied_ids = set()
        self.apply_pd = None
        self.apply_callback = callback
        if len(self.apply_id_map) > 1:
            from calibre.gui2.dialogs.progress import ProgressDialog
            self.apply_pd = ProgressDialog(title, msg, min=0,
                    max=len(self.apply_id_map)-1, parent=self.gui,
                    cancelable=False, icon=icon)
            self.apply_pd.setModal(True)
            self.apply_pd.show()
        self._am_merge_tags = merge_tags
        self._am_merge_comments = merge_comments
        self.do_one_apply()
Exemplo n.º 4
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.º 5
0
    def __init__(self,
                 source,
                 single_book_per_directory=True,
                 db=None,
                 parent=None,
                 callback=None,
                 pool=None,
                 list_of_archives=False):
        if isinstance(source, str):
            source = make_long_path_useable(source)
        else:
            source = list(map(make_long_path_useable, source))
        if not validate_source(source, parent):
            return
        QObject.__init__(self, parent)
        self.author_map_rules = None
        if gprefs.get('author_map_on_add_rules'):
            from calibre.ebooks.metadata.author_mapper import compile_rules as acr
            self.author_map_rules = acr(gprefs['author_map_on_add_rules'])
        self.single_book_per_directory = single_book_per_directory
        self.ignore_opf = False
        self.list_of_archives = list_of_archives
        self.callback = callback
        self.add_formats_to_existing = prefs['add_formats_to_existing']
        self.do_one_signal.connect(self.tick,
                                   type=Qt.ConnectionType.QueuedConnection)
        self.pool = pool
        self.pd = ProgressDialog(_('Adding books...'),
                                 _('Scanning for files...'),
                                 min=0,
                                 max=0,
                                 parent=parent,
                                 icon='add_book.png')
        self.win_id = None
        if parent is not None and hasattr(parent, 'effectiveWinId'):
            self.win_id = parent.effectiveWinId()
            if self.win_id is not None:
                self.win_id = int(self.win_id)
        self.db = getattr(db, 'new_api', None)
        if self.db is not None:
            self.dbref = weakref.ref(db)
        self.source = source
        self.tdir = PersistentTemporaryDirectory('_add_books')
        self.scan_error = None
        self.file_groups = OrderedDict()
        self.abort_scan = False
        self.duplicates = []
        self.report = []
        self.items = []
        self.added_book_ids = set()
        self.merged_formats_added_to = set()
        self.merged_books = set()
        self.added_duplicate_info = set()
        self.pd.show()

        self.scan_thread = Thread(target=self.scan, name='ScanBooks')
        self.scan_thread.daemon = True
        self.scan_thread.start()
        self.do_one = self.monitor_scan
        self.do_one_signal.emit()
        if DEBUG:
            self.start_time = time.time()
Exemplo n.º 6
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.º 7
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