Exemple #1
0
def set_book_path(path, pathtoebook):
    set_book_path.pathtoebook = pathtoebook
    set_book_path.path = os.path.abspath(path)
    set_book_path.metadata = get_data('calibre-book-metadata.json')[0]
    set_book_path.manifest, set_book_path.manifest_mime = get_data('calibre-book-manifest.json')
    set_book_path.parsed_metadata = json_loads(set_book_path.metadata)
    set_book_path.parsed_manifest = json_loads(set_book_path.manifest)
Exemple #2
0
def windows_repair(library_path=None):
    import subprocess
    from calibre.utils.serialize import json_dumps, json_loads
    from polyglot.binary import as_hex_unicode, from_hex_bytes
    if library_path:
        library_path = as_hex_unicode(json_dumps(library_path))
        winutil.prepare_for_restart()
        os.environ['CALIBRE_REPAIR_CORRUPTED_DB'] = environ_item(library_path)
        subprocess.Popen([sys.executable])
    else:
        try:
            app = Application([])
            from calibre.gui2.dialogs.restore_library import repair_library_at
            library_path = json_loads(
                from_hex_bytes(os.environ.pop('CALIBRE_REPAIR_CORRUPTED_DB')))
            done = repair_library_at(library_path, wait_time=4)
        except Exception:
            done = False
            error_dialog(
                None,
                _('Failed to repair library'),
                _('Could not repair library. Click "Show details" for more information.'
                  ),
                det_msg=traceback.format_exc(),
                show=True)
        if done:
            subprocess.Popen([sys.executable])
        app.quit()
def get_stored_annotations(container, bookmark_data):
    raw = bookmark_data or b''
    if not raw:
        return
    if raw.startswith(EPUB_FILE_TYPE_MAGIC):
        raw = raw[len(EPUB_FILE_TYPE_MAGIC):].replace(b'\n', b'')
        for annot in json_loads(from_base64_bytes(raw)):
            yield annot
        return

    from calibre.ebooks.oeb.iterator.bookmarks import parse_bookmarks
    for bm in parse_bookmarks(raw):
        if bm['type'] == 'cfi' and isinstance(bm['pos'], unicode_type):
            spine_index = (1 + bm['spine']) * 2
            epubcfi = 'epubcfi(/{}/{})'.format(spine_index,
                                               bm['pos'].lstrip('/'))
            title = bm.get('title')
            if title and title != 'calibre_current_page_bookmark':
                yield {
                    'type': 'bookmark',
                    'title': title,
                    'pos': epubcfi,
                    'pos_type': 'epubcfi',
                    'timestamp': EPOCH
                }
            else:
                yield {
                    'type': 'last-read',
                    'pos': epubcfi,
                    'pos_type': 'epubcfi',
                    'timestamp': EPOCH
                }
Exemple #4
0
def cdb_run(ctx, rd, which, version):
    try:
        m = module_for_cmd(which)
    except ImportError:
        raise HTTPNotFound('No module named: {}'.format(which))
    if not getattr(m, 'readonly', False):
        ctx.check_for_write_access(rd)
    if getattr(m, 'version', 0) != int(version):
        raise HTTPNotFound(('The module {} is not available in version: {}.'
                           'Make sure the version of calibre used for the'
                            ' server and calibredb match').format(which, version))
    db = get_library_data(ctx, rd, strict_library_id=True)[0]
    if ctx.restriction_for(rd, db):
        raise HTTPForbidden('Cannot use the command-line db interface with a user who has per library restrictions')
    raw = rd.read()
    ct = rd.inheaders.get('Content-Type', all=True)
    try:
        if MSGPACK_MIME in ct:
            args = msgpack_loads(raw)
        elif 'application/json' in ct:
            args = json_loads(raw)
        else:
            raise HTTPBadRequest('Only JSON or msgpack requests are supported')
    except Exception:
        raise HTTPBadRequest('args are not valid encoded data')
    if getattr(m, 'needs_srv_ctx', False):
        args = [ctx] + list(args)
    try:
        result = m.implementation(db, partial(ctx.notify_changes, db.backend.library_path), *args)
    except Exception as err:
        import traceback
        return {'err': as_unicode(err), 'tb': traceback.format_exc()}
    return {'result': result}
Exemple #5
0
 def dragMoveEvent(self, event):
     QTreeView.dragMoveEvent(self, event)
     self.setDropIndicatorShown(False)
     index = self.indexAt(event.pos())
     if not index.isValid():
         return
     src_is_tb = event.mimeData().hasFormat('application/calibre+from_tag_browser')
     item = index.data(Qt.UserRole)
     if item.type == TagTreeItem.ROOT:
         return
     flags = self._model.flags(index)
     if item.type == TagTreeItem.TAG and flags & Qt.ItemIsDropEnabled:
         self.setDropIndicatorShown(not src_is_tb)
         return
     if item.type == TagTreeItem.CATEGORY and not item.is_gst:
         fm_dest = self.db.metadata_for_field(item.category_key)
         if fm_dest['kind'] == 'user':
             if src_is_tb:
                 if event.dropAction() == Qt.MoveAction:
                     data = bytes(event.mimeData().data('application/calibre+from_tag_browser'))
                     src = json_loads(data)
                     for s in src:
                         if s[0] == TagTreeItem.TAG and \
                                 (not s[1].startswith('@') or s[2]):
                             return
                 self.setDropIndicatorShown(True)
                 return
             md = event.mimeData()
             if hasattr(md, 'column_name'):
                 fm_src = self.db.metadata_for_field(md.column_name)
                 if md.column_name in ['authors', 'publisher', 'series'] or \
                         (fm_src['is_custom'] and (
                          (fm_src['datatype'] in ['series', 'text', 'enumeration'] and not fm_src['is_multiple']) or (
                              fm_src['datatype'] == 'composite' and fm_src['display'].get('make_category', False)))):
                     self.setDropIndicatorShown(True)
Exemple #6
0
def details_context_menu_event(view, ev, book_info, add_popup_action=False):
    url = view.anchorAt(ev.pos())
    menu = QMenu(view)
    menu.addAction(QIcon(I('edit-copy.png')), _('Copy all book details'),
                   partial(copy_all, view))
    search_internet_added = False
    if url and url.startswith('action:'):
        data = json_loads(from_hex_bytes(url.split(':', 1)[1]))
        search_internet_added = add_item_specific_entries(
            menu, data, book_info)
    elif url and not url.startswith('#'):
        ac = book_info.copy_link_action
        ac.current_url = url
        ac.setText(_('Copy link location'))
        menu.addAction(ac)
    if not search_internet_added and hasattr(book_info, 'search_internet'):
        menu.addSeparator()
        menu.si = create_search_internet_menu(book_info.search_internet)
        menu.addMenu(menu.si)
    for ac in tuple(menu.actions()):
        if not ac.isEnabled():
            menu.removeAction(ac)
    if add_popup_action:
        menu.addSeparator()
        ac = menu.addAction(_('Open the Book details window'))
        ac.triggered.connect(book_info.show_book_info)
    if len(menu.actions()) > 0:
        menu.exec_(ev.globalPos())
Exemple #7
0
def details_context_menu_event(view,
                               ev,
                               book_info,
                               add_popup_action=False,
                               edit_metadata=None):
    url = view.anchorAt(ev.pos())
    menu = QMenu(view)
    copy_menu = menu.addMenu(QIcon(I('edit-copy.png')), _('Copy'))
    copy_menu.addAction(QIcon(I('edit-copy.png')), _('All book details'),
                        partial(copy_all, view))
    if view.textCursor().hasSelection():
        copy_menu.addAction(QIcon(I('edit-copy.png')), _('Selected text'),
                            view.copy)
    copy_menu.addSeparator()
    copy_links_added = False
    search_internet_added = False
    search_menu = QMenu(_('Search'), menu)
    search_menu.setIcon(QIcon(I('search.png')))
    if url and url.startswith('action:'):
        data = json_loads(from_hex_bytes(url.split(':', 1)[1]))
        search_internet_added = add_item_specific_entries(
            menu, data, book_info, copy_menu, search_menu)
        create_copy_links(copy_menu, data)
        copy_links_added = True
    elif url and not url.startswith('#'):
        ac = book_info.copy_link_action
        ac.current_url = url
        ac.setText(_('Copy link location'))
        menu.addAction(ac)
    if not copy_links_added:
        create_copy_links(copy_menu)

    if not search_internet_added and hasattr(book_info, 'search_internet'):
        sim = create_search_internet_menu(book_info.search_internet)
        if search_menu.isEmpty():
            search_menu = sim
        else:
            search_menu.addSeparator()
            for ac in sim.actions():
                search_menu.addAction(ac)
                ac.setText(_('Search {0} for this book').format(ac.text()))
    if not search_menu.isEmpty():
        menu.addMenu(search_menu)
    for ac in tuple(menu.actions()):
        if not ac.isEnabled():
            menu.removeAction(ac)
    menu.addSeparator()
    if add_popup_action:
        ac = menu.addAction(_('Open the Book details window'))
        ac.triggered.connect(book_info.show_book_info)
    else:
        from calibre.gui2.ui import get_gui
        ema = get_gui().iactions['Edit Metadata'].menuless_qaction
        menu.addAction(
            _('Open the Edit metadata window') + '\t' +
            ema.shortcut().toString(QKeySequence.SequenceFormat.NativeText),
            edit_metadata)
    if len(menu.actions()) > 0:
        menu.exec_(ev.globalPos())
Exemple #8
0
 def load_book_annotations(self):
     amap = self.current_book_data['annotations_map']
     path = os.path.join(self.current_book_data['base'], 'calibre-book-annotations.json')
     if os.path.exists(path):
         with open(path, 'rb') as f:
             raw = f.read()
         merge_annotations(json_loads(raw), amap)
     path = os.path.join(annotations_dir, self.current_book_data['annotations_path_key'])
     if os.path.exists(path):
         with open(path, 'rb') as f:
             raw = f.read()
         merge_annotations(parse_annotations(raw), amap)
Exemple #9
0
def cdb_set_fields(ctx, rd, book_id, library_id):
    db = get_db(ctx, rd, library_id)
    if ctx.restriction_for(rd, db):
        raise HTTPForbidden(
            'Cannot use the set fields interface with a user who has per library restrictions'
        )
    raw = rd.read()
    ct = rd.inheaders.get('Content-Type', all=True)
    ct = {x.lower().partition(';')[0] for x in ct}
    try:
        if MSGPACK_MIME in ct:
            data = msgpack_loads(raw)
        elif 'application/json' in ct:
            data = json_loads(raw)
        else:
            raise HTTPBadRequest('Only JSON or msgpack requests are supported')
    except Exception:
        raise HTTPBadRequest('Invalid encoded data')
    try:
        changes, loaded_book_ids = data['changes'], frozenset(
            map(int, data.get('loaded_book_ids', ())))
        all_dirtied = bool(data.get('all_dirtied'))
        if not isinstance(changes, dict):
            raise TypeError('changes must be a dict')
    except Exception:
        raise HTTPBadRequest(
            '''Data must be of the form {'changes': {'title': 'New Title', ...}, 'loaded_book_ids':[book_id1, book_id2, ...]'}'''
        )
    dirtied = set()
    cdata = changes.pop('cover', False)
    if cdata is not False:
        if cdata is not None:
            try:
                cdata = standard_b64decode(
                    cdata.split(',', 1)[-1].encode('ascii'))
            except Exception:
                raise HTTPBadRequest(
                    'Cover data is not valid base64 encoded data')
            try:
                fmt = what(None, cdata)
            except Exception:
                fmt = None
            if fmt not in ('jpeg', 'png'):
                raise HTTPBadRequest('Cover data must be either JPEG or PNG')
        dirtied |= db.set_cover({book_id: cdata})

    for field, value in changes.iteritems():
        dirtied |= db.set_field(field, {book_id: value})
    ctx.notify_changes(db.backend.library_path, metadata(dirtied))
    all_ids = dirtied if all_dirtied else (dirtied & loaded_book_ids)
    all_ids |= {book_id}
    return {bid: book_as_json(db, book_id) for bid in all_ids}
Exemple #10
0
def load_payload_data(rd):
    raw = rd.read()
    ct = rd.inheaders.get('Content-Type', all=True)
    ct = {x.lower().partition(';')[0] for x in ct}
    try:
        if MSGPACK_MIME in ct:
            return msgpack_loads(raw)
        elif 'application/json' in ct:
            return json_loads(raw)
        else:
            raise HTTPBadRequest('Only JSON or msgpack requests are supported')
    except Exception:
        raise HTTPBadRequest('Invalid encoded data')
Exemple #11
0
def load_payload_data(rd):
    raw = rd.read()
    ct = rd.inheaders.get('Content-Type', all=True)
    ct = {x.lower().partition(';')[0] for x in ct}
    try:
        if MSGPACK_MIME in ct:
            return msgpack_loads(raw)
        elif 'application/json' in ct:
            return json_loads(raw)
        else:
            raise HTTPBadRequest('Only JSON or msgpack requests are supported')
    except Exception:
        raise HTTPBadRequest('Invalid encoded data')
Exemple #12
0
 def test_serialize_metadata(self):  # {{{
     from calibre.utils.serialize import json_dumps, json_loads, msgpack_dumps, msgpack_loads
     from calibre.library.field_metadata import fm_as_dict
     cache = self.init_cache(self.library_path)
     fm = cache.field_metadata
     for d, l in ((json_dumps, json_loads), (msgpack_dumps, msgpack_loads)):
         fm2 = l(d(fm))
         self.assertEqual(fm_as_dict(fm), fm_as_dict(fm2))
     for i in range(1, 4):
         mi = cache.get_metadata(i, get_cover=True, cover_as_data=True)
         rmi = msgpack_loads(msgpack_dumps(mi))
         self.compare_metadata(mi, rmi, exclude='format_metadata has_cover formats id'.split())
         rmi = json_loads(json_dumps(mi))
         self.compare_metadata(mi, rmi, exclude='format_metadata has_cover formats id'.split())
Exemple #13
0
 def test_serialize_metadata(self):  # {{{
     from calibre.utils.serialize import json_dumps, json_loads, msgpack_dumps, msgpack_loads
     from calibre.library.field_metadata import fm_as_dict
     cache = self.init_cache(self.library_path)
     fm = cache.field_metadata
     for d, l in ((json_dumps, json_loads), (msgpack_dumps, msgpack_loads)):
         fm2 = l(d(fm))
         self.assertEqual(fm_as_dict(fm), fm_as_dict(fm2))
     for i in range(1, 4):
         mi = cache.get_metadata(i, get_cover=True, cover_as_data=True)
         rmi = msgpack_loads(msgpack_dumps(mi))
         self.compare_metadata(mi, rmi, exclude='format_metadata has_cover formats id'.split())
         rmi = json_loads(json_dumps(mi))
         self.compare_metadata(mi, rmi, exclude='format_metadata has_cover formats id'.split())
Exemple #14
0
    def handle_click(self, link):
        typ, val = link.partition(':')[::2]

        def search_term(field, val):
            append = ''
            mods = QApplication.instance().keyboardModifiers()
            if mods & Qt.KeyboardModifier.ControlModifier:
                append = 'AND' if mods & Qt.KeyboardModifier.ShiftModifier else 'OR'

            fmt = '{}:{}' if is_boolean(field) else '{}:"={}"'
            self.search_requested.emit(
                fmt.format(field, val.replace('"', '\\"')),
                append
            )

        def browse(url):
            try:
                safe_open_url(QUrl(url, QUrl.ParsingMode.TolerantMode))
            except Exception:
                import traceback
                traceback.print_exc()

        if typ == 'action':
            data = json_loads(from_hex_bytes(val))
            dt = data['type']
            if dt == 'search':
                search_term(data['term'], data['value'])
            elif dt == 'author':
                url = data['url']
                if url == 'calibre':
                    search_term('authors', data['name'])
                else:
                    browse(url)
            elif dt == 'format':
                book_id, fmt = data['book_id'], data['fmt']
                self.view_specific_format.emit(int(book_id), fmt)
            elif dt == 'identifier':
                if data['url']:
                    browse(data['url'])
            elif dt == 'path':
                self.open_containing_folder.emit(int(data['loc']))
            elif dt == 'devpath':
                self.view_device_book.emit(data['loc'])
        else:
            browse(link)
Exemple #15
0
def cdb_set_fields(ctx, rd, book_id, library_id):
    db = get_db(ctx, rd, library_id)
    if ctx.restriction_for(rd, db):
        raise HTTPForbidden('Cannot use the set fields interface with a user who has per library restrictions')
    raw = rd.read()
    ct = rd.inheaders.get('Content-Type', all=True)
    ct = {x.lower().partition(';')[0] for x in ct}
    try:
        if MSGPACK_MIME in ct:
            data = msgpack_loads(raw)
        elif 'application/json' in ct:
            data = json_loads(raw)
        else:
            raise HTTPBadRequest('Only JSON or msgpack requests are supported')
        changes, loaded_book_ids = data['changes'], frozenset(map(int, data['loaded_book_ids']))
    except Exception:
        raise HTTPBadRequest('Invalid encoded data')
    dirtied = set()
    for field, value in changes.iteritems():
        dirtied |= db.set_field(field, {book_id: value})
    metadata(dirtied)
    return {bid: book_as_json(db, book_id) for bid in (dirtied & loaded_book_ids) | {book_id}}
Exemple #16
0
def windows_repair(library_path=None):
    import subprocess
    from calibre.utils.serialize import json_dumps, json_loads
    from polyglot.binary import as_hex_unicode, from_hex_bytes
    if library_path:
        library_path = as_hex_unicode(json_dumps(library_path))
        winutil.prepare_for_restart()
        os.environ['CALIBRE_REPAIR_CORRUPTED_DB'] = environ_item(library_path)
        subprocess.Popen([sys.executable])
    else:
        try:
            app = Application([])
            from calibre.gui2.dialogs.restore_library import repair_library_at
            library_path = json_loads(from_hex_bytes(os.environ.pop('CALIBRE_REPAIR_CORRUPTED_DB')))
            done = repair_library_at(library_path, wait_time=4)
        except Exception:
            done = False
            error_dialog(None, _('Failed to repair library'), _(
                'Could not repair library. Click "Show details" for more information.'), det_msg=traceback.format_exc(), show=True)
        if done:
            subprocess.Popen([sys.executable])
        app.quit()
Exemple #17
0
def cdb_set_fields(ctx, rd, book_id, library_id):
    db = get_db(ctx, rd, library_id)
    if ctx.restriction_for(rd, db):
        raise HTTPForbidden('Cannot use the set fields interface with a user who has per library restrictions')
    raw = rd.read()
    ct = rd.inheaders.get('Content-Type', all=True)
    ct = {x.lower().partition(';')[0] for x in ct}
    try:
        if MSGPACK_MIME in ct:
            data = msgpack_loads(raw)
        elif 'application/json' in ct:
            data = json_loads(raw)
        else:
            raise HTTPBadRequest('Only JSON or msgpack requests are supported')
        changes, loaded_book_ids = data['changes'], frozenset(map(int, data['loaded_book_ids']))
    except Exception:
        raise HTTPBadRequest('Invalid encoded data')
    dirtied = set()
    cdata = changes.pop('cover', False)
    if cdata is not False:
        if cdata is not None:
            try:
                cdata = standard_b64decode(cdata.split(',', 1)[-1].encode('ascii'))
            except Exception:
                raise HTTPBadRequest('Cover data is not valid base64 encoded data')
            try:
                fmt = what(None, cdata)
            except Exception:
                fmt = None
            if fmt not in ('jpeg', 'png'):
                raise HTTPBadRequest('Cover data must be either JPEG or PNG')
        dirtied |= db.set_cover({book_id: cdata})

    for field, value in changes.iteritems():
        dirtied |= db.set_field(field, {book_id: value})
    ctx.notify_changes(db.backend.library_path, metadata(dirtied))
    return {bid: book_as_json(db, book_id) for bid in (dirtied & loaded_book_ids) | {book_id}}
Exemple #18
0
    def handle_click(self, link):
        typ, val = link.partition(':')[::2]

        def search_term(field, val):
            self.search_requested.emit('{}:"={}"'.format(
                field, val.replace('"', '\\"')))

        def browse(url):
            try:
                safe_open_url(QUrl(url, QUrl.TolerantMode))
            except Exception:
                import traceback
                traceback.print_exc()

        if typ == 'action':
            data = json_loads(from_hex_bytes(val))
            dt = data['type']
            if dt == 'search':
                search_term(data['term'], data['value'])
            elif dt == 'author':
                url = data['url']
                if url == 'calibre':
                    search_term('authors', data['name'])
                else:
                    browse(url)
            elif dt == 'format':
                book_id, fmt = data['book_id'], data['fmt']
                self.view_specific_format.emit(int(book_id), fmt)
            elif dt == 'identifier':
                if data['url']:
                    browse(data['url'])
            elif dt == 'path':
                self.open_containing_folder.emit(int(data['loc']))
            elif dt == 'devpath':
                self.view_device_book.emit(data['loc'])
        else:
            browse(link)
Exemple #19
0
def parse_annotations(raw):
    for annot in json_loads(raw):
        yield parse_annotation(annot)
Exemple #20
0
def details_context_menu_event(view, ev, book_info):  # {{{
    p = view.page()
    mf = p.mainFrame()
    r = mf.hitTestContent(ev.pos())
    url = unicode_type(r.linkUrl().toString(NO_URL_FORMATTING)).strip()
    menu = p.createStandardContextMenu()
    ca = view.pageAction(p.Copy)
    for action in list(menu.actions()):
        if action is not ca:
            menu.removeAction(action)
    menu.addAction(QIcon(I('edit-copy.png')), _('Copy &all'),
                   partial(copy_all, book_info))
    search_internet_added = False
    if not r.isNull():
        from calibre.ebooks.oeb.polish.main import SUPPORTED
        if url.startswith('format:'):
            parts = url.split(':')
            try:
                book_id, fmt = int(parts[1]), parts[2].upper()
            except:
                import traceback
                traceback.print_exc()
            else:
                from calibre.gui2.ui import get_gui
                db = get_gui().current_db.new_api
                ofmt = fmt.upper() if fmt.startswith(
                    'ORIGINAL_') else 'ORIGINAL_' + fmt
                nfmt = ofmt[len('ORIGINAL_'):]
                fmts = {x.upper() for x in db.formats(book_id)}
                for a, t in [
                    ('remove', _('Delete the %s format')),
                    ('save', _('Save the %s format to disk')),
                    ('restore', _('Restore the %s format')),
                    ('compare', ''),
                    ('set_cover', _('Set the book cover from the %s file')),
                ]:
                    if a == 'restore' and not fmt.startswith('ORIGINAL_'):
                        continue
                    if a == 'compare':
                        if ofmt not in fmts or nfmt not in SUPPORTED:
                            continue
                        t = _('Compare to the %s format') % (
                            fmt[9:] if fmt.startswith('ORIGINAL_') else ofmt)
                    else:
                        t = t % fmt
                    ac = getattr(book_info, '%s_format_action' % a)
                    ac.current_fmt = (book_id, fmt)
                    ac.setText(t)
                    menu.addAction(ac)
                if not fmt.upper().startswith('ORIGINAL_'):
                    from calibre.gui2.open_with import populate_menu, edit_programs
                    m = QMenu(_('Open %s with...') % fmt.upper())

                    def connect_action(ac, entry):
                        connect_lambda(
                            ac.triggered, book_info,
                            lambda book_info: book_info.open_with(
                                book_id, fmt, entry))

                    populate_menu(m, connect_action, fmt)
                    if len(m.actions()) == 0:
                        menu.addAction(
                            _('Open %s with...') % fmt.upper(),
                            partial(book_info.choose_open_with, book_id, fmt))
                    else:
                        m.addSeparator()
                        m.addAction(
                            _('Add other application for %s files...') %
                            fmt.upper(),
                            partial(book_info.choose_open_with, book_id, fmt))
                        m.addAction(_('Edit Open With applications...'),
                                    partial(edit_programs, fmt, book_info))
                        menu.addMenu(m)
                        menu.ow = m
                    if fmt.upper() in SUPPORTED:
                        menu.addSeparator()
                        menu.addAction(
                            _('Edit %s...') % fmt.upper(),
                            partial(book_info.edit_fmt, book_id, fmt))
                ac = book_info.copy_link_action
                ac.current_url = r.linkElement().attribute('data-full-path')
                if ac.current_url:
                    ac.setText(_('&Copy path to file'))
                    menu.addAction(ac)
        else:
            el = r.linkElement()
            data = el.attribute('data-item')
            author = el.toPlainText() if unicode_type(
                el.attribute('calibre-data')) == 'authors' else None
            if url and not url.startswith('search:'):
                for a, t in [
                    ('copy', _('&Copy link')),
                ]:
                    ac = getattr(book_info, '%s_link_action' % a)
                    ac.current_url = url
                    if url.startswith('path:'):
                        ac.current_url = el.attribute('title')
                    ac.setText(t)
                    menu.addAction(ac)
            if author is not None:
                menu.addAction(
                    init_manage_action(book_info.manage_action, 'authors',
                                       author))
                if hasattr(book_info, 'search_internet'):
                    menu.sia = sia = create_search_internet_menu(
                        book_info.search_internet, author)
                    menu.addMenu(sia)
                    search_internet_added = True
                if hasattr(book_info, 'search_requested'):
                    menu.addAction(
                        _('Search calibre for %s') % author, lambda: book_info.
                        search_requested('authors:"={}"'.format(
                            author.replace('"', r'\"'))))
            if data:
                try:
                    field, value, book_id = json_loads(from_hex_bytes(data))
                except Exception:
                    field = value = book_id = None
                if field:
                    if author is None:
                        if field in ('tags', 'series',
                                     'publisher') or is_category(field):
                            menu.addAction(
                                init_manage_action(book_info.manage_action,
                                                   field, value))
                        elif field == 'identifiers':
                            menu.addAction(book_info.edit_identifiers_action)
                    ac = book_info.remove_item_action
                    ac.data = (field, value, book_id)
                    ac.setText(_('Remove %s from this book') % value)
                    menu.addAction(ac)

    if not search_internet_added and hasattr(book_info, 'search_internet'):
        menu.addSeparator()
        menu.si = create_search_internet_menu(book_info.search_internet)
        menu.addMenu(menu.si)
    if len(menu.actions()) > 0:
        menu.exec_(ev.globalPos())
Exemple #21
0
 def serialized_prefs(self, val: bytes) -> None:
     from calibre.utils.serialize import json_loads
     prefs = json_loads(val)
     self.apply_prefs(prefs)
     self.update_preview()
Exemple #22
0
def details_context_menu_event(view, ev, book_info):  # {{{
    p = view.page()
    mf = p.mainFrame()
    r = mf.hitTestContent(ev.pos())
    url = unicode_type(r.linkUrl().toString(NO_URL_FORMATTING)).strip()
    menu = p.createStandardContextMenu()
    ca = view.pageAction(p.Copy)
    for action in list(menu.actions()):
        if action is not ca:
            menu.removeAction(action)
    menu.addAction(QIcon(I('edit-copy.png')), _('Copy &all'), partial(copy_all, book_info))
    search_internet_added = False
    if not r.isNull():
        from calibre.ebooks.oeb.polish.main import SUPPORTED
        if url.startswith('format:'):
            parts = url.split(':')
            try:
                book_id, fmt = int(parts[1]), parts[2].upper()
            except:
                import traceback
                traceback.print_exc()
            else:
                from calibre.gui2.ui import get_gui
                db = get_gui().current_db.new_api
                ofmt = fmt.upper() if fmt.startswith('ORIGINAL_') else 'ORIGINAL_' + fmt
                nfmt = ofmt[len('ORIGINAL_'):]
                fmts = {x.upper() for x in db.formats(book_id)}
                for a, t in [
                        ('remove', _('Delete the %s format')),
                        ('save', _('Save the %s format to disk')),
                        ('restore', _('Restore the %s format')),
                        ('compare', ''),
                        ('set_cover', _('Set the book cover from the %s file')),
                ]:
                    if a == 'restore' and not fmt.startswith('ORIGINAL_'):
                        continue
                    if a == 'compare':
                        if ofmt not in fmts or nfmt not in SUPPORTED:
                            continue
                        t = _('Compare to the %s format') % (fmt[9:] if fmt.startswith('ORIGINAL_') else ofmt)
                    else:
                        t = t % fmt
                    ac = getattr(book_info, '%s_format_action'%a)
                    ac.current_fmt = (book_id, fmt)
                    ac.setText(t)
                    menu.addAction(ac)
                if not fmt.upper().startswith('ORIGINAL_'):
                    from calibre.gui2.open_with import populate_menu, edit_programs
                    m = QMenu(_('Open %s with...') % fmt.upper())

                    def connect_action(ac, entry):
                        connect_lambda(ac.triggered, book_info, lambda book_info: book_info.open_with(book_id, fmt, entry))

                    populate_menu(m, connect_action, fmt)
                    if len(m.actions()) == 0:
                        menu.addAction(_('Open %s with...') % fmt.upper(), partial(book_info.choose_open_with, book_id, fmt))
                    else:
                        m.addSeparator()
                        m.addAction(_('Add other application for %s files...') % fmt.upper(), partial(book_info.choose_open_with, book_id, fmt))
                        m.addAction(_('Edit Open With applications...'), partial(edit_programs, fmt, book_info))
                        menu.addMenu(m)
                        menu.ow = m
                    if fmt.upper() in SUPPORTED:
                        menu.addSeparator()
                        menu.addAction(_('Edit %s...') % fmt.upper(), partial(book_info.edit_fmt, book_id, fmt))
                ac = book_info.copy_link_action
                ac.current_url = r.linkElement().attribute('data-full-path')
                if ac.current_url:
                    ac.setText(_('&Copy path to file'))
                    menu.addAction(ac)
        else:
            el = r.linkElement()
            data = el.attribute('data-item')
            author = el.toPlainText() if unicode_type(el.attribute('calibre-data')) == u'authors' else None
            if url and not url.startswith('search:'):
                for a, t in [('copy', _('&Copy link')),
                ]:
                    ac = getattr(book_info, '%s_link_action'%a)
                    ac.current_url = url
                    if url.startswith('path:'):
                        ac.current_url = el.attribute('title')
                    ac.setText(t)
                    menu.addAction(ac)
            if author is not None:
                menu.addAction(init_manage_action(book_info.manage_action, 'authors', author))
                if hasattr(book_info, 'search_internet'):
                    menu.sia = sia = create_search_internet_menu(book_info.search_internet, author)
                    menu.addMenu(sia)
                    search_internet_added = True
                if hasattr(book_info, 'search_requested'):
                    menu.addAction(_('Search calibre for %s') % author,
                                   lambda : book_info.search_requested('authors:"={}"'.format(author.replace('"', r'\"'))))
            if data:
                try:
                    field, value, book_id = json_loads(from_hex_bytes(data))
                except Exception:
                    field = value = book_id = None
                if field:
                    if author is None:
                        if field in ('tags', 'series', 'publisher') or is_category(field):
                            menu.addAction(init_manage_action(book_info.manage_action, field, value))
                        elif field == 'identifiers':
                            menu.addAction(book_info.edit_identifiers_action)
                    ac = book_info.remove_item_action
                    ac.data = (field, value, book_id)
                    ac.setText(_('Remove %s from this book') % value)
                    menu.addAction(ac)

    if not search_internet_added and hasattr(book_info, 'search_internet'):
        menu.addSeparator()
        menu.si = create_search_internet_menu(book_info.search_internet)
        menu.addMenu(menu.si)
    if len(menu.actions()) > 0:
        menu.exec_(ev.globalPos())