Ejemplo n.º 1
0
def get(ctx, rd, what, book_id, library_id):
    book_id, rest = book_id.partition('_')[::2]
    try:
        book_id = int(book_id)
    except Exception:
        raise HTTPNotFound('Book with id %r does not exist' % book_id)
    db = get_db(ctx, rd, library_id)
    if db is None:
        raise HTTPNotFound('Library %r not found' % library_id)
    with db.safe_read_lock:
        if not ctx.has_id(rd, db, book_id):
            raise BookNotFound(book_id, db)
        library_id = db.server_library_id  # in case library_id was None
        if what == 'thumb':
            sz = rd.query.get('sz')
            w, h = 60, 80
            if sz is None:
                if rest:
                    try:
                        w, h = map(int, rest.split('_'))
                    except Exception:
                        pass
            elif sz == 'full':
                w = h = None
            elif 'x' in sz:
                try:
                    w, h = map(int, sz.partition('x')[::2])
                except Exception:
                    pass
            else:
                try:
                    w = h = int(sz)
                except Exception:
                    pass
            return cover(ctx, rd, library_id, db, book_id, width=w, height=h)
        elif what == 'cover':
            return cover(ctx, rd, library_id, db, book_id)
        elif what == 'opf':
            mi = db.get_metadata(book_id, get_cover=False)
            rd.outheaders['Content-Type'] = 'application/oebps-package+xml; charset=UTF-8'
            rd.outheaders['Last-Modified'] = http_date(timestampfromdt(mi.last_modified))
            return metadata_to_opf(mi)
        elif what == 'json':
            from calibre.srv.ajax import book_to_json
            data, last_modified = book_to_json(ctx, rd, db, book_id)
            rd.outheaders['Last-Modified'] = http_date(timestampfromdt(last_modified))
            return json(ctx, rd, get, data)
        else:
            try:
                return book_fmt(ctx, rd, library_id, db, book_id, what.lower())
            except NoSuchFormat:
                raise HTTPNotFound('No %s format for the book %r' % (what.lower(), book_id))
Ejemplo n.º 2
0
def opds(ctx, rd):
    rc = RequestContext(ctx, rd)
    db = rc.db
    categories = rc.get_categories()
    category_meta = db.field_metadata
    cats = [(_("Newest"), _("Date"), "Onewest"), (_("Title"), _("Title"), "Otitle")]

    def getter(x):
        try:
            return category_meta[x]["name"].lower()
        except KeyError:
            return x

    for category in sorted(categories, key=lambda x: sort_key(getter(x))):
        if len(categories[category]) == 0:
            continue
        if category in ("formats", "identifiers"):
            continue
        meta = category_meta.get(category, None)
        if meta is None:
            continue
        cats.append((meta["name"], meta["name"], "N" + category))
    last_modified = db.last_modified()
    rd.outheaders["Last-Modified"] = http_date(timestampfromdt(last_modified))
    return TopLevel(last_modified, cats, rc).root
Ejemplo n.º 3
0
def create_file_copy(ctx, rd, prefix, library_id, book_id, ext, mtime, copy_func, extra_etag_data=''):
    ''' We cannot copy files directly from the library folder to the output
    socket, as this can potentially lock the library for an extended period. So
    instead we copy out the data from the library folder into a temp folder. We
    make sure to only do this copy once, using the previous copy, if there have
    been no changes to the data for the file since the last copy. '''

    # Avoid too many items in a single directory for performance
    base = os.path.join(rd.tdir, 'fcache', (('%x' % book_id)[-3:]))

    library_id = library_id.replace('\\', '_').replace('/', '_')
    bname = '%s-%s-%s.%s' % (prefix, library_id, book_id, ext)
    fname = os.path.join(base, bname)
    do_copy = True
    mtime = timestampfromdt(mtime)
    try:
        ans = lopen(fname, 'r+b')
        do_copy = os.fstat(ans.fileno()).st_mtime < mtime
    except EnvironmentError:
        try:
            ans = lopen(fname, 'w+b')
        except EnvironmentError:
            try:
                os.makedirs(base)
            except EnvironmentError:
                pass
            ans = lopen(fname, 'w+b')
            do_copy = True
    if do_copy:
        copy_func(ans)
        ans.seek(0)
    if ctx.testing:
        rd.outheaders['Used-Cache'] = 'no' if do_copy else 'yes'
    return rd.filesystem_file_with_custom_etag(ans, prefix, library_id, book_id, mtime, extra_etag_data)
Ejemplo n.º 4
0
def mobile(ctx, rd):
    db, library_id, library_map, default_library = get_library_data(ctx, rd)
    try:
        start = max(1, int(rd.query.get('start', 1)))
    except ValueError:
        raise HTTPBadRequest('start is not an integer')
    try:
        num = max(0, int(rd.query.get('num', 25)))
    except ValueError:
        raise HTTPBadRequest('num is not an integer')
    search = rd.query.get('search') or ''
    with db.safe_read_lock:
        book_ids = ctx.search(rd, db, search)
        total = len(book_ids)
        ascending = rd.query.get('order', '').lower().strip() == 'ascending'
        sort_by = sanitize_sort_field_name(db.field_metadata, rd.query.get('sort') or 'date')
        try:
            book_ids = db.multisort([(sort_by, ascending)], book_ids)
        except Exception:
            sort_by = 'date'
            book_ids = db.multisort([(sort_by, ascending)], book_ids)
        books = [db.get_metadata(book_id) for book_id in book_ids[(start-1):(start-1)+num]]
    rd.outheaders['Last-Modified'] = http_date(timestampfromdt(db.last_modified()))
    order = 'ascending' if ascending else 'descending'
    q = {b'search':search.encode('utf-8'), b'order':bytes(order), b'sort':sort_by.encode('utf-8'), b'num':bytes(num), 'library_id':library_id}
    url_base = ctx.url_for('/mobile') + '?' + urlencode(q)
    lm = {k:v for k, v in library_map.iteritems() if k != library_id}
    return build_index(books, num, search, sort_by, order, start, total, url_base, db.field_metadata, ctx, lm, library_id)
Ejemplo n.º 5
0
def generated_cover(ctx, rd, library_id, db, book_id, width=None, height=None):
    prefix = 'generated-cover'
    if height is not None:
        prefix += '-%sx%s' % (width, height)

    mtime = timestampfromdt(db.field_for('last_modified', book_id))
    return create_file_copy(ctx, rd, prefix, library_id, book_id, 'jpg', mtime, partial(write_generated_cover, db, book_id, width, height))
Ejemplo n.º 6
0
def opds(ctx, rd):
    rc = RequestContext(ctx, rd)
    db = rc.db
    try:
        categories = rc.get_categories(report_parse_errors=True)
    except ParseException as p:
        raise HTTPInternalServerError(p.msg)

    category_meta = db.field_metadata
    cats = [
        (_('Newest'), _('Date'), 'Onewest'),
        (_('Title'), _('Title'), 'Otitle'),
    ]

    def getter(x):
        try:
            return category_meta[x]['name'].lower()
        except KeyError:
            return x

    fm = rc.db.field_metadata
    for category in sorted(categories, key=lambda x: sort_key(getter(x))):
        if fm.is_ignorable_field(category) and not rc.ctx.is_field_displayable(category):
            continue
        if len(categories[category]) == 0:
            continue
        if category in ('formats', 'identifiers'):
            continue
        meta = category_meta.get(category, None)
        if meta is None:
            continue
        cats.append((meta['name'], meta['name'], 'N'+category))
    last_modified = db.last_modified()
    rd.outheaders['Last-Modified'] = http_date(timestampfromdt(last_modified))
    return TopLevel(last_modified, cats, rc).root
Ejemplo n.º 7
0
def opds(ctx, rd):
    rc = RequestContext(ctx, rd)
    db = rc.db
    categories = rc.get_categories()
    category_meta = db.field_metadata
    cats = [
        (_('Newest'), _('Date'), 'Onewest'),
        (_('Title'), _('Title'), 'Otitle'),
    ]

    def getter(x):
        try:
            return category_meta[x]['name'].lower()
        except KeyError:
            return x

    for category in sorted(categories, key=lambda x: sort_key(getter(x))):
        if len(categories[category]) == 0:
            continue
        if category in ('formats', 'identifiers'):
            continue
        meta = category_meta.get(category, None)
        if meta is None:
            continue
        cats.append((meta['name'], meta['name'], 'N'+category))
    last_modified = db.last_modified()
    rd.outheaders['Last-Modified'] = http_date(timestampfromdt(last_modified))
    return TopLevel(last_modified, cats, rc).root
Ejemplo n.º 8
0
def get(ctx, rd, what, book_id, library_id):
    db = ctx.get_library(library_id)
    if db is None:
        raise HTTPNotFound("Library %r not found" % library_id)
    with db.safe_read_lock:
        if book_id not in ctx.allowed_book_ids(rd, db):
            raise HTTPNotFound("Book with id %r does not exist" % book_id)
        library_id = db.server_library_id  # in case library_id was None
        if what == "thumb":
            sz = rd.query.get("sz")
            w, h = 60, 80
            if sz is None:
                pass
            elif sz == "full":
                w = h = None
            elif "x" in sz:
                try:
                    w, h = map(int, sz.partition("x")[::2])
                except Exception:
                    pass
            else:
                try:
                    w = h = int(sz)
                except Exception:
                    pass
            return cover(ctx, rd, library_id, db, book_id, width=w, height=h)
        elif what == "cover":
            return cover(ctx, rd, library_id, db, book_id)
        elif what == "opf":
            mi = db.get_metadata(book_id, get_cover=False)
            rd.outheaders["Content-Type"] = "application/oebps-package+xml; charset=UTF-8"
            rd.outheaders["Last-Modified"] = http_date(timestampfromdt(mi.last_modified))
            return metadata_to_opf(mi)
        elif what == "json":
            from calibre.srv.ajax import book_to_json

            data, last_modified = book_to_json(ctx, rd, db, book_id)
            rd.outheaders["Last-Modified"] = http_date(timestampfromdt(last_modified))
            return json(ctx, rd, get, data)
        else:
            try:
                return book_fmt(ctx, rd, library_id, db, book_id, what.lower())
            except NoSuchFormat:
                raise HTTPNotFound("No %r format for the book %r" % (what.lower(), book_id))
Ejemplo n.º 9
0
def get_acquisition_feed(rc, ids, offset, page_url, up_url, id_,
        sort_by='title', ascending=True, feed_title=None):
    if not ids:
        raise HTTPNotFound('No books found')
    with rc.db.safe_read_lock:
        items = rc.db.multisort([(sort_by, ascending)], ids)
        max_items = rc.opts.max_opds_items
        offsets = Offsets(offset, max_items, len(items))
        items = items[offsets.offset:offsets.offset+max_items]
        lm = rc.last_modified()
        rc.outheaders['Last-Modified'] = http_date(timestampfromdt(lm))
        return AcquisitionFeed(id_, lm, rc, items, offsets, page_url, up_url, title=feed_title).root
Ejemplo n.º 10
0
def books(ctx, rd, library_id):
    """
    Return the metadata for the books as a JSON dictionary.

    Query parameters: ?ids=all&category_urls=true&id_is_uuid=false&device_for_template=None

    If category_urls is true the returned dictionary also contains a
    mapping of category (field) names to URLs that return the list of books in the
    given category.

    If id_is_uuid is true then the book_id is assumed to be a book uuid instead.
    """
    db = get_db(ctx, library_id)
    with db.safe_read_lock:
        id_is_uuid = rd.query.get("id_is_uuid", "false")
        ids = rd.query.get("ids")
        if ids is None or ids == "all":
            ids = db.all_book_ids()
        else:
            ids = ids.split(",")
            if id_is_uuid == "true":
                ids = {db.lookup_by_uuid(x) for x in ids}
                ids.discard(None)
            else:
                try:
                    ids = {int(x) for x in ids}
                except Exception:
                    raise HTTPNotFound("ids must a comma separated list of integers")
        last_modified = None
        category_urls = rd.query.get("category_urls", "true").lower() == "true"
        device_compatible = rd.query.get("device_compatible", "false").lower() == "true"
        device_for_template = rd.query.get("device_for_template", None)
        ans = {}
        restricted_to = ctx.allowed_book_ids(rd, db)
        for book_id in ids:
            if book_id not in restricted_to:
                ans[book_id] = None
                continue
            data, lm = book_to_json(
                ctx,
                rd,
                db,
                book_id,
                get_category_urls=category_urls,
                device_compatible=device_compatible,
                device_for_template=device_for_template,
            )
            last_modified = lm if last_modified is None else max(lm, last_modified)
            ans[book_id] = data
    if last_modified is not None:
        rd.outheaders["Last-Modified"] = http_date(timestampfromdt(last_modified))
    return ans
Ejemplo n.º 11
0
def books(ctx, rd, library_id):
    '''
    Return the metadata for the books as a JSON dictionary.

    Query parameters: ?ids=all&category_urls=true&id_is_uuid=false&device_for_template=None

    If category_urls is true the returned dictionary also contains a
    mapping of category (field) names to URLs that return the list of books in the
    given category.

    If id_is_uuid is true then the book_id is assumed to be a book uuid instead.
    '''
    db = ctx.get_library(library_id)
    if db is None:
        raise HTTPNotFound('Library %r not found' % library_id)
    with db.safe_read_lock:
        id_is_uuid = rd.query.get('id_is_uuid', 'false')
        ids = rd.query.get('ids')
        if ids is None or ids == 'all':
            ids = db.all_book_ids()
        else:
            ids = ids.split(',')
            if id_is_uuid == 'true':
                ids = {db.lookup_by_uuid(x) for x in ids}
                ids.discard(None)
            else:
                try:
                    ids = {int(x) for x in ids}
                except Exception:
                    raise HTTPNotFound('ids must a comma separated list of integers')
        last_modified = None
        category_urls = rd.query.get('category_urls', 'true').lower() == 'true'
        device_compatible = rd.query.get('device_compatible', 'false').lower() == 'true'
        device_for_template = rd.query.get('device_for_template', None)
        ans = {}
        restricted_to = ctx.restrict_to_ids(db, rd)
        for book_id in ids:
            if book_id not in restricted_to:
                ans[book_id] = None
                continue
            data, lm = book_to_json(
                ctx, rd, db, book_id, get_category_urls=category_urls,
                device_compatible=device_compatible, device_for_template=device_for_template)
            last_modified = lm if last_modified is None else max(lm, last_modified)
            ans[book_id] = data
    if last_modified is not None:
        rd.outheaders['Last-Modified'] = http_date(timestampfromdt(last_modified))
    return ans
Ejemplo n.º 12
0
def get_navcatalog(request_context, which, page_url, up_url, offset=0):
    categories = request_context.get_categories()
    if which not in categories:
        raise HTTPNotFound('Category %r not found'%which)

    items = categories[which]
    updated = request_context.last_modified()
    category_meta = request_context.db.field_metadata
    meta = category_meta.get(which, {})
    category_name = meta.get('name', which)
    feed_title = default_feed_title + ' :: ' + _('By %s') % category_name

    id_ = 'calibre-category-feed:'+which

    MAX_ITEMS = request_context.opts.max_opds_ungrouped_items

    if MAX_ITEMS > 0 and len(items) <= MAX_ITEMS:
        max_items = request_context.opts.max_opds_items
        offsets = Offsets(offset, max_items, len(items))
        items = list(items)[offsets.offset:offsets.offset+max_items]
        ans = CategoryFeed(items, which, id_, updated, request_context, offsets,
            page_url, up_url, title=feed_title)
    else:
        Group = namedtuple('Group', 'text count')
        starts = set()
        for x in items:
            val = getattr(x, 'sort', x.name)
            if not val:
                val = 'A'
            starts.add(val[0].upper())
        category_groups = OrderedDict()
        for x in sorted(starts, key=sort_key):
            category_groups[x] = len([y for y in items if
                getattr(y, 'sort', y.name).startswith(x)])
        items = [Group(x, y) for x, y in category_groups.items()]
        max_items = request_context.opts.max_opds_items
        offsets = Offsets(offset, max_items, len(items))
        items = items[offsets.offset:offsets.offset+max_items]
        ans = CategoryGroupFeed(items, which, id_, updated, request_context, offsets,
            page_url, up_url, title=feed_title)

    request_context.outheaders['Last-Modified'] = http_date(timestampfromdt(updated))

    return ans.root
Ejemplo n.º 13
0
def book(ctx, rd, book_id, library_id):
    """
    Return the metadata of the book as a JSON dictionary.

    Query parameters: ?category_urls=true&id_is_uuid=false&device_for_template=None

    If category_urls is true the returned dictionary also contains a
    mapping of category (field) names to URLs that return the list of books in the
    given category.

    If id_is_uuid is true then the book_id is assumed to be a book uuid instead.
    """
    db = get_db(ctx, library_id)
    with db.safe_read_lock:
        id_is_uuid = rd.query.get("id_is_uuid", "false")
        oid = book_id
        if id_is_uuid == "true":
            book_id = db.lookup_by_uuid(book_id)
        else:
            try:
                book_id = int(book_id)
                if not db.has_id(book_id):
                    book_id = None
            except Exception:
                book_id = None
        if book_id is None or book_id not in ctx.allowed_book_ids(rd, db):
            raise HTTPNotFound("Book with id %r does not exist" % oid)
        category_urls = rd.query.get("category_urls", "true").lower()
        device_compatible = rd.query.get("device_compatible", "false").lower()
        device_for_template = rd.query.get("device_for_template", None)

        data, last_modified = book_to_json(
            ctx,
            rd,
            db,
            book_id,
            get_category_urls=category_urls == "true",
            device_compatible=device_compatible == "true",
            device_for_template=device_for_template,
        )
    rd.outheaders["Last-Modified"] = http_date(timestampfromdt(last_modified))
    return data
Ejemplo n.º 14
0
def opds_categorygroup(ctx, rd, category, which):
    try:
        offset = int(rd.query.get("offset", 0))
    except Exception:
        raise HTTPNotFound("Not found")

    if not which or not category:
        raise HTTPNotFound("Not found")

    rc = RequestContext(ctx, rd)
    categories = rc.get_categories()
    page_url = rc.url_for("/opds/categorygroup", category=category, which=which)

    category = unhexlify(category)
    if category not in categories:
        raise HTTPNotFound("Category %r not found" % which)
    category_meta = rc.db.field_metadata
    meta = category_meta.get(category, {})
    category_name = meta.get("name", which)
    which = unhexlify(which)
    feed_title = default_feed_title + " :: " + (_("By {0} :: {1}").format(category_name, which))
    owhich = hexlify("N" + which)
    up_url = rc.url_for("/opds/navcatalog", which=owhich)
    items = categories[category]

    def belongs(x, which):
        return getattr(x, "sort", x.name).lower().startswith(which.lower())

    items = [x for x in items if belongs(x, which)]
    if not items:
        raise HTTPNotFound("No items in group %r:%r" % (category, which))
    updated = rc.last_modified()

    id_ = "calibre-category-group-feed:" + category + ":" + which

    max_items = rc.opts.max_opds_items
    offsets = Offsets(offset, max_items, len(items))
    items = list(items)[offsets.offset : offsets.offset + max_items]

    rc.outheaders["Last-Modified"] = http_date(timestampfromdt(updated))

    return CategoryFeed(items, category, id_, updated, rc, offsets, page_url, up_url, title=feed_title).root
Ejemplo n.º 15
0
def opds_categorygroup(ctx, rd, category, which):
    try:
        offset = int(rd.query.get('offset', 0))
    except Exception:
        raise HTTPNotFound('Not found')

    if not which or not category:
        raise HTTPNotFound('Not found')

    rc = RequestContext(ctx, rd)
    categories = rc.get_categories()
    page_url = rc.url_for('/opds/categorygroup', category=category, which=which)

    category = from_hex_unicode(category)
    if category not in categories:
        raise HTTPNotFound('Category %r not found'%which)
    category_meta = rc.db.field_metadata
    meta = category_meta.get(category, {})
    category_name = meta.get('name', which)
    which = from_hex_unicode(which)
    feed_title = default_feed_title + ' :: ' + (_('By {0} :: {1}').format(category_name, which))
    owhich = as_hex_unicode('N'+which)
    up_url = rc.url_for('/opds/navcatalog', which=owhich)
    items = categories[category]

    def belongs(x, which):
        return getattr(x, 'sort', x.name).lower().startswith(which.lower())
    items = [x for x in items if belongs(x, which)]
    if not items:
        raise HTTPNotFound('No items in group %r:%r'%(category, which))
    updated = rc.last_modified()

    id_ = 'calibre-category-group-feed:'+category+':'+which

    max_items = rc.opts.max_opds_items
    offsets = Offsets(offset, max_items, len(items))
    items = list(items)[offsets.offset:offsets.offset+max_items]

    rc.outheaders['Last-Modified'] = http_date(timestampfromdt(updated))

    return CategoryFeed(items, category, id_, updated, rc, offsets, page_url, up_url, title=feed_title).root
Ejemplo n.º 16
0
def book(ctx, rd, book_id, library_id):
    '''
    Return the metadata of the book as a JSON dictionary.

    Query parameters: ?category_urls=true&id_is_uuid=false&device_for_template=None

    If category_urls is true the returned dictionary also contains a
    mapping of category (field) names to URLs that return the list of books in the
    given category.

    If id_is_uuid is true then the book_id is assumed to be a book uuid instead.
    '''
    db = ctx.get_library(library_id)
    if db is None:
        raise HTTPNotFound('Library %r not found' % library_id)
    with db.safe_read_lock:
        id_is_uuid = rd.query.get('id_is_uuid', 'false')
        oid = book_id
        if id_is_uuid == 'true':
            book_id = db.lookup_by_uuid(book_id)
        else:
            try:
                book_id = int(book_id)
                if not db.has_id(book_id):
                    book_id = None
            except Exception:
                book_id = None
        if book_id is None:
            raise HTTPNotFound('Book with id %r does not exist' % oid)
        category_urls = rd.query.get('category_urls', 'true').lower()
        device_compatible = rd.query.get('device_compatible', 'false').lower()
        device_for_template = rd.query.get('device_for_template', None)

        data, last_modified = book_to_json(ctx, rd, db, book_id,
                get_category_urls=category_urls == 'true',
                device_compatible=device_compatible == 'true',
                device_for_template=device_for_template)
    rd.outheaders['Last-Modified'] = http_date(timestampfromdt(last_modified))
    return data
Ejemplo n.º 17
0
    def fill_in_books_box(self, selected_item):
        '''
        Given the selected row in the left-hand box, fill in the grid with
        the books that contain that data.
        '''
        # Do a bit of fix-up on the items so that the search works.
        if selected_item.startswith('.'):
            sv = '.' + selected_item
        else:
            sv = selected_item
        sv = self.current_key + ':"=' + sv.replace('"', r'\"') + '"'
        if gprefs['qv_respects_vls']:
            books = self.db.search(sv, return_matches=True, sort_results=False)
        else:
            books = self.db.new_api.search(sv)

        self.books_table.setRowCount(len(books))
        label_text = _('&Books with selected item "{0}": {1}')
        if self.is_pane:
            label_text = label_text.replace('&', '')
        self.books_label.setText(label_text.format(selected_item, len(books)))

        select_item = None
        self.books_table.setSortingEnabled(False)
        self.books_table.blockSignals(True)
        tt = ('<p>' + _(
            'Double click on a book to change the selection in the library view or '
              'change the column shown in the left-hand pane. '
              'Shift- or Control- double click to edit the metadata of a book, '
              'which also changes the selected book.'
              ) + '</p>')
        for row, b in enumerate(books):
            mi = self.db.new_api.get_proxy_metadata(b)
            for col in self.column_order:
                try:
                    if col == 'title':
                        a = TableItem(mi.title, mi.title_sort)
                        if b == self.current_book_id:
                            select_item = a
                    elif col == 'authors':
                        a = TableItem(' & '.join(mi.authors), mi.author_sort)
                    elif col == 'series':
                        series = mi.format_field('series')[1]
                        if series is None:
                            a = TableItem('', '', 0)
                        else:
                            a = TableItem(series, mi.series, mi.series_index)
                    elif col == 'size':
                        v = mi.get('book_size')
                        if v is not None:
                            a = TableItem('{:n}'.format(v), v)
                            a.setTextAlignment(Qt.AlignRight)
                        else:
                            a = TableItem(' ', None)
                    elif self.fm[col]['datatype'] == 'series':
                        v = mi.format_field(col)[1]
                        a = TableItem(v, mi.get(col), mi.get(col+'_index'))
                    elif self.fm[col]['datatype'] == 'datetime':
                        v = mi.format_field(col)[1]
                        d = mi.get(col)
                        if d is None:
                            d = UNDEFINED_DATE
                        a = TableItem(v, timestampfromdt(d))
                    elif self.fm[col]['datatype'] in ('float', 'int'):
                        v = mi.format_field(col)[1]
                        sv = mi.get(col)
                        a = TableItem(v, sv)
                    else:
                        v = mi.format_field(col)[1]
                        a = TableItem(v, v)
                except:
                    traceback.print_exc()
                    a = TableItem(_('Something went wrong while filling in the table'), '')
                a.setData(Qt.UserRole, b)
                a.setToolTip(tt)
                self.books_table.setItem(row, self.key_to_table_widget_column(col), a)
                self.books_table.setRowHeight(row, self.books_table_row_height)
        self.books_table.blockSignals(False)
        self.books_table.setSortingEnabled(True)
        if select_item is not None:
            self.books_table.setCurrentItem(select_item)
            self.books_table.scrollToItem(select_item, QAbstractItemView.PositionAtCenter)
        self.set_search_text(sv)