Пример #1
0
 def __new__(self, series, series_index):
     series = roman = escape(series or u'')
     if series and series_index is not None:
         roman = _('Number {1} of <em>{0}</em>').format(
             escape(series), escape(fmt_sidx(series_index, use_roman=True)))
         series = escape(series + ' [%s]'%fmt_sidx(series_index, use_roman=False))
     s = unicode.__new__(self, series)
     s.roman = roman
     return s
Пример #2
0
 def __new__(self, series, series_index):
     if series and series_index is not None:
         roman = _('{1} of <em>{0}</em>').format(
             escape(series), escape(fmt_sidx(series_index, use_roman=True)))
         combined = _('{1} of <em>{0}</em>').format(
             escape(series), escape(fmt_sidx(series_index, use_roman=False)))
     else:
         combined = roman = escape(series or u'')
     s = unicode_type.__new__(self, combined)
     s.roman = roman
     s.name = escape(series or u'')
     s.number = escape(fmt_sidx(series_index or 1.0, use_roman=False))
     s.roman_number = escape(fmt_sidx(series_index or 1.0, use_roman=True))
     return s
Пример #3
0
 def helpEvent(self, event, view, option, index):
     if event is not None and view is not None and event.type() == QEvent.ToolTip:
         try:
             db = index.model().db
         except AttributeError:
             return False
         try:
             book_id = db.id(index.row())
         except (ValueError, IndexError, KeyError):
             return False
         db = db.new_api
         device_connected = self.parent().gui.device_connected
         on_device = device_connected is not None and db.field_for('ondevice', book_id)
         p = prepare_string_for_xml
         title = db.field_for('title', book_id)
         authors = db.field_for('authors', book_id)
         if title and authors:
             title = '<b>%s</b>' % ('<br>'.join(wrap(p(title), 120)))
             authors = '<br>'.join(wrap(p(' & '.join(authors)), 120))
             tt = '%s<br><br>%s' % (title, authors)
             series = db.field_for('series', book_id)
             if series:
                 use_roman_numbers=config['use_roman_numerals_for_series_number']
                 val = _('Book %(sidx)s of <span class="series_name">%(series)s</span>')%dict(
                     sidx=fmt_sidx(db.field_for('series_index', book_id), use_roman=use_roman_numbers),
                     series=p(series))
                 tt += '<br><br>' + val
             if on_device:
                 val = _('This book is on the device in %s') % on_device
                 tt += '<br><br>' + val
             QToolTip.showText(event.globalPos(), tt, view)
             return True
     return False
Пример #4
0
    def orig_series_index_string(self):
#        debug_print("SeriesBook:orig_series_index - self._orig_series_index=", self._orig_series_index)
#        debug_print("SeriesBook:orig_series_index - self._orig_series_index.__class__=", self._orig_series_index.__class__)
        if self._orig_series_index_string is not None:
            return self._orig_series_index_string
        
        return fmt_sidx(self._orig_series_index)
Пример #5
0
 def insert_series_index(self, series):
     db = self.dbref()
     if db is None or not series:
         return
     num = db.get_next_series_num_for(series)
     sidx = fmt_sidx(num)
     self.setText(self.text() + ' [%s]' % sidx)
Пример #6
0
def format_text(mi, prefs):
    with preserve_fields(mi, 'authors formatted_series_index'):
        mi.authors = [a for a in mi.authors if a != _('Unknown')]
        mi.formatted_series_index = fmt_sidx(
            mi.series_index or 0,
            use_roman=config['use_roman_numerals_for_series_number'])
        return tuple(format_fields(mi, prefs))
Пример #7
0
 def helpEvent(self, event, view, option, index):
     if event is not None and view is not None and event.type() == QEvent.ToolTip:
         try:
             db = index.model().db
         except AttributeError:
             return False
         try:
             book_id = db.id(index.row())
         except (ValueError, IndexError, KeyError):
             return False
         db = db.new_api
         device_connected = self.parent().gui.device_connected
         on_device = device_connected is not None and db.field_for('ondevice', book_id)
         p = prepare_string_for_xml
         title = db.field_for('title', book_id)
         authors = db.field_for('authors', book_id)
         if title and authors:
             title = '<b>%s</b>' % ('<br>'.join(wrap(p(title), 120)))
             authors = '<br>'.join(wrap(p(' & '.join(authors)), 120))
             tt = '%s<br><br>%s' % (title, authors)
             series = db.field_for('series', book_id)
             if series:
                 use_roman_numbers=config['use_roman_numerals_for_series_number']
                 val = _('Book %(sidx)s of <span class="series_name">%(series)s</span>')%dict(
                     sidx=fmt_sidx(db.field_for('series_index', book_id), use_roman=use_roman_numbers),
                     series=p(series))
                 tt += '<br><br>' + val
             if on_device:
                 val = _('This book is on the device in %s') % on_device
                 tt += '<br><br>' + val
             QToolTip.showText(event.globalPos(), tt, view)
             return True
     return False
Пример #8
0
 def insert_series_index(self, series):
     db = self.dbref()
     if db is None or not series:
         return
     num = db.get_next_series_num_for(series)
     sidx = fmt_sidx(num)
     self.setText(self.text() + ' [%s]' % sidx)
Пример #9
0
    def default_cover(self):
        '''
        Create a generic cover for books that dont have a cover
        '''
        from calibre.ebooks.metadata import authors_to_string, fmt_sidx
        if self.no_default_cover:
            return None
        self.log('Generating default cover')
        m = self.oeb.metadata
        title = unicode(m.title[0])
        authors = [unicode(x) for x in m.creator if x.role == 'aut']
        series_string = None
        if m.series and m.series_index:
            series_string = _('Book %(sidx)s of %(series)s') % dict(
                sidx=fmt_sidx(m.series_index[0], use_roman=True),
                series=unicode(m.series[0]))

        try:
            from calibre.ebooks import calibre_cover
            img_data = calibre_cover(title,
                                     authors_to_string(authors),
                                     series_string=series_string)
            id, href = self.oeb.manifest.generate('cover', u'cover_image.jpg')
            item = self.oeb.manifest.add(id,
                                         href,
                                         guess_type('t.jpg')[0],
                                         data=img_data)
            m.clear('cover')
            m.add('cover', item.id)

            return item.href
        except:
            self.log.exception('Failed to generate default cover')
        return None
Пример #10
0
    def default_cover(self):
        '''
        Create a generic cover for books that dont have a cover
        '''
        from calibre.ebooks.metadata import authors_to_string, fmt_sidx
        if self.no_default_cover:
            return None
        self.log('Generating default cover')
        m = self.oeb.metadata
        title = unicode(m.title[0])
        authors = [unicode(x) for x in m.creator if x.role == 'aut']
        series_string = None
        if m.series and m.series_index:
            series_string = _('Book %(sidx)s of %(series)s')%dict(
                    sidx=fmt_sidx(m.series_index[0], use_roman=True),
                    series=unicode(m.series[0]))

        try:
            from calibre.ebooks import calibre_cover
            img_data = calibre_cover(title, authors_to_string(authors),
                    series_string=series_string)
            id, href = self.oeb.manifest.generate('cover',
                    u'cover_image.jpg')
            item = self.oeb.manifest.add(id, href, guess_type('t.jpg')[0],
                        data=img_data)
            m.clear('cover')
            m.add('cover', item.id)

            return item.href
        except:
            self.log.exception('Failed to generate default cover')
        return None
Пример #11
0
def ACQUISITION_ENTRY(book_id, updated, request_context):
    field_metadata = request_context.db.field_metadata
    mi = request_context.db.get_metadata(book_id)
    extra = []
    if (mi.rating or 0) > 0:
        rating = rating_to_stars(mi.rating)
        extra.append(_('RATING: %s<br />')%rating)
    if mi.tags:
        extra.append(_('TAGS: %s<br />')%xml(format_tag_string(mi.tags, None)))
    if mi.series:
        extra.append(_('SERIES: %(series)s [%(sidx)s]<br />')%
                dict(series=xml(mi.series),
                sidx=fmt_sidx(float(mi.series_index))))
    for key in filter(request_context.ctx.is_field_displayable, field_metadata.ignorable_field_keys()):
        name, val = mi.format_field(key)
        if val:
            fm = field_metadata[key]
            datatype = fm['datatype']
            if datatype == 'text' and fm['is_multiple']:
                extra.append('%s: %s<br />'%
                             (xml(name),
                              xml(format_tag_string(val,
                                    fm['is_multiple']['ui_to_list'],
                                    joinval=fm['is_multiple']['list_to_ui']))))
            elif datatype == 'comments' or (fm['datatype'] == 'composite' and fm['display'].get('contains_html', False)):
                extra.append('%s: %s<br />'%(xml(name), comments_to_html(str(val))))
            else:
                extra.append('%s: %s<br />'%(xml(name), xml(str(val))))
    if mi.comments:
        comments = comments_to_html(mi.comments)
        extra.append(comments)
    if extra:
        extra = html_to_lxml('\n'.join(extra))
    ans = E.entry(TITLE(mi.title), E.author(E.name(authors_to_string(mi.authors))), ID('urn:uuid:' + mi.uuid), UPDATED(mi.last_modified),
                  E.published(mi.timestamp.isoformat()))
    if mi.pubdate and not is_date_undefined(mi.pubdate):
        ans.append(ans.makeelement('{%s}date' % DC_NS))
        ans[-1].text = mi.pubdate.isoformat()
    if len(extra):
        ans.append(E.content(extra, type='xhtml'))
    get = partial(request_context.ctx.url_for, '/get', book_id=book_id, library_id=request_context.library_id)
    if mi.formats:
        fm = mi.format_metadata
        for fmt in mi.formats:
            fmt = fmt.lower()
            mt = guess_type('a.'+fmt)[0]
            if mt:
                link = E.link(type=mt, href=get(what=fmt), rel="http://opds-spec.org/acquisition")
                ffm = fm.get(fmt.upper())
                if ffm:
                    link.set('length', str(ffm['size']))
                    link.set('mtime', ffm['mtime'].isoformat())
                ans.append(link)
    ans.append(E.link(type='image/jpeg', href=get(what='cover'), rel="http://opds-spec.org/cover"))
    ans.append(E.link(type='image/jpeg', href=get(what='thumb'), rel="http://opds-spec.org/thumbnail"))
    ans.append(E.link(type='image/jpeg', href=get(what='cover'), rel="http://opds-spec.org/image"))
    ans.append(E.link(type='image/jpeg', href=get(what='thumb'), rel="http://opds-spec.org/image/thumbnail"))

    return ans
Пример #12
0
 def __init__(self, series_name, series_index):
     if series_name:
         text = '%s [%s]' % (series_name, fmt_sidx(series_index))
     else:
         text = ''
     ReadOnlyTableWidgetItem.__init__(self, text)
     self.series_name = series_name
     self.series_index = series_index
Пример #13
0
 def __init__(self, series_name, series_index):
     if series_name:
         text = '%s [%s]' % (series_name, fmt_sidx(series_index))
     else:
         text = ''
     ReadOnlyTableWidgetItem.__init__(self, text)
     self.series_name = series_name
     self.series_index = series_index
Пример #14
0
 def format_series_index(self, val=None):
     from calibre.ebooks.metadata import fmt_sidx
     v = self.series_index if val is None else val
     try:
         x = float(v)
     except (ValueError, TypeError):
         x = 1
     return fmt_sidx(x)
Пример #15
0
 def format_series_index(self, val=None):
     from calibre.ebooks.metadata import fmt_sidx
     v = self.series_index if val is None else val
     try:
         x = float(v)
     except (ValueError, TypeError):
         x = 1
     return fmt_sidx(x)
Пример #16
0
def ACQUISITION_ENTRY(book_id, updated, request_context):
    field_metadata = request_context.db.field_metadata
    mi = request_context.db.get_metadata(book_id)
    extra = []
    if mi.rating > 0:
        rating = u"".join(repeat(u"\u2605", int(mi.rating / 2.0)))
        extra.append(_("RATING: %s<br />") % rating)
    if mi.tags:
        extra.append(_("TAGS: %s<br />") % xml(format_tag_string(mi.tags, None)))
    if mi.series:
        extra.append(
            _("SERIES: %(series)s [%(sidx)s]<br />")
            % dict(series=xml(mi.series), sidx=fmt_sidx(float(mi.series_index)))
        )
    for key in filter(request_context.ctx.is_field_displayable, field_metadata.ignorable_field_keys()):
        name, val = mi.format_field(key)
        if val:
            fm = field_metadata[key]
            datatype = fm["datatype"]
            if datatype == "text" and fm["is_multiple"]:
                extra.append(
                    "%s: %s<br />"
                    % (
                        xml(name),
                        xml(
                            format_tag_string(
                                val, fm["is_multiple"]["ui_to_list"], joinval=fm["is_multiple"]["list_to_ui"]
                            )
                        ),
                    )
                )
            elif datatype == "comments" or (
                fm["datatype"] == "composite" and fm["display"].get("contains_html", False)
            ):
                extra.append("%s: %s<br />" % (xml(name), comments_to_html(unicode(val))))
            else:
                extra.append("%s: %s<br />" % (xml(name), xml(unicode(val))))
    if mi.comments:
        comments = comments_to_html(mi.comments)
        extra.append(comments)
    if extra:
        extra = html_to_lxml("\n".join(extra))
    ans = E.entry(
        TITLE(mi.title), E.author(E.name(authors_to_string(mi.authors))), ID("urn:uuid:" + mi.uuid), UPDATED(updated)
    )
    if len(extra):
        ans.append(E.content(extra, type="xhtml"))
    get = partial(request_context.ctx.url_for, "/get", book_id=book_id, library_id=request_context.library_id)
    if mi.formats:
        for fmt in mi.formats:
            fmt = fmt.lower()
            mt = guess_type("a." + fmt)[0]
            if mt:
                ans.append(E.link(type=mt, href=get(what=fmt), rel="http://opds-spec.org/acquisition"))
    ans.append(E.link(type="image/jpeg", href=get(what="cover"), rel="http://opds-spec.org/cover"))
    ans.append(E.link(type="image/jpeg", href=get(what="thumb"), rel="http://opds-spec.org/thumbnail"))

    return ans
Пример #17
0
def ACQUISITION_ENTRY(book_id, updated, request_context):
    field_metadata = request_context.db.field_metadata
    mi = request_context.db.get_metadata(book_id)
    extra = []
    if mi.rating > 0:
        rating = rating_to_stars(mi.rating)
        extra.append(_('RATING: %s<br />')%rating)
    if mi.tags:
        extra.append(_('TAGS: %s<br />')%xml(format_tag_string(mi.tags, None)))
    if mi.series:
        extra.append(_('SERIES: %(series)s [%(sidx)s]<br />')%
                dict(series=xml(mi.series),
                sidx=fmt_sidx(float(mi.series_index))))
    for key in filter(request_context.ctx.is_field_displayable, field_metadata.ignorable_field_keys()):
        name, val = mi.format_field(key)
        if val:
            fm = field_metadata[key]
            datatype = fm['datatype']
            if datatype == 'text' and fm['is_multiple']:
                extra.append('%s: %s<br />'%
                             (xml(name),
                              xml(format_tag_string(val,
                                    fm['is_multiple']['ui_to_list'],
                                    joinval=fm['is_multiple']['list_to_ui']))))
            elif datatype == 'comments' or (fm['datatype'] == 'composite' and
                            fm['display'].get('contains_html', False)):
                extra.append('%s: %s<br />'%(xml(name), comments_to_html(unicode(val))))
            else:
                extra.append('%s: %s<br />'%(xml(name), xml(unicode(val))))
    if mi.comments:
        comments = comments_to_html(mi.comments)
        extra.append(comments)
    if extra:
        extra = html_to_lxml('\n'.join(extra))
    ans = E.entry(TITLE(mi.title), E.author(E.name(authors_to_string(mi.authors))), ID('urn:uuid:' + mi.uuid), UPDATED(mi.last_modified),
                  E.published(mi.timestamp.isoformat()))
    if mi.pubdate and not is_date_undefined(mi.pubdate):
        ans.append(ans.makeelement('{%s}date' % DC_NS))
        ans[-1].text = mi.pubdate.isoformat()
    if len(extra):
        ans.append(E.content(extra, type='xhtml'))
    get = partial(request_context.ctx.url_for, '/get', book_id=book_id, library_id=request_context.library_id)
    if mi.formats:
        fm = mi.format_metadata
        for fmt in mi.formats:
            fmt = fmt.lower()
            mt = guess_type('a.'+fmt)[0]
            if mt:
                link = E.link(type=mt, href=get(what=fmt), rel="http://opds-spec.org/acquisition")
                ffm = fm.get(fmt.upper())
                if ffm:
                    link.set('length', str(ffm['size']))
                    link.set('mtime', ffm['mtime'].isoformat())
                ans.append(link)
    ans.append(E.link(type='image/jpeg', href=get(what='cover'), rel="http://opds-spec.org/cover"))
    ans.append(E.link(type='image/jpeg', href=get(what='thumb'), rel="http://opds-spec.org/thumbnail"))

    return ans
Пример #18
0
 def __init__(self, series, series_index=None):
     display = ''
     if series:
         if series_index:
             from calibre.ebooks.metadata import fmt_sidx
             display = '%s [%s]' % (series, fmt_sidx(series_index))
             self.sortKey = '%s%04d' % (series, series_index)
         else:
             display = series
             self.sortKey = series
     ReadOnlyTableWidgetItem.__init__(self, display)
Пример #19
0
def set_series(root, prefixes, refines, series, series_index):
    for meta in XPath(
            './opf:metadata/opf:meta[@name="calibre:series" or @name="calibre:series_index"]'
    )(root):
        remove_element(meta, refines)
    for meta in XPath(
            './opf:metadata/opf:meta[@property="belongs-to-collection"]')(
                root):
        remove_element(meta, refines)
    if series:
        create_series(root, refines, series, fmt_sidx(series_index))
Пример #20
0
 def __init__(self, series, series_index=None):
     display = ''
     if series:
         if series_index:
             from calibre.ebooks.metadata import fmt_sidx
             display = '%s [%s]' % (series, fmt_sidx(series_index))
             self.sortKey = '%s%04d' % (series, series_index)
         else:
             display = series
             self.sortKey = series
     ReadOnlyTableWidgetItem.__init__(self, display)
Пример #21
0
def ACQUISITION_ENTRY(book_id, updated, request_context):
    field_metadata = request_context.db.field_metadata
    mi = request_context.db.get_metadata(book_id)
    extra = []
    if mi.rating > 0:
        rating = u''.join(repeat(u'\u2605', int(mi.rating/2.)))
        extra.append(_('RATING: %s<br />')%rating)
    if mi.tags:
        extra.append(_('TAGS: %s<br />')%xml(format_tag_string(mi.tags, None, no_tag_count=True)))
    if mi.series:
        extra.append(_('SERIES: %(series)s [%(sidx)s]<br />')%
                dict(series=xml(mi.series),
                sidx=fmt_sidx(float(mi.series_index))))
    for key in field_metadata.ignorable_field_keys():
        name, val = mi.format_field(key)
        if val:
            fm = field_metadata[key]
            datatype = fm['datatype']
            if datatype == 'text' and fm['is_multiple']:
                extra.append('%s: %s<br />'%
                             (xml(name),
                              xml(format_tag_string(val,
                                    fm['is_multiple']['ui_to_list'],
                                    no_tag_count=True,
                                    joinval=fm['is_multiple']['list_to_ui']))))
            elif datatype == 'comments' or (fm['datatype'] == 'composite' and
                            fm['display'].get('contains_html', False)):
                extra.append('%s: %s<br />'%(xml(name), comments_to_html(unicode(val))))
            else:
                extra.append('%s: %s<br />'%(xml(name), xml(unicode(val))))
    if mi.comments:
        comments = comments_to_html(mi.comments)
        extra.append(comments)
    if extra:
        extra = html_to_lxml('\n'.join(extra))
    ans = E.entry(TITLE(mi.title), E.author(E.name(authors_to_string(mi.authors))), ID('urn:uuid:' + mi.uuid), UPDATED(updated))
    if len(extra):
        ans.append(E.content(extra, type='xhtml'))
    get = partial(request_context.ctx.url_for, '/get', book_id=book_id, library_id=request_context.library_id)
    if mi.formats:
        for fmt in mi.formats:
            fmt = fmt.lower()
            mt = guess_type('a.'+fmt)[0]
            if mt:
                ans.append(E.link(type=mt, href=get(what=fmt), rel="http://opds-spec.org/acquisition"))
    ans.append(E.link(type='image/jpeg', href=get(what='cover'), rel="http://opds-spec.org/cover"))
    ans.append(E.link(type='image/jpeg', href=get(what='thumb'), rel="http://opds-spec.org/thumbnail"))

    return ans
 def __init__(self,
              series_name,
              series_index,
              is_original=False,
              assigned_index=None):
     if series_name:
         text = '%s [%s]' % (series_name, fmt_sidx(series_index))
     else:
         text = ''
     ReadOnlyTableWidgetItem.__init__(self, text)
     if assigned_index is not None:
         self.setIcon(get_icon('images/lock.png'))
         self.setToolTip('Value assigned by user')
     if is_original:
         #self.foreground().setColor(Qt.darkGray)
         self.setForeground(Qt.darkGray)
Пример #23
0
 def generate_cover(self, *args):
     from calibre.ebooks import calibre_cover
     from calibre.ebooks.metadata import fmt_sidx
     from calibre.gui2 import config
     title = self.dialog.title.current_val
     author = authors_to_string(self.dialog.authors.current_val)
     if not title or not author:
         return error_dialog(self, _('Specify title and author'),
                 _('You must specify a title and author before generating '
                     'a cover'), show=True)
     series = self.dialog.series.current_val
     series_string = None
     if series:
         series_string = _('Book %(sidx)s of %(series)s')%dict(
                 sidx=fmt_sidx(self.dialog.series_index.current_val,
                 use_roman=config['use_roman_numerals_for_series_number']),
                 series=series)
     self.current_val = calibre_cover(title, author,
             series_string=series_string)
Пример #24
0
 def generate_cover(self, *args):
     from calibre.ebooks import calibre_cover
     from calibre.ebooks.metadata import fmt_sidx
     from calibre.gui2 import config
     title = self.dialog.title.current_val
     author = authors_to_string(self.dialog.authors.current_val)
     if not title or not author:
         return error_dialog(
             self,
             _('Specify title and author'),
             _('You must specify a title and author before generating '
               'a cover'),
             show=True)
     series = self.dialog.series.current_val
     series_string = None
     if series:
         series_string = _('Book %(sidx)s of %(series)s') % dict(
             sidx=fmt_sidx(
                 self.dialog.series_index.current_val,
                 use_roman=config['use_roman_numerals_for_series_number']),
             series=series)
     self.current_val = calibre_cover(title,
                                      author,
                                      series_string=series_string)
Пример #25
0
def format_text(mi, prefs):
    with preserve_fields(mi, 'authors formatted_series_index'):
        mi.authors = [a for a in mi.authors if a != _('Unknown')]
        mi.formatted_series_index = fmt_sidx(mi.series_index or 0, use_roman=get_use_roman())
        return tuple(format_fields(mi, prefs))
Пример #26
0
 def series_index_string(self, column=None):
     if self._series_index_format is not None:
         return self._series_index_format % self._mi.series_index
     return fmt_sidx(self._mi.series_index)
Пример #27
0
def render_data(mi, use_roman_numbers=True, all_fields=False):
    ans = []
    comment_fields = []
    isdevice = not hasattr(mi, "id")
    fm = getattr(mi, "field_metadata", field_metadata)
    row = u'<td class="title">%s</td><td class="value">%s</td>'

    for field, display in get_field_list(fm):
        metadata = fm.get(field, None)
        if field == "sort":
            field = "title_sort"
        if all_fields:
            display = True
        if metadata["datatype"] == "bool":
            isnull = mi.get(field) is None
        else:
            isnull = mi.is_null(field)
        if not display or not metadata or isnull:
            continue
        name = metadata["name"]
        if not name:
            name = field
        name += ":"
        if metadata["datatype"] == "comments" or field == "comments":
            val = getattr(mi, field)
            if val:
                val = force_unicode(val)
                comment_fields.append(comments_to_html(val))
        elif metadata["datatype"] == "rating":
            val = getattr(mi, field)
            if val:
                val = val / 2.0
                ans.append(
                    (
                        field,
                        u'<td class="title">%s</td><td class="rating value" '
                        "style='font-family:\"%s\"'>%s</td>" % (name, rating_font(), u"\u2605" * int(val)),
                    )
                )
        elif metadata["datatype"] == "composite" and metadata["display"].get("contains_html", False):
            val = getattr(mi, field)
            if val:
                val = force_unicode(val)
                ans.append((field, row % (name, comments_to_html(val))))
        elif field == "path":
            if mi.path:
                path = force_unicode(mi.path, filesystem_encoding)
                scheme = u"devpath" if isdevice else u"path"
                url = prepare_string_for_xml(path if isdevice else unicode(mi.id), True)
                pathstr = _("Click to open")
                extra = ""
                if isdevice:
                    durl = url
                    if durl.startswith("mtp:::"):
                        durl = ":::".join((durl.split(":::"))[2:])
                    extra = '<br><span style="font-size:smaller">%s</span>' % (prepare_string_for_xml(durl))
                link = u'<a href="%s:%s" title="%s">%s</a>%s' % (
                    scheme,
                    url,
                    prepare_string_for_xml(path, True),
                    pathstr,
                    extra,
                )
                ans.append((field, row % (name, link)))
        elif field == "formats":
            if isdevice:
                continue
            fmts = [u'<a href="format:%s:%s">%s</a>' % (mi.id, x, x) for x in mi.formats]
            ans.append((field, row % (name, u", ".join(fmts))))
        elif field == "identifiers":
            urls = urls_from_identifiers(mi.identifiers)
            links = [
                u'<a href="%s" title="%s:%s">%s</a>' % (url, id_typ, id_val, name) for name, id_typ, id_val, url in urls
            ]
            links = u", ".join(links)
            if links:
                ans.append((field, row % (_("Ids") + ":", links)))
        elif field == "authors" and not isdevice:
            authors = []
            formatter = EvalFormatter()
            for aut in mi.authors:
                link = ""
                if mi.author_link_map[aut]:
                    link = mi.author_link_map[aut]
                elif gprefs.get("default_author_link"):
                    vals = {"author": aut.replace(" ", "+")}
                    try:
                        vals["author_sort"] = mi.author_sort_map[aut].replace(" ", "+")
                    except:
                        vals["author_sort"] = aut.replace(" ", "+")
                    link = formatter.safe_format(gprefs.get("default_author_link"), vals, "", vals)
                if link:
                    link = prepare_string_for_xml(link)
                    authors.append(u'<a calibre-data="authors" href="%s">%s</a>' % (link, aut))
                else:
                    authors.append(aut)
            ans.append((field, row % (name, u" & ".join(authors))))
        elif field == "languages":
            if not mi.languages:
                continue
            names = filter(None, map(calibre_langcode_to_name, mi.languages))
            ans.append((field, row % (name, u", ".join(names))))
        else:
            val = mi.format_field(field)[-1]
            if val is None:
                continue
            val = prepare_string_for_xml(val)
            if metadata["datatype"] == "series":
                sidx = mi.get(field + "_index")
                if sidx is None:
                    sidx = 1.0
                val = _('Book %(sidx)s of <span class="series_name">%(series)s</span>') % dict(
                    sidx=fmt_sidx(sidx, use_roman=use_roman_numbers), series=prepare_string_for_xml(getattr(mi, field))
                )
            elif metadata["datatype"] == "datetime":
                aval = getattr(mi, field)
                if is_date_undefined(aval):
                    continue

            ans.append((field, row % (name, val)))

    dc = getattr(mi, "device_collections", [])
    if dc:
        dc = u", ".join(sorted(dc, key=sort_key))
        ans.append(("device_collections", row % (_("Collections") + ":", dc)))

    def classname(field):
        try:
            dt = fm[field]["datatype"]
        except:
            dt = "text"
        return "datatype_%s" % dt

    ans = [u'<tr id="%s" class="%s">%s</tr>' % (field.replace("#", "_"), classname(field), html) for field, html in ans]
    # print '\n'.join(ans)
    return u'<table class="fields">%s</table>' % (u"\n".join(ans)), comment_fields
Пример #28
0
    def do_one(self, id):
        remove_all, remove, add, au, aus, do_aus, rating, pub, do_series, \
            do_autonumber, do_remove_format, remove_format, do_swap_ta, \
            do_remove_conv, do_auto_author, series, do_series_restart, \
            series_start_value, do_title_case, cover_action, clear_series, \
            pubdate, adddate, do_title_sort, languages, clear_languages, \
            restore_original = self.args


        # first loop: All changes that modify the filesystem and commit
        # immediately. We want to
        # try hard to keep the DB and the file system in sync, even in the face
        # of exceptions or forced exits.
        if self.current_phase == 1:
            title_set = False
            if do_swap_ta:
                title = self.db.title(id, index_is_id=True)
                aum = self.db.authors(id, index_is_id=True)
                if aum:
                    aum = [a.strip().replace('|', ',') for a in aum.split(',')]
                    new_title = authors_to_string(aum)
                    if do_title_case:
                        new_title = titlecase(new_title)
                    self.db.set_title(id, new_title, notify=False)
                    title_set = True
                if title:
                    new_authors = string_to_authors(title)
                    self.db.set_authors(id, new_authors, notify=False)
            if do_title_case and not title_set:
                title = self.db.title(id, index_is_id=True)
                self.db.set_title(id, titlecase(title), notify=False)
            if do_title_sort:
                title = self.db.title(id, index_is_id=True)
                if languages:
                    lang = languages[0]
                else:
                    lang = self.db.languages(id, index_is_id=True)
                    if lang:
                        lang = lang.partition(',')[0]
                self.db.set_title_sort(id, title_sort(title, lang=lang),
                        notify=False)
            if au:
                self.db.set_authors(id, string_to_authors(au), notify=False)
            if cover_action == 'remove':
                self.db.remove_cover(id)
            elif cover_action == 'generate':
                from calibre.ebooks import calibre_cover
                from calibre.ebooks.metadata import fmt_sidx
                from calibre.gui2 import config
                mi = self.db.get_metadata(id, index_is_id=True)
                series_string = None
                if mi.series:
                    series_string = _('Book %(sidx)s of %(series)s')%dict(
                        sidx=fmt_sidx(mi.series_index,
                        use_roman=config['use_roman_numerals_for_series_number']),
                        series=mi.series)

                cdata = calibre_cover(mi.title, mi.format_field('authors')[-1],
                        series_string=series_string)
                self.db.set_cover(id, cdata)
            elif cover_action == 'fromfmt':
                fmts = self.db.formats(id, index_is_id=True, verify_formats=False)
                if fmts:
                    covers = []
                    for fmt in fmts.split(','):
                        fmtf = self.db.format(id, fmt, index_is_id=True,
                                as_file=True)
                        if fmtf is None: continue
                        cdata, area = get_cover_data(fmtf, fmt)
                        if cdata:
                            covers.append((cdata, area))
                    covers.sort(key=lambda x: x[1])
                    if covers:
                        self.db.set_cover(id, covers[-1][0])
                    covers = []

            if do_remove_format:
                self.db.remove_format(id, remove_format, index_is_id=True,
                        notify=False, commit=True)

            if restore_original:
                formats = self.db.formats(id, index_is_id=True)
                formats = formats.split(',') if formats else []
                originals = [x.upper() for x in formats if
                        x.upper().startswith('ORIGINAL_')]
                for ofmt in originals:
                    fmt = ofmt.replace('ORIGINAL_', '')
                    with SpooledTemporaryFile(SPOOL_SIZE) as stream:
                        self.db.copy_format_to(id, ofmt, stream,
                                index_is_id=True)
                        stream.seek(0)
                        self.db.add_format(id, fmt, stream, index_is_id=True,
                                notify=False)
                    self.db.remove_format(id, ofmt, index_is_id=True,
                            notify=False, commit=True)

        elif self.current_phase == 2:
            # All of these just affect the DB, so we can tolerate a total rollback
            if do_auto_author:
                x = self.db.author_sort_from_book(id, index_is_id=True)
                if x:
                    self.db.set_author_sort(id, x, notify=False, commit=False)

            if aus and do_aus:
                self.db.set_author_sort(id, aus, notify=False, commit=False)

            if rating != -1:
                self.db.set_rating(id, 2*rating, notify=False, commit=False)

            if pub:
                self.db.set_publisher(id, pub, notify=False, commit=False)

            if clear_series:
                self.db.set_series(id, '', notify=False, commit=False)

            if pubdate is not None:
                self.db.set_pubdate(id, pubdate, notify=False, commit=False)

            if adddate is not None:
                self.db.set_timestamp(id, adddate, notify=False, commit=False)

            if do_series:
                if do_series_restart:
                    if self.series_start_value is None:
                        self.series_start_value = series_start_value
                    next = self.series_start_value
                    self.series_start_value += 1
                else:
                    next = self.db.get_next_series_num_for(series)
                self.db.set_series(id, series, notify=False, commit=False)
                if not series:
                    self.db.set_series_index(id, 1.0, notify=False, commit=False)
                elif do_autonumber: # is True if do_series_restart is True
                    self.db.set_series_index(id, next, notify=False, commit=False)
                elif tweaks['series_index_auto_increment'] != 'no_change':
                    self.db.set_series_index(id, 1.0, notify=False, commit=False)

            if do_remove_conv:
                self.db.delete_conversion_options(id, 'PIPE', commit=False)

            if clear_languages:
                self.db.set_languages(id, [], notify=False, commit=False)
            elif languages:
                self.db.set_languages(id, languages, notify=False, commit=False)

        elif self.current_phase == 3:
            # both of these are fast enough to just do them all
            for w in self.cc_widgets:
                w.commit(self.ids)
            if remove_all:
                self.db.remove_all_tags(self.ids)
            self.db.bulk_modify_tags(self.ids, add=add, remove=remove,
                                         notify=False)
            self.current_index = len(self.ids)
        elif self.current_phase == 4:
            self.s_r_func(id)
        # do the next one
        self.current_index += 1
        self.do_one_signal.emit()
Пример #29
0
def ACQUISITION_ENTRY(item, version, db, updated, CFM, CKEYS, prefix):
    FM = db.FIELD_MAP
    title = item[FM['title']]
    if not title:
        title = _('Unknown')
    authors = item[FM['authors']]
    if not authors:
        authors = _('Unknown')
    authors = ' & '.join([i.replace('|', ',') for i in authors.split(',')])
    extra = []
    rating = item[FM['rating']]
    if rating > 0:
        rating = u''.join(repeat(u'\u2605', int(rating / 2.)))
        extra.append(_('RATING: %s<br />') % rating)
    tags = item[FM['tags']]
    if tags:
        extra.append(
            _('TAGS: %s<br />') % xml(
                format_tag_string(
                    tags, ',', ignore_max=True, no_tag_count=True)))
    series = item[FM['series']]
    if series:
        extra.append(
            _('SERIES: %(series)s [%(sidx)s]<br />') %
            dict(series=xml(series),
                 sidx=fmt_sidx(float(item[FM['series_index']]))))
    for key in CKEYS:
        mi = db.get_metadata(item[CFM['id']['rec_index']], index_is_id=True)
        name, val = mi.format_field(key)
        if val:
            datatype = CFM[key]['datatype']
            if datatype == 'text' and CFM[key]['is_multiple']:
                extra.append(
                    '%s: %s<br />' %
                    (xml(name),
                     xml(
                         format_tag_string(
                             val,
                             CFM[key]['is_multiple']['ui_to_list'],
                             ignore_max=True,
                             no_tag_count=True,
                             joinval=CFM[key]['is_multiple']['list_to_ui']))))
            elif datatype == 'comments' or (CFM[key]['datatype'] == 'composite'
                                            and CFM[key]['display'].get(
                                                'contains_html', False)):
                extra.append('%s: %s<br />' %
                             (xml(name), comments_to_html(unicode(val))))
            else:
                extra.append('%s: %s<br />' % (xml(name), xml(unicode(val))))
    comments = item[FM['comments']]
    if comments:
        comments = comments_to_html(comments)
        extra.append(comments)
    if extra:
        extra = html_to_lxml('\n'.join(extra))
    idm = 'calibre' if version == 0 else 'uuid'
    id_ = 'urn:%s:%s' % (idm, item[FM['uuid']])
    ans = E.entry(TITLE(title), E.author(E.name(authors)), ID(id_),
                  UPDATED(updated))
    if len(extra):
        ans.append(E.content(extra, type='xhtml'))
    formats = item[FM['formats']]
    if formats:
        for fmt in formats.split(','):
            fmt = fmt.lower()
            mt = guess_type('a.' + fmt)[0]
            href = prefix + '/get/%s/%s' % (fmt, item[FM['id']])
            if mt:
                link = E.link(type=mt, href=href)
                if version > 0:
                    link.set('rel', "http://opds-spec.org/acquisition")
                ans.append(link)
    ans.append(
        E.link(type='image/jpeg',
               href=prefix + '/get/cover/%s' % item[FM['id']],
               rel="x-stanza-cover-image"
               if version == 0 else "http://opds-spec.org/cover"))
    ans.append(
        E.link(type='image/jpeg',
               href=prefix + '/get/thumb/%s' % item[FM['id']],
               rel="x-stanza-cover-image-thumbnail"
               if version == 0 else "http://opds-spec.org/thumbnail"))

    return ans
Пример #30
0
def mi_to_html(mi,
               field_list=None,
               default_author_link=None,
               use_roman_numbers=True,
               rating_font='Liberation Serif'):
    if field_list is None:
        field_list = get_field_list(mi)
    ans = []
    comment_fields = []
    isdevice = not hasattr(mi, 'id')
    row = u'<td class="title">%s</td><td class="value">%s</td>'
    p = prepare_string_for_xml
    a = partial(prepare_string_for_xml, attribute=True)

    for field in (field for field, display in field_list if display):
        try:
            metadata = mi.metadata_for_field(field)
        except:
            continue
        if not metadata:
            continue
        if field == 'sort':
            field = 'title_sort'
        if metadata['datatype'] == 'bool':
            isnull = mi.get(field) is None
        else:
            isnull = mi.is_null(field)
        if isnull:
            continue
        name = metadata['name']
        if not name:
            name = field
        name += ':'
        if metadata['datatype'] == 'comments' or field == 'comments':
            val = getattr(mi, field)
            if val:
                val = force_unicode(val)
                comment_fields.append(comments_to_html(val))
        elif metadata['datatype'] == 'rating':
            val = getattr(mi, field)
            if val:
                val = val / 2.0
                ans.append(
                    (field,
                     u'<td class="title">%s</td><td class="rating value" '
                     'style=\'font-family:"%s"\'>%s</td>' %
                     (name, rating_font, u'\u2605' * int(val))))
        elif metadata['datatype'] == 'composite' and \
                            metadata['display'].get('contains_html', False):
            val = getattr(mi, field)
            if val:
                val = force_unicode(val)
                ans.append((field, row % (name, comments_to_html(val))))
        elif field == 'path':
            if mi.path:
                path = force_unicode(mi.path, filesystem_encoding)
                scheme = u'devpath' if isdevice else u'path'
                url = prepare_string_for_xml(
                    path if isdevice else unicode(mi.id), True)
                pathstr = _('Click to open')
                extra = ''
                if isdevice:
                    durl = url
                    if durl.startswith('mtp:::'):
                        durl = ':::'.join((durl.split(':::'))[2:])
                    extra = '<br><span style="font-size:smaller">%s</span>' % (
                        prepare_string_for_xml(durl))
                link = u'<a href="%s:%s" title="%s">%s</a>%s' % (
                    scheme, url, prepare_string_for_xml(path,
                                                        True), pathstr, extra)
                ans.append((field, row % (name, link)))
        elif field == 'formats':
            if isdevice:
                continue
            path = ''
            if mi.path:
                h, t = os.path.split(mi.path)
                path = '/'.join((os.path.basename(h), t))
            data = ({
                'fmt': x,
                'path': a(path or ''),
                'fname': a(mi.format_files.get(x, '')),
                'ext': x.lower(),
                'id': mi.id
            } for x in mi.formats)
            fmts = [
                u'<a title="{path}/{fname}.{ext}" href="format:{id}:{fmt}">{fmt}</a>'
                .format(**x) for x in data
            ]
            ans.append((field, row % (name, u', '.join(fmts))))
        elif field == 'identifiers':
            urls = urls_from_identifiers(mi.identifiers)
            links = [
                u'<a href="%s" title="%s:%s">%s</a>' %
                (a(url), a(id_typ), a(id_val), p(namel))
                for namel, id_typ, id_val, url in urls
            ]
            links = u', '.join(links)
            if links:
                ans.append((field, row % (_('Ids') + ':', links)))
        elif field == 'authors' and not isdevice:
            authors = []
            formatter = EvalFormatter()
            for aut in mi.authors:
                link = ''
                if mi.author_link_map[aut]:
                    link = mi.author_link_map[aut]
                elif default_author_link:
                    vals = {'author': aut.replace(' ', '+')}
                    try:
                        vals['author_sort'] = mi.author_sort_map[aut].replace(
                            ' ', '+')
                    except:
                        vals['author_sort'] = aut.replace(' ', '+')
                    link = formatter.safe_format(default_author_link, vals, '',
                                                 vals)
                aut = p(aut)
                if link:
                    authors.append(
                        u'<a calibre-data="authors" title="%s" href="%s">%s</a>'
                        % (a(link), a(link), aut))
                else:
                    authors.append(aut)
            ans.append((field, row % (name, u' & '.join(authors))))
        elif field == 'languages':
            if not mi.languages:
                continue
            names = filter(None, map(calibre_langcode_to_name, mi.languages))
            ans.append((field, row % (name, u', '.join(names))))
        else:
            val = mi.format_field(field)[-1]
            if val is None:
                continue
            val = p(val)
            if metadata['datatype'] == 'series':
                sidx = mi.get(field + '_index')
                if sidx is None:
                    sidx = 1.0
                val = _(
                    'Book %(sidx)s of <span class="series_name">%(series)s</span>'
                ) % dict(sidx=fmt_sidx(sidx, use_roman=use_roman_numbers),
                         series=p(getattr(mi, field)))
            elif metadata['datatype'] == 'datetime':
                aval = getattr(mi, field)
                if is_date_undefined(aval):
                    continue

            ans.append((field, row % (name, val)))

    dc = getattr(mi, 'device_collections', [])
    if dc:
        dc = u', '.join(sorted(dc, key=sort_key))
        ans.append(('device_collections', row % (_('Collections') + ':', dc)))

    def classname(field):
        try:
            dt = mi.metadata_for_field(field)['datatype']
        except:
            dt = 'text'
        return 'datatype_%s' % dt

    ans = [
        u'<tr id="%s" class="%s">%s</tr>' %
        (fieldl.replace('#', '_'), classname(fieldl), html)
        for fieldl, html in ans
    ]
    # print '\n'.join(ans)
    return u'<table class="fields">%s</table>' % (
        u'\n'.join(ans)), comment_fields
Пример #31
0
    def show_result(self, result_or_none):
        self.current_result = r = result_or_none
        if r is None:
            self.set_controls_visibility(False)
            return
        self.set_controls_visibility(True)
        db = current_db()
        book_id = r['book_id']
        title, authors = db.field_for('title', book_id), db.field_for(
            'authors', book_id)
        authors = authors_to_string(authors)
        series, sidx = db.field_for('series', book_id), db.field_for(
            'series_index', book_id)
        series_text = ''
        if series:
            use_roman_numbers = config['use_roman_numerals_for_series_number']
            series_text = '{0} of {1}'.format(
                fmt_sidx(sidx, use_roman=use_roman_numbers), series)
        annot = r['annotation']
        atype = annotation_title(annot['type'], singular=True)
        book_format = r['format']
        annot_text = ''
        a = prepare_string_for_xml

        paras = []

        def p(text, tag='p'):
            paras.append('<{0}>{1}</{0}>'.format(tag, a(text)))

        if annot['type'] == 'bookmark':
            p(annot['title'])
        elif annot['type'] == 'highlight':
            p(annot['highlighted_text'])
            notes = annot.get('notes')
            if notes:
                paras.append(
                    '<h4>{} (<a title="{}" href="calibre://edit_result">{}</a>)</h4>'
                    .format(_('Notes'), _('Edit the notes of this highlight'),
                            _('Edit')))
                paras.extend(render_notes(notes))
            else:
                paras.append(
                    '<p><a title="{}" href="calibre://edit_result">{}</a></p>'.
                    format(_('Add notes to this highlight'), _('Add notes')))

        annot_text += '\n'.join(paras)
        date = QDateTime.fromString(annot['timestamp'],
                                    Qt.ISODate).toLocalTime().toString(
                                        Qt.SystemLocaleShortDate)

        text = '''
        <style>a {{ text-decoration: none }}</style>
        <h2 style="text-align: center">{title} [{book_format}]</h2>
        <div style="text-align: center">{authors}</div>
        <div style="text-align: center">{series}</div>
        <div>&nbsp;</div>
        <div>&nbsp;</div>
        <div>{dt}: {date}</div>
        <div>{ut}: {user}</div>
        <div>
            <a href="calibre://open_result" title="{ovtt}" style="margin-right: 20px">{ov}</a>
            <span>\xa0\xa0\xa0</span>
            <a title="{sictt}" href="calibre://show_in_library">{sic}</a>
        </div>
        <h2 style="text-align: left">{atype}</h2>
        {text}
        '''.format(
            title=a(title),
            authors=a(authors),
            series=a(series_text),
            book_format=a(book_format),
            atype=a(atype),
            text=annot_text,
            dt=_('Date'),
            date=a(date),
            ut=a(_('User')),
            user=a(friendly_username(r['user_type'], r['user'])),
            ov=a(_('Open in viewer')),
            sic=a(_('Show in calibre')),
            ovtt=a(
                _('Open the book at this annotation in the calibre viewer')),
            sictt=(_('Show this book in the main calibre book list')),
        )
        self.text_browser.setHtml(text)
Пример #32
0
def mi_to_html(mi, field_list=None, default_author_link=None, use_roman_numbers=True, rating_font='Liberation Serif'):
    if field_list is None:
        field_list = get_field_list(mi)
    ans = []
    comment_fields = []
    isdevice = not hasattr(mi, 'id')
    row = u'<td class="title">%s</td><td class="value">%s</td>'
    p = prepare_string_for_xml
    a = partial(prepare_string_for_xml, attribute=True)

    for field in (field for field, display in field_list if display):
        try:
            metadata = mi.metadata_for_field(field)
        except:
            continue
        if not metadata:
            continue
        if field == 'sort':
            field = 'title_sort'
        if metadata['datatype'] == 'bool':
            isnull = mi.get(field) is None
        else:
            isnull = mi.is_null(field)
        if isnull:
            continue
        name = metadata['name']
        if not name:
            name = field
        name += ':'
        if metadata['datatype'] == 'comments' or field == 'comments':
            val = getattr(mi, field)
            if val:
                val = force_unicode(val)
                comment_fields.append(comments_to_html(val))
        elif metadata['datatype'] == 'rating':
            val = getattr(mi, field)
            if val:
                val = val/2.0
                ans.append((field,
                    u'<td class="title">%s</td><td class="rating value" '
                    'style=\'font-family:"%s"\'>%s</td>'%(
                        name, rating_font, u'\u2605'*int(val))))
        elif metadata['datatype'] == 'composite' and \
                            metadata['display'].get('contains_html', False):
            val = getattr(mi, field)
            if val:
                val = force_unicode(val)
                ans.append((field,
                    row % (name, comments_to_html(val))))
        elif field == 'path':
            if mi.path:
                path = force_unicode(mi.path, filesystem_encoding)
                scheme = u'devpath' if isdevice else u'path'
                url = prepare_string_for_xml(path if isdevice else
                        unicode(mi.id), True)
                pathstr = _('Click to open')
                extra = ''
                if isdevice:
                    durl = url
                    if durl.startswith('mtp:::'):
                        durl = ':::'.join((durl.split(':::'))[2:])
                    extra = '<br><span style="font-size:smaller">%s</span>'%(
                            prepare_string_for_xml(durl))
                link = u'<a href="%s:%s" title="%s">%s</a>%s' % (scheme, url,
                        prepare_string_for_xml(path, True), pathstr, extra)
                ans.append((field, row % (name, link)))
        elif field == 'formats':
            if isdevice:
                continue
            path = ''
            if mi.path:
                h, t = os.path.split(mi.path)
                path = '/'.join((os.path.basename(h), t))
            data = ({
                'fmt':x, 'path':a(path or ''), 'fname':a(mi.format_files.get(x, '')),
                'ext':x.lower(), 'id':mi.id
            } for x in mi.formats)
            fmts = [u'<a title="{path}/{fname}.{ext}" href="format:{id}:{fmt}">{fmt}</a>'.format(**x) for x in data]
            ans.append((field, row % (name, u', '.join(fmts))))
        elif field == 'identifiers':
            urls = urls_from_identifiers(mi.identifiers)
            links = [u'<a href="%s" title="%s:%s">%s</a>' % (a(url), a(id_typ), a(id_val), p(name))
                    for name, id_typ, id_val, url in urls]
            links = u', '.join(links)
            if links:
                ans.append((field, row % (_('Ids')+':', links)))
        elif field == 'authors' and not isdevice:
            authors = []
            formatter = EvalFormatter()
            for aut in mi.authors:
                link = ''
                if mi.author_link_map[aut]:
                    link = mi.author_link_map[aut]
                elif default_author_link:
                    vals = {'author': aut.replace(' ', '+')}
                    try:
                        vals['author_sort'] =  mi.author_sort_map[aut].replace(' ', '+')
                    except:
                        vals['author_sort'] = aut.replace(' ', '+')
                    link = formatter.safe_format(
                            default_author_link, vals, '', vals)
                aut = p(aut)
                if link:
                    authors.append(u'<a calibre-data="authors" href="%s">%s</a>'%(a(link), aut))
                else:
                    authors.append(aut)
            ans.append((field, row % (name, u' & '.join(authors))))
        elif field == 'languages':
            if not mi.languages:
                continue
            names = filter(None, map(calibre_langcode_to_name, mi.languages))
            ans.append((field, row % (name, u', '.join(names))))
        else:
            val = mi.format_field(field)[-1]
            if val is None:
                continue
            val = p(val)
            if metadata['datatype'] == 'series':
                sidx = mi.get(field+'_index')
                if sidx is None:
                    sidx = 1.0
                val = _('Book %(sidx)s of <span class="series_name">%(series)s</span>')%dict(
                        sidx=fmt_sidx(sidx, use_roman=use_roman_numbers),
                        series=p(getattr(mi, field)))
            elif metadata['datatype'] == 'datetime':
                aval = getattr(mi, field)
                if is_date_undefined(aval):
                    continue

            ans.append((field, row % (name, val)))

    dc = getattr(mi, 'device_collections', [])
    if dc:
        dc = u', '.join(sorted(dc, key=sort_key))
        ans.append(('device_collections',
            row % (_('Collections')+':', dc)))

    def classname(field):
        try:
            dt = mi.metadata_for_field(field)['datatype']
        except:
            dt = 'text'
        return 'datatype_%s'%dt

    ans = [u'<tr id="%s" class="%s">%s</tr>'%(field.replace('#', '_'),
        classname(field), html) for field, html in ans]
    # print '\n'.join(ans)
    return u'<table class="fields">%s</table>'%(u'\n'.join(ans)), comment_fields
Пример #33
0
    def do_all(self):
        cache = self.db.new_api
        args = self.args

        # Title and authors
        if args.do_swap_ta:
            title_map = cache.all_field_for('title', self.ids)
            authors_map = cache.all_field_for('authors', self.ids)
            def new_title(authors):
                ans = authors_to_string(authors)
                return titlecase(ans) if args.do_title_case else ans
            new_title_map = {bid:new_title(authors) for bid, authors in authors_map.iteritems()}
            new_authors_map = {bid:string_to_authors(title) for bid, title in title_map.iteritems()}
            cache.set_field('authors', new_authors_map)
            cache.set_field('title', new_title_map)

        if args.do_title_case and not args.do_swap_ta:
            title_map = cache.all_field_for('title', self.ids)
            cache.set_field('title', {bid:titlecase(title) for bid, title in title_map.iteritems()})

        if args.do_title_sort:
            lang_map = cache.all_field_for('languages', self.ids)
            title_map = cache.all_field_for('title', self.ids)
            def get_sort(book_id):
                if args.languages:
                    lang = args.languages[0]
                else:
                    try:
                        lang = lang_map[book_id][0]
                    except (KeyError, IndexError, TypeError, AttributeError):
                        lang = 'eng'
                return title_sort(title_map[book_id], lang=lang)
            cache.set_field('sort', {bid:get_sort(bid) for bid in self.ids})

        if args.au:
            authors = string_to_authors(args.au)
            cache.set_field('authors', {bid:authors for bid in self.ids})

        if args.do_auto_author:
            aus_map = cache.author_sort_strings_for_books(self.ids)
            cache.set_field('author_sort', {book_id:' & '.join(aus_map[book_id]) for book_id in aus_map})

        if args.aus and args.do_aus:
            cache.set_field('author_sort', {bid:args.aus for bid in self.ids})

        # Covers
        if args.cover_action == 'remove':
            cache.set_cover({bid:None for bid in self.ids})
        elif args.cover_action == 'generate':
            from calibre.ebooks import calibre_cover
            from calibre.ebooks.metadata import fmt_sidx
            from calibre.gui2 import config
            for book_id in self.ids:
                mi = self.db.get_metadata(book_id, index_is_id=True)
                series_string = None
                if mi.series:
                    series_string = _('Book %(sidx)s of %(series)s')%dict(
                        sidx=fmt_sidx(mi.series_index,
                        use_roman=config['use_roman_numerals_for_series_number']),
                        series=mi.series)

                cdata = calibre_cover(mi.title, mi.format_field('authors')[-1],
                        series_string=series_string)
                cache.set_cover({book_id:cdata})
        elif args.cover_action == 'fromfmt':
            for book_id in self.ids:
                fmts = cache.formats(book_id, verify_formats=False)
                if fmts:
                    covers = []
                    for fmt in fmts:
                        fmtf = cache.format(book_id, fmt, as_file=True)
                        if fmtf is None:
                            continue
                        cdata, area = get_cover_data(fmtf, fmt)
                        if cdata:
                            covers.append((cdata, area))
                    covers.sort(key=lambda x: x[1])
                    if covers:
                        cache.set_cover({book_id:covers[-1][0]})
        elif args.cover_action == 'trim':
            from calibre.utils.magick import Image
            for book_id in self.ids:
                cdata = cache.cover(book_id)
                if cdata:
                    im = Image()
                    im.load(cdata)
                    im.trim(tweaks['cover_trim_fuzz_value'])
                    cdata = im.export('jpg')
                    cache.set_cover({book_id:cdata})
        elif args.cover_action == 'clone':
            cdata = None
            for book_id in self.ids:
                cdata = cache.cover(book_id)
                if cdata:
                    break
            if cdata:
                cache.set_cover({bid:cdata for bid in self.ids if bid != book_id})

        # Formats
        if args.do_remove_format:
            cache.remove_formats({bid:(args.remove_format,) for bid in self.ids})

        if args.restore_original:
            for book_id in self.ids:
                formats = cache.formats(book_id)
                originals = tuple(x.upper() for x in formats if x.upper().startswith('ORIGINAL_'))
                for ofmt in originals:
                    cache.restore_original_format(book_id, ofmt)

        # Various fields
        if args.rating != -1:
            cache.set_field('rating', {bid:args.rating*2 for bid in self.ids})

        if args.clear_pub:
            cache.set_field('publisher', {bid:'' for bid in self.ids})

        if args.pub:
            cache.set_field('publisher', {bid:args.pub for bid in self.ids})

        if args.clear_series:
            cache.set_field('series', {bid:'' for bid in self.ids})

        if args.pubdate is not None:
            cache.set_field('pubdate', {bid:args.pubdate for bid in self.ids})

        if args.adddate is not None:
            cache.set_field('timestamp', {bid:args.adddate for bid in self.ids})

        if args.do_series:
            sval = args.series_start_value if args.do_series_restart else cache.get_next_series_num_for(args.series, current_indices=True)
            cache.set_field('series', {bid:args.series for bid in self.ids})
            if not args.series:
                cache.set_field('series_index', {bid:1.0 for bid in self.ids})
            else:
                def next_series_num(bid, i):
                    if args.do_series_restart:
                        return sval + i
                    next_num = _get_next_series_num_for_list(sorted(sval.itervalues()), unwrap=False)
                    sval[bid] = next_num
                    return next_num

                smap = {bid:next_series_num(bid, i) for i, bid in enumerate(self.ids)}
                if args.do_autonumber:
                    cache.set_field('series_index', smap)
                elif tweaks['series_index_auto_increment'] != 'no_change':
                    cache.set_field('series_index', {bid:1.0 for bid in self.ids})

        if args.comments is not null:
            cache.set_field('comments', {bid:args.comments for bid in self.ids})

        if args.do_remove_conv:
            cache.delete_conversion_options(self.ids)

        if args.clear_languages:
            cache.set_field('languages', {bid:() for bid in self.ids})
        elif args.languages:
            cache.set_field('languages', {bid:args.languages for bid in self.ids})

        if args.remove_all:
            cache.set_field('tags', {bid:() for bid in self.ids})
        if args.add or args.remove:
            self.db.bulk_modify_tags(self.ids, add=args.add, remove=args.remove)

        if self.do_sr:
            for book_id in self.ids:
                self.s_r_func(book_id)
            if self.sr_calls:
                for field, book_id_val_map in self.sr_calls.iteritems():
                    self.refresh_books.update(self.db.new_api.set_field(field, book_id_val_map))
Пример #34
0
def render_data(mi, use_roman_numbers=True, all_fields=False):
    ans = []
    isdevice = not hasattr(mi, 'id')
    fm = getattr(mi, 'field_metadata', field_metadata)

    for field, display in get_field_list(fm):
        metadata = fm.get(field, None)
        if field == 'sort':
            field = 'title_sort'
        if all_fields:
            display = True
        if metadata['datatype'] == 'bool':
            isnull = mi.get(field) is None
        else:
            isnull = mi.is_null(field)
        if (not display or not metadata or isnull or field == 'comments'):
            continue
        name = metadata['name']
        if not name:
            name = field
        name += ':'
        if metadata['datatype'] == 'comments':
            val = getattr(mi, field)
            if val:
                val = force_unicode(val)
                ans.append(
                    (field, u'<td class="comments" colspan="2">%s</td>' %
                     comments_to_html(val)))
        elif metadata['datatype'] == 'rating':
            val = getattr(mi, field)
            if val:
                val = val / 2.0
                ans.append(
                    (field, u'<td class="title">%s</td><td class="rating" '
                     'style=\'font-family:"%s"\'>%s</td>' %
                     (name, rating_font(), u'\u2605' * int(val))))
        elif metadata['datatype'] == 'composite' and \
                            metadata['display'].get('contains_html', False):
            val = getattr(mi, field)
            if val:
                val = force_unicode(val)
                ans.append((field, u'<td class="title">%s</td><td>%s</td>' %
                            (name, comments_to_html(val))))
        elif field == 'path':
            if mi.path:
                path = force_unicode(mi.path, filesystem_encoding)
                scheme = u'devpath' if isdevice else u'path'
                url = prepare_string_for_xml(
                    path if isdevice else unicode(mi.id), True)
                pathstr = _('Click to open')
                extra = ''
                if isdevice:
                    durl = url
                    if durl.startswith('mtp:::'):
                        durl = ':::'.join((durl.split(':::'))[2:])
                    extra = '<br><span style="font-size:smaller">%s</span>' % (
                        prepare_string_for_xml(durl))
                link = u'<a href="%s:%s" title="%s">%s</a>%s' % (
                    scheme, url, prepare_string_for_xml(path,
                                                        True), pathstr, extra)
                ans.append(
                    (field,
                     u'<td class="title">%s</td><td>%s</td>' % (name, link)))
        elif field == 'formats':
            if isdevice: continue
            fmts = [
                u'<a href="format:%s:%s">%s</a>' % (mi.id, x, x)
                for x in mi.formats
            ]
            ans.append((field, u'<td class="title">%s</td><td>%s</td>' %
                        (name, u', '.join(fmts))))
        elif field == 'identifiers':
            urls = urls_from_identifiers(mi.identifiers)
            links = [
                u'<a href="%s" title="%s:%s">%s</a>' %
                (url, id_typ, id_val, name)
                for name, id_typ, id_val, url in urls
            ]
            links = u', '.join(links)
            if links:
                ans.append((field, u'<td class="title">%s</td><td>%s</td>' %
                            (_('Ids') + ':', links)))
        elif field == 'authors' and not isdevice:
            authors = []
            formatter = EvalFormatter()
            for aut in mi.authors:
                link = ''
                if mi.author_link_map[aut]:
                    link = mi.author_link_map[aut]
                elif gprefs.get('default_author_link'):
                    vals = {'author': aut.replace(' ', '+')}
                    try:
                        vals['author_sort'] = mi.author_sort_map[aut].replace(
                            ' ', '+')
                    except:
                        vals['author_sort'] = aut.replace(' ', '+')
                    link = formatter.safe_format(
                        gprefs.get('default_author_link'), vals, '', vals)
                if link:
                    link = prepare_string_for_xml(link)
                    authors.append(u'<a href="%s">%s</a>' % (link, aut))
                else:
                    authors.append(aut)
            ans.append((field, u'<td class="title">%s</td><td>%s</td>' %
                        (name, u' & '.join(authors))))
        elif field == 'languages':
            if not mi.languages:
                continue
            names = filter(None, map(calibre_langcode_to_name, mi.languages))
            ans.append((field, u'<td class="title">%s</td><td>%s</td>' %
                        (name, u', '.join(names))))
        else:
            val = mi.format_field(field)[-1]
            if val is None:
                continue
            val = prepare_string_for_xml(val)
            if metadata['datatype'] == 'series':
                sidx = mi.get(field + '_index')
                if sidx is None:
                    sidx = 1.0
                val = _(
                    'Book %(sidx)s of <span class="series_name">%(series)s</span>'
                ) % dict(sidx=fmt_sidx(sidx, use_roman=use_roman_numbers),
                         series=prepare_string_for_xml(getattr(mi, field)))
            elif metadata['datatype'] == 'datetime':
                aval = getattr(mi, field)
                if is_date_undefined(aval):
                    continue

            ans.append(
                (field, u'<td class="title">%s</td><td>%s</td>' % (name, val)))

    dc = getattr(mi, 'device_collections', [])
    if dc:
        dc = u', '.join(sorted(dc, key=sort_key))
        ans.append(
            ('device_collections', u'<td class="title">%s</td><td>%s</td>' %
             (_('Collections') + ':', dc)))

    def classname(field):
        try:
            dt = fm[field]['datatype']
        except:
            dt = 'text'
        return 'datatype_%s' % dt

    ans = [
        u'<tr id="%s" class="%s">%s</tr>' %
        (field.replace('#', '_'), classname(field), html)
        for field, html in ans
    ]
    # print '\n'.join(ans)
    return u'<table class="fields">%s</table>' % (u'\n'.join(ans))
Пример #35
0
def mi_to_html(mi,
               field_list=None,
               default_author_link=None,
               use_roman_numbers=True,
               rating_font='Liberation Serif',
               rtl=False):
    if field_list is None:
        field_list = get_field_list(mi)
    ans = []
    comment_fields = []
    isdevice = not hasattr(mi, 'id')
    row = u'<td class="title">%s</td><td class="value">%s</td>'
    p = prepare_string_for_xml
    a = partial(prepare_string_for_xml, attribute=True)
    book_id = getattr(mi, 'id', 0)

    for field in (field for field, display in field_list if display):
        try:
            metadata = mi.metadata_for_field(field)
        except:
            continue
        if not metadata:
            continue
        if field == 'sort':
            field = 'title_sort'
        if metadata['is_custom'] and metadata['datatype'] in {
                'bool', 'int', 'float'
        }:
            isnull = mi.get(field) is None
        else:
            isnull = mi.is_null(field)
        if isnull:
            continue
        name = metadata['name']
        if not name:
            name = field
        name += ':'
        disp = metadata['display']
        if metadata['datatype'] == 'comments' or field == 'comments':
            val = getattr(mi, field)
            if val:
                ctype = disp.get('interpret_as') or 'html'
                val = force_unicode(val)
                if ctype == 'long-text':
                    val = '<pre style="white-space:pre-wrap">%s</pre>' % p(val)
                elif ctype == 'short-text':
                    val = '<span>%s</span>' % p(val)
                elif ctype == 'markdown':
                    val = markdown(val)
                else:
                    val = comments_to_html(val)
                if disp.get('heading_position', 'hide') == 'side':
                    ans.append((field, row % (name, val)))
                else:
                    if disp.get('heading_position', 'hide') == 'above':
                        val = '<h3 class="comments-heading">%s</h3>%s' % (
                            p(name), val)
                    comment_fields.append(
                        '<div id="%s" class="comments">%s</div>' %
                        (field.replace('#', '_'), val))
        elif metadata['datatype'] == 'rating':
            val = getattr(mi, field)
            if val:
                star_string = rating_to_stars(
                    val, disp.get('allow_half_stars', False))
                ans.append(
                    (field,
                     u'<td class="title">%s</td><td class="rating value" '
                     'style=\'font-family:"%s"\'>%s</td>' %
                     (name, rating_font, star_string)))
        elif metadata['datatype'] == 'composite':
            val = getattr(mi, field)
            if val:
                val = force_unicode(val)
                if disp.get('contains_html', False):
                    ans.append((field, row % (name, comments_to_html(val))))
                else:
                    if not metadata['is_multiple']:
                        val = '<a href="%s" title="%s">%s</a>' % (
                            search_action(field, val),
                            _('Click to see books with {0}: {1}').format(
                                metadata['name'], a(val)), p(val))
                    else:
                        all_vals = [
                            v.strip() for v in val.split(
                                metadata['is_multiple']['list_to_ui'])
                            if v.strip()
                        ]
                        links = [
                            '<a href="%s" title="%s">%s</a>' %
                            (search_action(field, x),
                             _('Click to see books with {0}: {1}').format(
                                 metadata['name'], a(x)), p(x))
                            for x in all_vals
                        ]
                        val = metadata['is_multiple']['list_to_ui'].join(links)
                    ans.append((field, row % (name, val)))
        elif field == 'path':
            if mi.path:
                path = force_unicode(mi.path, filesystem_encoding)
                scheme = u'devpath' if isdevice else u'path'
                loc = path if isdevice else book_id
                pathstr = _('Click to open')
                extra = ''
                if isdevice:
                    durl = path
                    if durl.startswith('mtp:::'):
                        durl = ':::'.join((durl.split(':::'))[2:])
                    extra = '<br><span style="font-size:smaller">%s</span>' % (
                        prepare_string_for_xml(durl))
                link = '<a href="%s" title="%s">%s</a>%s' % (action(
                    scheme, loc=loc), prepare_string_for_xml(
                        path, True), pathstr, extra)
                ans.append((field, row % (name, link)))
        elif field == 'formats':
            if isdevice:
                continue
            path = mi.path or ''
            bpath = ''
            if path:
                h, t = os.path.split(path)
                bpath = os.sep.join((os.path.basename(h), t))
            data = ({
                'fmt':
                x,
                'path':
                a(path or ''),
                'fname':
                a(mi.format_files.get(x, '')),
                'ext':
                x.lower(),
                'id':
                book_id,
                'bpath':
                bpath,
                'sep':
                os.sep,
                'action':
                action('format',
                       book_id=book_id,
                       fmt=x,
                       path=path or '',
                       fname=mi.format_files.get(x, ''))
            } for x in mi.formats)
            fmts = [
                '<a title="{bpath}{sep}{fname}.{ext}" href="{action}">{fmt}</a>'
                .format(**x) for x in data
            ]
            ans.append((field, row % (name, ', '.join(fmts))))
        elif field == 'identifiers':
            urls = urls_from_identifiers(mi.identifiers)
            links = [
                '<a href="%s" title="%s:%s">%s</a>' %
                (action('identifier',
                        url=url,
                        name=namel,
                        id_type=id_typ,
                        value=id_val,
                        field='identifiers',
                        book_id=book_id), a(id_typ), a(id_val), p(namel))
                for namel, id_typ, id_val, url in urls
            ]
            links = u', '.join(links)
            if links:
                ans.append((field, row % (_('Ids') + ':', links)))
        elif field == 'authors':
            authors = []
            formatter = EvalFormatter()
            for aut in mi.authors:
                link = ''
                if mi.author_link_map.get(aut):
                    link = lt = mi.author_link_map[aut]
                elif default_author_link:
                    if isdevice and default_author_link == 'search-calibre':
                        default_author_link = DEFAULT_AUTHOR_LINK
                    if default_author_link.startswith('search-'):
                        which_src = default_author_link.partition('-')[2]
                        link, lt = author_search_href(which_src,
                                                      title=mi.title,
                                                      author=aut)
                    else:
                        vals = {
                            'author': qquote(aut),
                            'title': qquote(mi.title)
                        }
                        try:
                            vals['author_sort'] = qquote(
                                mi.author_sort_map[aut])
                        except KeyError:
                            vals['author_sort'] = qquote(aut)
                        link = lt = formatter.safe_format(
                            default_author_link, vals, '', vals)
                aut = p(aut)
                if link:
                    authors.append(
                        '<a title="%s" href="%s">%s</a>' %
                        (a(lt), action('author', url=link, name=aut,
                                       title=lt), aut))
                else:
                    authors.append(aut)
            ans.append((field, row % (name, ' & '.join(authors))))
        elif field == 'languages':
            if not mi.languages:
                continue
            names = filter(None, map(calibre_langcode_to_name, mi.languages))
            names = [
                '<a href="%s" title="%s">%s</a>' %
                (search_action('languages', n),
                 _('Search calibre for books with the language: {}').format(n),
                 n) for n in names
            ]
            ans.append((field, row % (name, u', '.join(names))))
        elif field == 'publisher':
            if not mi.publisher:
                continue
            val = '<a href="%s" title="%s">%s</a>' % (search_action_with_data(
                'publisher', mi.publisher,
                book_id), _('Click to see books with {0}: {1}').format(
                    metadata['name'], a(mi.publisher)), p(mi.publisher))
            ans.append((field, row % (name, val)))
        elif field == 'title':
            # otherwise title gets metadata['datatype'] == 'text'
            # treatment below with a click to search link (which isn't
            # too bad), and a right-click 'Delete' option to delete
            # the title (which is bad).
            val = mi.format_field(field)[-1]
            ans.append((field, row % (name, val)))
        else:
            val = mi.format_field(field)[-1]
            if val is None:
                continue
            val = p(val)
            if metadata['datatype'] == 'series':
                sidx = mi.get(field + '_index')
                if sidx is None:
                    sidx = 1.0
                try:
                    st = metadata['search_terms'][0]
                except Exception:
                    st = field
                series = getattr(mi, field)
                val = _('%(sidx)s of <a href="%(href)s" title="%(tt)s">'
                        '<span class="%(cls)s">%(series)s</span></a>') % dict(
                            sidx=fmt_sidx(sidx, use_roman=use_roman_numbers),
                            cls="series_name",
                            series=p(series),
                            href=search_action_with_data(
                                st, series, book_id, field),
                            tt=p(_('Click to see books in this series')))
            elif metadata['datatype'] == 'datetime':
                aval = getattr(mi, field)
                if is_date_undefined(aval):
                    continue
            elif metadata['datatype'] == 'text' and metadata['is_multiple']:
                try:
                    st = metadata['search_terms'][0]
                except Exception:
                    st = field
                all_vals = mi.get(field)
                if not metadata.get('display', {}).get('is_names', False):
                    all_vals = sorted(all_vals, key=sort_key)
                links = [
                    '<a href="%s" title="%s">%s</a>' %
                    (search_action_with_data(st, x, book_id, field),
                     _('Click to see books with {0}: {1}').format(
                         metadata['name'], a(x)), p(x)) for x in all_vals
                ]
                val = metadata['is_multiple']['list_to_ui'].join(links)
            elif metadata['datatype'] == 'text' or metadata[
                    'datatype'] == 'enumeration':
                # text/is_multiple handled above so no need to add the test to the if
                try:
                    st = metadata['search_terms'][0]
                except Exception:
                    st = field
                val = '<a href="%s" title="%s">%s</a>' % (
                    search_action_with_data(st, val, book_id, field),
                    a(
                        _('Click to see books with {0}: {1}').format(
                            metadata['name'], val)), p(val))

            ans.append((field, row % (name, val)))

    dc = getattr(mi, 'device_collections', [])
    if dc:
        dc = u', '.join(sorted(dc, key=sort_key))
        ans.append(('device_collections', row % (_('Collections') + ':', dc)))

    def classname(field):
        try:
            dt = mi.metadata_for_field(field)['datatype']
        except:
            dt = 'text'
        return 'datatype_%s' % dt

    ans = [
        u'<tr id="%s" class="%s">%s</tr>' %
        (fieldl.replace('#', '_'), classname(fieldl), html)
        for fieldl, html in ans
    ]
    # print '\n'.join(ans)
    direction = 'rtl' if rtl else 'ltr'
    margin = 'left' if rtl else 'right'
    return u'<style>table.fields td { vertical-align:top}</style>' + \
           u'<table class="fields" style="direction: %s; margin-%s:auto">%s</table>'%(
               direction, margin, u'\n'.join(ans)), comment_fields
Пример #36
0
    def mobile(self,
               start='1',
               num='25',
               sort='date',
               search='',
               _=None,
               order='descending'):
        '''
        Serves metadata from the calibre database as XML.

        :param sort: Sort results by ``sort``. Can be one of `title,author,rating`.
        :param search: Filter results by ``search`` query. See :class:`SearchQueryParser` for query syntax
        :param start,num: Return the slice `[start:start+num]` of the sorted and filtered results
        :param _: Firefox seems to sometimes send this when using XMLHttpRequest with no caching
        '''
        try:
            start = int(start)
        except ValueError:
            raise cherrypy.HTTPError(400,
                                     'start: %s is not an integer' % start)
        try:
            num = int(num)
        except ValueError:
            raise cherrypy.HTTPError(400, 'num: %s is not an integer' % num)
        if not search:
            search = ''
        if isbytestring(search):
            search = search.decode('UTF-8')
        ids = self.db.search_getting_ids(search.strip(),
                                         self.search_restriction)
        FM = self.db.FIELD_MAP
        items = [r for r in iter(self.db) if r[FM['id']] in ids]
        if sort is not None:
            self.sort(items, sort, (order.lower().strip() == 'ascending'))

        CFM = self.db.field_metadata
        CKEYS = [
            key for key in sorted(custom_fields_to_display(self.db),
                                  key=lambda x: sort_key(CFM[x]['name']))
        ]
        # This method uses its own book dict, not the Metadata dict. The loop
        # below could be changed to use db.get_metadata instead of reading
        # info directly from the record made by the view, but it doesn't seem
        # worth it at the moment.
        books = []
        for record in items[(start - 1):(start - 1) + num]:
            book = {
                'formats': record[FM['formats']],
                'size': record[FM['size']]
            }
            if not book['formats']:
                book['formats'] = ''
            if not book['size']:
                book['size'] = 0
            book['size'] = human_readable(book['size'])

            aus = record[FM['authors']] if record[
                FM['authors']] else __builtin__._('Unknown')
            aut_is = CFM['authors']['is_multiple']
            authors = aut_is['list_to_ui'].join(
                [i.replace('|', ',') for i in aus.split(',')])
            book['authors'] = authors
            book['series_index'] = fmt_sidx(float(record[FM['series_index']]))
            book['series'] = record[FM['series']]
            book['tags'] = format_tag_string(record[FM['tags']],
                                             ',',
                                             no_tag_count=True)
            book['title'] = record[FM['title']]
            for x in ('timestamp', 'pubdate'):
                book[x] = strftime('%d %b, %Y', record[FM[x]])
            book['id'] = record[FM['id']]
            books.append(book)
            for key in CKEYS:

                def concat(name, val):
                    return '%s:#:%s' % (name, unicode(val))

                mi = self.db.get_metadata(record[CFM['id']['rec_index']],
                                          index_is_id=True)
                name, val = mi.format_field(key)
                if not val:
                    continue
                datatype = CFM[key]['datatype']
                if datatype in ['comments']:
                    continue
                if datatype == 'text' and CFM[key]['is_multiple']:
                    book[key] = concat(
                        name,
                        format_tag_string(
                            val,
                            CFM[key]['is_multiple']['ui_to_list'],
                            no_tag_count=True,
                            joinval=CFM[key]['is_multiple']['list_to_ui']))
                else:
                    book[key] = concat(name, val)

        updated = self.db.last_modified()

        cherrypy.response.headers['Content-Type'] = 'text/html; charset=utf-8'
        cherrypy.response.headers['Last-Modified'] = self.last_modified(
            updated)

        url_base = "/mobile?search=" + search + ";order=" + order + ";sort=" + sort + ";num=" + str(
            num)

        raw = html.tostring(build_index(books, num, search, sort, order, start,
                                        len(ids), url_base, CKEYS,
                                        self.opts.url_prefix),
                            encoding='utf-8',
                            pretty_print=True)
        # tostring's include_meta_content_type is broken
        raw = raw.replace(
            '<head>', '<head>\n'
            '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
        )
        return raw
Пример #37
0
    def xml(self, start='0', num='50', sort=None, search=None,
                _=None, order='ascending'):
        '''
        Serves metadata from the calibre database as XML.

        :param sort: Sort results by ``sort``. Can be one of `title,author,rating`.
        :param search: Filter results by ``search`` query. See :class:`SearchQueryParser` for query syntax
        :param start,num: Return the slice `[start:start+num]` of the sorted and filtered results
        :param _: Firefox seems to sometimes send this when using XMLHttpRequest with no caching
        '''
        try:
            start = int(start)
        except ValueError:
            raise cherrypy.HTTPError(400, 'start: %s is not an integer'%start)
        try:
            num = int(num)
        except ValueError:
            raise cherrypy.HTTPError(400, 'num: %s is not an integer'%num)

        order = order.lower().strip() == 'ascending'

        if not search:
            search = ''
        if isbytestring(search):
            search = search.decode('UTF-8')

        ids = self.search_for_books(search)

        FM = self.db.FIELD_MAP

        items = [r for r in iter(self.db) if r[FM['id']] in ids]
        if sort is not None:
            self.sort(items, sort, order)

        books = []

        def serialize(x):
            if isinstance(x, unicode):
                return x
            if isbytestring(x):
                return x.decode(preferred_encoding, 'replace')
            return unicode(x)

        # This method uses its own book dict, not the Metadata dict. The loop
        # below could be changed to use db.get_metadata instead of reading
        # info directly from the record made by the view, but it doesn't seem
        # worth it at the moment.
        for record in items[start:start+num]:
            kwargs = {}
            aus = record[FM['authors']] if record[FM['authors']] else __builtin__._('Unknown')
            authors = '|'.join([i.replace('|', ',') for i in aus.split(',')])
            kwargs['authors'] = authors

            kwargs['series_index'] = \
                fmt_sidx(float(record[FM['series_index']]))

            for x in ('timestamp', 'pubdate'):
                kwargs[x] = strftime('%Y/%m/%d %H:%M:%S', record[FM[x]])

            for x in ('id', 'title', 'sort', 'author_sort', 'rating', 'size'):
                kwargs[x] = serialize(record[FM[x]])

            for x in ('formats', 'series', 'tags', 'publisher',
                    'comments', 'identifiers'):
                y = record[FM[x]]
                if x == 'tags':
                    y = format_tag_string(y, ',', ignore_max=True)
                kwargs[x] = serialize(y) if y else ''

            isbn = self.db.isbn(record[FM['id']], index_is_id=True)
            kwargs['isbn'] = serialize(isbn if isbn else '')

            kwargs['safe_title'] = ascii_filename(kwargs['title'])

            c = kwargs.pop('comments')

            CFM = self.db.field_metadata
            CKEYS = [key for key in sorted(custom_fields_to_display(self.db),
                                           key=lambda x: sort_key(CFM[x]['name']))]
            custcols = []
            for key in CKEYS:
                def concat(name, val):
                    return '%s:#:%s'%(name, unicode(val))
                mi = self.db.get_metadata(record[CFM['id']['rec_index']], index_is_id=True)
                name, val = mi.format_field(key)
                if not val:
                    continue
                datatype = CFM[key]['datatype']
                if datatype in ['comments']:
                    continue
                k = str('CF_'+key[1:])
                name = CFM[key]['name']
                custcols.append(k)
                if datatype == 'text' and CFM[key]['is_multiple']:
                    kwargs[k] = \
                        concat('#T#'+name,
                               format_tag_string(val,
                                   CFM[key]['is_multiple']['ui_to_list'],
                                   ignore_max=True,
                                   joinval=CFM[key]['is_multiple']['list_to_ui']))
                else:
                    kwargs[k] = concat(name, val)
            kwargs['custcols'] = ','.join(custcols)
            books.append(E.book(c, **kwargs))

        updated = self.db.last_modified()
        kwargs = dict(
                start=str(start),
                updated=updated.strftime('%Y-%m-%dT%H:%M:%S+00:00'),
                total=str(len(ids)),
                num=str(len(books)))
        ans = E.library(*books, **kwargs)

        cherrypy.response.headers['Content-Type'] = 'text/xml'
        cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)

        return etree.tostring(ans, encoding='utf-8', pretty_print=True,
                xml_declaration=True)
Пример #38
0
    def xml(self,
            start='0',
            num='50',
            sort=None,
            search=None,
            _=None,
            order='ascending'):
        '''
        Serves metadata from the calibre database as XML.

        :param sort: Sort results by ``sort``. Can be one of `title,author,rating`.
        :param search: Filter results by ``search`` query. See :class:`SearchQueryParser` for query syntax
        :param start,num: Return the slice `[start:start+num]` of the sorted and filtered results
        :param _: Firefox seems to sometimes send this when using XMLHttpRequest with no caching
        '''
        try:
            start = int(start)
        except ValueError:
            raise cherrypy.HTTPError(400,
                                     'start: %s is not an integer' % start)
        try:
            num = int(num)
        except ValueError:
            raise cherrypy.HTTPError(400, 'num: %s is not an integer' % num)

        order = order.lower().strip() == 'ascending'

        if not search:
            search = ''
        if isbytestring(search):
            search = search.decode('UTF-8')

        ids = self.search_for_books(search)

        FM = self.db.FIELD_MAP

        items = [r for r in iter(self.db) if r[FM['id']] in ids]
        if sort is not None:
            self.sort(items, sort, order)

        books = []

        def serialize(x):
            if isinstance(x, unicode):
                return x
            if isbytestring(x):
                return x.decode(preferred_encoding, 'replace')
            return unicode(x)

        # This method uses its own book dict, not the Metadata dict. The loop
        # below could be changed to use db.get_metadata instead of reading
        # info directly from the record made by the view, but it doesn't seem
        # worth it at the moment.
        for record in items[start:start + num]:
            kwargs = {}
            aus = record[FM['authors']] if record[
                FM['authors']] else __builtin__._('Unknown')
            authors = '|'.join([i.replace('|', ',') for i in aus.split(',')])
            kwargs['authors'] = authors

            kwargs['series_index'] = \
                fmt_sidx(float(record[FM['series_index']]))

            for x in ('timestamp', 'pubdate'):
                kwargs[x] = strftime('%Y/%m/%d %H:%M:%S', record[FM[x]])

            for x in ('id', 'title', 'sort', 'author_sort', 'rating', 'size'):
                kwargs[x] = serialize(record[FM[x]])

            for x in ('formats', 'series', 'tags', 'publisher', 'comments',
                      'identifiers'):
                y = record[FM[x]]
                if x == 'tags':
                    y = format_tag_string(y, ',', ignore_max=True)
                kwargs[x] = serialize(y) if y else ''

            isbn = self.db.isbn(record[FM['id']], index_is_id=True)
            kwargs['isbn'] = serialize(isbn if isbn else '')

            kwargs['safe_title'] = ascii_filename(kwargs['title'])

            c = kwargs.pop('comments')

            CFM = self.db.field_metadata
            CKEYS = [
                key for key in sorted(custom_fields_to_display(self.db),
                                      key=lambda x: sort_key(CFM[x]['name']))
            ]
            custcols = []
            for key in CKEYS:

                def concat(name, val):
                    return '%s:#:%s' % (name, unicode(val))

                mi = self.db.get_metadata(record[CFM['id']['rec_index']],
                                          index_is_id=True)
                name, val = mi.format_field(key)
                if not val:
                    continue
                datatype = CFM[key]['datatype']
                if datatype in ['comments']:
                    continue
                k = str('CF_' + key[1:])
                name = CFM[key]['name']
                custcols.append(k)
                if datatype == 'text' and CFM[key]['is_multiple']:
                    kwargs[k] = \
                        concat('#T#'+name,
                               format_tag_string(val,
                                   CFM[key]['is_multiple']['ui_to_list'],
                                   ignore_max=True,
                                   joinval=CFM[key]['is_multiple']['list_to_ui']))
                else:
                    kwargs[k] = concat(name, val)
            kwargs['custcols'] = ','.join(custcols)
            books.append(E.book(c, **kwargs))

        updated = self.db.last_modified()
        kwargs = dict(start=str(start),
                      updated=updated.strftime('%Y-%m-%dT%H:%M:%S+00:00'),
                      total=str(len(ids)),
                      num=str(len(books)))
        ans = E.library(*books, **kwargs)

        cherrypy.response.headers['Content-Type'] = 'text/xml'
        cherrypy.response.headers['Last-Modified'] = self.last_modified(
            updated)

        return etree.tostring(ans,
                              encoding='utf-8',
                              pretty_print=True,
                              xml_declaration=True)
Пример #39
0
def render_data(mi, use_roman_numbers=True, all_fields=False):
    ans = []
    isdevice = not hasattr(mi, 'id')
    fm = getattr(mi, 'field_metadata', field_metadata)

    for field, display in get_field_list(fm):
        metadata = fm.get(field, None)
        if field == 'sort':
            field = 'title_sort'
        if all_fields:
            display = True
        if metadata['datatype'] == 'bool':
            isnull = mi.get(field) is None
        else:
            isnull = mi.is_null(field)
        if (not display or not metadata or isnull or field == 'comments'):
            continue
        name = metadata['name']
        if not name:
            name = field
        name += ':'
        if metadata['datatype'] == 'comments':
            val = getattr(mi, field)
            if val:
                val = force_unicode(val)
                ans.append((field,
                    u'<td class="comments" colspan="2">%s</td>'%comments_to_html(val)))
        elif metadata['datatype'] == 'rating':
            val = getattr(mi, field)
            if val:
                val = val/2.0
                ans.append((field,
                    u'<td class="title">%s</td><td class="rating" '
                    'style=\'font-family:"%s"\'>%s</td>'%(
                        name, rating_font(), u'\u2605'*int(val))))
        elif metadata['datatype'] == 'composite' and \
                            metadata['display'].get('contains_html', False):
            val = getattr(mi, field)
            if val:
                val = force_unicode(val)
                ans.append((field,
                    u'<td class="title">%s</td><td>%s</td>'%
                        (name, comments_to_html(val))))
        elif field == 'path':
            if mi.path:
                path = force_unicode(mi.path, filesystem_encoding)
                scheme = u'devpath' if isdevice else u'path'
                url = prepare_string_for_xml(path if isdevice else
                        unicode(mi.id), True)
                pathstr = _('Click to open')
                extra = ''
                if isdevice:
                    durl = url
                    if durl.startswith('mtp:::'):
                        durl = ':::'.join((durl.split(':::'))[2:])
                    extra = '<br><span style="font-size:smaller">%s</span>'%(
                            prepare_string_for_xml(durl))
                link = u'<a href="%s:%s" title="%s">%s</a>%s' % (scheme, url,
                        prepare_string_for_xml(path, True), pathstr, extra)
                ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(name, link)))
        elif field == 'formats':
            if isdevice:
                continue
            fmts = [u'<a href="format:%s:%s">%s</a>' % (mi.id, x, x) for x
                        in mi.formats]
            ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(name,
                u', '.join(fmts))))
        elif field == 'identifiers':
            urls = urls_from_identifiers(mi.identifiers)
            links = [u'<a href="%s" title="%s:%s">%s</a>' % (url, id_typ, id_val, name)
                    for name, id_typ, id_val, url in urls]
            links = u', '.join(links)
            if links:
                ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(
                    _('Ids')+':', links)))
        elif field == 'authors' and not isdevice:
            authors = []
            formatter = EvalFormatter()
            for aut in mi.authors:
                link = ''
                if mi.author_link_map[aut]:
                    link = mi.author_link_map[aut]
                elif gprefs.get('default_author_link'):
                    vals = {'author': aut.replace(' ', '+')}
                    try:
                        vals['author_sort'] =  mi.author_sort_map[aut].replace(' ', '+')
                    except:
                        vals['author_sort'] = aut.replace(' ', '+')
                    link = formatter.safe_format(
                            gprefs.get('default_author_link'), vals, '', vals)
                if link:
                    link = prepare_string_for_xml(link)
                    authors.append(u'<a calibre-data="authors" href="%s">%s</a>'%(link, aut))
                else:
                    authors.append(aut)
            ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(name,
                u' & '.join(authors))))
        elif field == 'languages':
            if not mi.languages:
                continue
            names = filter(None, map(calibre_langcode_to_name, mi.languages))
            ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(name,
                u', '.join(names))))
        else:
            val = mi.format_field(field)[-1]
            if val is None:
                continue
            val = prepare_string_for_xml(val)
            if metadata['datatype'] == 'series':
                sidx = mi.get(field+'_index')
                if sidx is None:
                    sidx = 1.0
                val = _('Book %(sidx)s of <span class="series_name">%(series)s</span>')%dict(
                        sidx=fmt_sidx(sidx, use_roman=use_roman_numbers),
                        series=prepare_string_for_xml(getattr(mi, field)))
            elif metadata['datatype'] == 'datetime':
                aval = getattr(mi, field)
                if is_date_undefined(aval):
                    continue

            ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(name, val)))

    dc = getattr(mi, 'device_collections', [])
    if dc:
        dc = u', '.join(sorted(dc, key=sort_key))
        ans.append(('device_collections',
            u'<td class="title">%s</td><td>%s</td>'%(
                _('Collections')+':', dc)))

    def classname(field):
        try:
            dt = fm[field]['datatype']
        except:
            dt = 'text'
        return 'datatype_%s'%dt

    ans = [u'<tr id="%s" class="%s">%s</tr>'%(field.replace('#', '_'),
        classname(field), html) for field, html in ans]
    # print '\n'.join(ans)
    return u'<table class="fields">%s</table>'%(u'\n'.join(ans))
Пример #40
0
def ACQUISITION_ENTRY(book_id, updated, request_context):
    field_metadata = request_context.db.field_metadata
    mi = request_context.db.get_metadata(book_id)
    extra = []
    if mi.rating > 0:
        rating = u''.join(repeat(u'\u2605', int(mi.rating / 2.)))
        extra.append(_('RATING: %s<br />') % rating)
    if mi.tags:
        extra.append(
            _('TAGS: %s<br />') %
            xml(format_tag_string(mi.tags, None, no_tag_count=True)))
    if mi.series:
        extra.append(
            _('SERIES: %(series)s [%(sidx)s]<br />') %
            dict(series=xml(mi.series), sidx=fmt_sidx(float(mi.series_index))))
    for key in field_metadata.ignorable_field_keys():
        name, val = mi.format_field(key)
        if val:
            fm = field_metadata[key]
            datatype = fm['datatype']
            if datatype == 'text' and fm['is_multiple']:
                extra.append(
                    '%s: %s<br />' %
                    (xml(name),
                     xml(
                         format_tag_string(
                             val,
                             fm['is_multiple']['ui_to_list'],
                             no_tag_count=True,
                             joinval=fm['is_multiple']['list_to_ui']))))
            elif datatype == 'comments' or (fm['datatype'] == 'composite'
                                            and fm['display'].get(
                                                'contains_html', False)):
                extra.append('%s: %s<br />' %
                             (xml(name), comments_to_html(unicode(val))))
            else:
                extra.append('%s: %s<br />' % (xml(name), xml(unicode(val))))
    if mi.comments:
        comments = comments_to_html(mi.comments)
        extra.append(comments)
    if extra:
        extra = html_to_lxml('\n'.join(extra))
    ans = E.entry(TITLE(mi.title),
                  E.author(E.name(authors_to_string(mi.authors))),
                  ID('urn:uuid:' + mi.uuid), UPDATED(updated))
    if len(extra):
        ans.append(E.content(extra, type='xhtml'))
    get = partial(request_context.ctx.url_for,
                  '/get',
                  book_id=book_id,
                  library_id=request_context.library_id)
    if mi.formats:
        for fmt in mi.formats:
            fmt = fmt.lower()
            mt = guess_type('a.' + fmt)[0]
            if mt:
                ans.append(
                    E.link(type=mt,
                           href=get(what=fmt),
                           rel="http://opds-spec.org/acquisition"))
    ans.append(
        E.link(type='image/jpeg',
               href=get(what='cover'),
               rel="http://opds-spec.org/cover"))
    ans.append(
        E.link(type='image/jpeg',
               href=get(what='thumb'),
               rel="http://opds-spec.org/thumbnail"))

    return ans
Пример #41
0
def format_text(mi, prefs):
    with preserve_fields(mi, 'authors formatted_series_index'):
        mi.authors = [a for a in mi.authors if a != _('Unknown')]
        mi.formatted_series_index = fmt_sidx(mi.series_index or 0, use_roman=config['use_roman_numerals_for_series_number'])
        return tuple(format_fields(mi, prefs))
Пример #42
0
 def __init__(self, series, series_index):
     display = ''
     if series:
         display = '%s [%s]' % (series, fmt_sidx(series_index))
     ReadOnlyTableWidgetItem.__init__(self, display)
     self.sortKey = '%s%04d' % (series, series_index)
Пример #43
0
def format_text(mi, prefs):
    with preserve_fields(mi, 'authors formatted_series_index'):
        mi.authors = [a for a in mi.authors if a != _('Unknown')]
        mi.formatted_series_index = fmt_sidx(mi.series_index or 0, use_roman=get_use_roman())
        return tuple(format_fields(mi, prefs))
Пример #44
0
def get_components(template, mi, id, timefmt='%b %Y', length=250,
        sanitize_func=ascii_filename, replace_whitespace=False,
        to_lowercase=False, safe_format=True, last_has_extension=True,
        single_dir=False):
    tsorder = tweaks['save_template_title_series_sorting']
    format_args = FORMAT_ARGS.copy()
    format_args.update(mi.all_non_none_fields())
    if mi.title:
        if tsorder == 'strictly_alphabetic':
            v = mi.title
        else:
            # title_sort might be missing or empty. Check both conditions
            v = mi.get('title_sort', None)
            if not v:
                v = title_sort(mi.title, order=tsorder)
        format_args['title'] = v
    if mi.authors:
        format_args['authors'] = mi.format_authors()
        format_args['author'] = format_args['authors']
    if mi.tags:
        format_args['tags'] = mi.format_tags()
        if format_args['tags'].startswith('/'):
            format_args['tags'] = format_args['tags'][1:]
    else:
        format_args['tags'] = ''
    if mi.series:
        format_args['series'] = title_sort(mi.series, order=tsorder)
        if mi.series_index is not None:
            format_args['series_index'] = mi.format_series_index()
    else:
        template = re.sub(r'\{series_index[^}]*?\}', '', template)
    if mi.rating is not None:
        format_args['rating'] = mi.format_rating(divide_by=2.0)
    if mi.identifiers:
        format_args['identifiers'] = mi.format_field_extended('identifiers')[1]
    else:
        format_args['identifiers'] = ''

    if hasattr(mi.timestamp, 'timetuple'):
        format_args['timestamp'] = strftime(timefmt, mi.timestamp.timetuple())
    if hasattr(mi.pubdate, 'timetuple'):
        format_args['pubdate'] = strftime(timefmt, mi.pubdate.timetuple())
    if hasattr(mi, 'last_modified') and hasattr(mi.last_modified, 'timetuple'):
        format_args['last_modified'] = strftime(timefmt, mi.last_modified.timetuple())

    format_args['id'] = unicode_type(id)
    # Now format the custom fields
    custom_metadata = mi.get_all_user_metadata(make_copy=False)
    for key in custom_metadata:
        if key in format_args:
            cm = custom_metadata[key]
            if cm['datatype'] == 'series':
                format_args[key] = title_sort(format_args[key], order=tsorder)
                if key+'_index' in format_args:
                    format_args[key+'_index'] = fmt_sidx(format_args[key+'_index'])
            elif cm['datatype'] == 'datetime':
                format_args[key] = strftime(timefmt, as_local_time(format_args[key]).timetuple())
            elif cm['datatype'] == 'bool':
                format_args[key] = _('yes') if format_args[key] else _('no')
            elif cm['datatype'] == 'rating':
                format_args[key] = mi.format_rating(format_args[key],
                        divide_by=2.0)
            elif cm['datatype'] in ['int', 'float']:
                if format_args[key] != 0:
                    format_args[key] = unicode_type(format_args[key])
                else:
                    format_args[key] = ''
    if safe_format:
        components = Formatter().safe_format(template, format_args,
                                            'G_C-EXCEPTION!', mi)
    else:
        components = Formatter().unsafe_format(template, format_args, mi)
    components = [x.strip() for x in components.split('/')]
    components = [sanitize_func(x) for x in components if x]
    if not components:
        components = [unicode_type(id)]
    if to_lowercase:
        components = [x.lower() for x in components]
    if replace_whitespace:
        components = [re.sub(r'\s', '_', x) for x in components]

    if single_dir:
        components = components[-1:]
    return shorten_components_to(length, components, last_has_extension=last_has_extension)
Пример #45
0
def mi_to_html(mi, field_list=None, default_author_link=None, use_roman_numbers=True, rating_font='Liberation Serif', rtl=False):
    if field_list is None:
        field_list = get_field_list(mi)
    ans = []
    comment_fields = []
    isdevice = not hasattr(mi, 'id')
    row = u'<td class="title">%s</td><td class="value">%s</td>'
    p = prepare_string_for_xml
    a = partial(prepare_string_for_xml, attribute=True)
    book_id = getattr(mi, 'id', 0)

    for field in (field for field, display in field_list if display):
        try:
            metadata = mi.metadata_for_field(field)
        except:
            continue
        if not metadata:
            continue
        if field == 'sort':
            field = 'title_sort'
        if metadata['is_custom'] and metadata['datatype'] in {'bool', 'int', 'float'}:
            isnull = mi.get(field) is None
        else:
            isnull = mi.is_null(field)
        if isnull:
            continue
        name = metadata['name']
        if not name:
            name = field
        name += ':'
        if metadata['datatype'] == 'comments' or field == 'comments':
            val = getattr(mi, field)
            if val:
                val = force_unicode(val)
                comment_fields.append(comments_to_html(val))
        elif metadata['datatype'] == 'rating':
            val = getattr(mi, field)
            if val:
                val = val/2.0
                ans.append((field,
                    u'<td class="title">%s</td><td class="rating value" '
                    'style=\'font-family:"%s"\'>%s</td>'%(
                        name, rating_font, u'\u2605'*int(val))))
        elif metadata['datatype'] == 'composite':
            val = getattr(mi, field)
            if val:
                val = force_unicode(val)
                if metadata['display'].get('contains_html', False):
                    ans.append((field, row % (name, comments_to_html(val))))
                else:
                    if not metadata['is_multiple']:
                        val = '<a href="%s" title="%s">%s</a>' % (
                              search_href(field, val),
                              _('Click to see books with {0}: {1}').format(metadata['name'], a(val)), p(val))
                    else:
                        all_vals = [v.strip()
                            for v in val.split(metadata['is_multiple']['list_to_ui']) if v.strip()]
                        links = ['<a href="%s" title="%s">%s</a>' % (
                            search_href(field, x), _('Click to see books with {0}: {1}').format(
                                     metadata['name'], a(x)), p(x)) for x in all_vals]
                        val = metadata['is_multiple']['list_to_ui'].join(links)
                    ans.append((field, row % (name, val)))
        elif field == 'path':
            if mi.path:
                path = force_unicode(mi.path, filesystem_encoding)
                scheme = u'devpath' if isdevice else u'path'
                url = prepare_string_for_xml(path if isdevice else
                        unicode(book_id), True)
                pathstr = _('Click to open')
                extra = ''
                if isdevice:
                    durl = url
                    if durl.startswith('mtp:::'):
                        durl = ':::'.join((durl.split(':::'))[2:])
                    extra = '<br><span style="font-size:smaller">%s</span>'%(
                            prepare_string_for_xml(durl))
                link = u'<a href="%s:%s" title="%s">%s</a>%s' % (scheme, url,
                        prepare_string_for_xml(path, True), pathstr, extra)
                ans.append((field, row % (name, link)))
        elif field == 'formats':
            if isdevice:
                continue
            path = mi.path or ''
            bpath = ''
            if path:
                h, t = os.path.split(path)
                bpath = os.sep.join((os.path.basename(h), t))
            data = ({
                'fmt':x, 'path':a(path or ''), 'fname':a(mi.format_files.get(x, '')),
                'ext':x.lower(), 'id':book_id, 'bpath':bpath, 'sep':os.sep
            } for x in mi.formats)
            fmts = [u'<a data-full-path="{path}{sep}{fname}.{ext}" title="{bpath}{sep}{fname}.{ext}" href="format:{id}:{fmt}">{fmt}</a>'.format(**x)
                    for x in data]
            ans.append((field, row % (name, u', '.join(fmts))))
        elif field == 'identifiers':
            urls = urls_from_identifiers(mi.identifiers)
            links = [u'<a href="%s" title="%s:%s" data-item="%s">%s</a>' % (a(url), a(id_typ), a(id_val), a(item_data(field, id_typ, book_id)), p(namel))
                    for namel, id_typ, id_val, url in urls]
            links = u', '.join(links)
            if links:
                ans.append((field, row % (_('Ids')+':', links)))
        elif field == 'authors' and not isdevice:
            authors = []
            formatter = EvalFormatter()
            for aut in mi.authors:
                link = ''
                if mi.author_link_map[aut]:
                    link = lt = mi.author_link_map[aut]
                elif default_author_link:
                    if default_author_link == 'search-calibre':
                        link = search_href('authors', aut)
                        lt = a(_('Search the calibre library for books by %s') % aut)
                    else:
                        vals = {'author': aut.replace(' ', '+')}
                        try:
                            vals['author_sort'] =  mi.author_sort_map[aut].replace(' ', '+')
                        except:
                            vals['author_sort'] = aut.replace(' ', '+')
                        link = lt = a(formatter.safe_format(default_author_link, vals, '', vals))
                aut = p(aut)
                if link:
                    authors.append(u'<a calibre-data="authors" title="%s" href="%s">%s</a>'%(lt, link, aut))
                else:
                    authors.append(aut)
            ans.append((field, row % (name, u' & '.join(authors))))
        elif field == 'languages':
            if not mi.languages:
                continue
            names = filter(None, map(calibre_langcode_to_name, mi.languages))
            ans.append((field, row % (name, u', '.join(names))))
        elif field == 'publisher':
            if not mi.publisher:
                continue
            val = '<a href="%s" title="%s" data-item="%s">%s</a>' % (
                search_href('publisher', mi.publisher), _('Click to see books with {0}: {1}').format(metadata['name'], a(mi.publisher)),
                a(item_data('publisher', mi.publisher, book_id)), p(mi.publisher))
            ans.append((field, row % (name, val)))
        elif field == 'title':
            # otherwise title gets metadata['datatype'] == 'text'
            # treatment below with a click to search link (which isn't
            # too bad), and a right-click 'Delete' option to delete
            # the title (which is bad).
            val = mi.format_field(field)[-1]
            ans.append((field, row % (name, val)))
        else:
            val = mi.format_field(field)[-1]
            if val is None:
                continue
            val = p(val)
            if metadata['datatype'] == 'series':
                sidx = mi.get(field+'_index')
                if sidx is None:
                    sidx = 1.0
                try:
                    st = metadata['search_terms'][0]
                except Exception:
                    st = field
                series = getattr(mi, field)
                val = _(
                    '%(sidx)s of <a href="%(href)s" title="%(tt)s" data-item="%(data)s">'
                    '<span class="%(cls)s">%(series)s</span></a>') % dict(
                        sidx=fmt_sidx(sidx, use_roman=use_roman_numbers), cls="series_name",
                        series=p(series), href=search_href(st, series),
                        data=a(item_data(field, series, book_id)),
                        tt=p(_('Click to see books in this series')))
            elif metadata['datatype'] == 'datetime':
                aval = getattr(mi, field)
                if is_date_undefined(aval):
                    continue
            elif metadata['datatype'] == 'text' and metadata['is_multiple']:
                try:
                    st = metadata['search_terms'][0]
                except Exception:
                    st = field
                all_vals = mi.get(field)
                if field == 'tags':
                    all_vals = sorted(all_vals, key=sort_key)
                links = ['<a href="%s" title="%s" data-item="%s">%s</a>' % (
                    search_href(st, x), _('Click to see books with {0}: {1}').format(
                        metadata['name'], a(x)), a(item_data(field, x, book_id)), p(x))
                         for x in all_vals]
                val = metadata['is_multiple']['list_to_ui'].join(links)
            elif metadata['datatype'] == 'text' or metadata['datatype'] == 'enumeration':
                # text/is_multiple handled above so no need to add the test to the if
                try:
                    st = metadata['search_terms'][0]
                except Exception:
                    st = field
                val = '<a href="%s" title="%s" data-item="%s">%s</a>' % (
                    search_href(st, val), a(_('Click to see books with {0}: {1}').format(metadata['name'], val)),
                    a(item_data(field, val, book_id)), p(val))

            ans.append((field, row % (name, val)))

    dc = getattr(mi, 'device_collections', [])
    if dc:
        dc = u', '.join(sorted(dc, key=sort_key))
        ans.append(('device_collections',
            row % (_('Collections')+':', dc)))

    def classname(field):
        try:
            dt = mi.metadata_for_field(field)['datatype']
        except:
            dt = 'text'
        return 'datatype_%s'%dt

    ans = [u'<tr id="%s" class="%s">%s</tr>'%(fieldl.replace('#', '_'),
        classname(fieldl), html) for fieldl, html in ans]
    # print '\n'.join(ans)
    direction = 'rtl' if rtl else 'ltr'
    margin = 'left' if rtl else 'right'
    return u'<table class="fields" style="direction: %s; margin-%s:auto">%s</table>'%(direction, margin, u'\n'.join(ans)), comment_fields
Пример #46
0
def ACQUISITION_ENTRY(item, version, db, updated, CFM, CKEYS, prefix):
    FM = db.FIELD_MAP
    title = item[FM["title"]]
    if not title:
        title = _("Unknown")
    authors = item[FM["authors"]]
    if not authors:
        authors = _("Unknown")
    authors = " & ".join([i.replace("|", ",") for i in authors.split(",")])
    extra = []
    rating = item[FM["rating"]]
    if rating > 0:
        rating = u"".join(repeat(u"\u2605", int(rating / 2.0)))
        extra.append(_("RATING: %s<br />") % rating)
    tags = item[FM["tags"]]
    if tags:
        extra.append(_("TAGS: %s<br />") % xml(format_tag_string(tags, ",", ignore_max=True, no_tag_count=True)))
    series = item[FM["series"]]
    if series:
        extra.append(
            _("SERIES: %(series)s [%(sidx)s]<br />")
            % dict(series=xml(series), sidx=fmt_sidx(float(item[FM["series_index"]])))
        )
    for key in CKEYS:
        mi = db.get_metadata(item[CFM["id"]["rec_index"]], index_is_id=True)
        name, val = mi.format_field(key)
        if val:
            datatype = CFM[key]["datatype"]
            if datatype == "text" and CFM[key]["is_multiple"]:
                extra.append(
                    "%s: %s<br />"
                    % (
                        xml(name),
                        xml(
                            format_tag_string(
                                val,
                                CFM[key]["is_multiple"]["ui_to_list"],
                                ignore_max=True,
                                no_tag_count=True,
                                joinval=CFM[key]["is_multiple"]["list_to_ui"],
                            )
                        ),
                    )
                )
            elif datatype == "comments" or (
                CFM[key]["datatype"] == "composite" and CFM[key]["display"].get("contains_html", False)
            ):
                extra.append("%s: %s<br />" % (xml(name), comments_to_html(unicode(val))))
            else:
                extra.append("%s: %s<br />" % (xml(name), xml(unicode(val))))
    comments = item[FM["comments"]]
    if comments:
        comments = comments_to_html(comments)
        extra.append(comments)
    if extra:
        extra = html_to_lxml("\n".join(extra))
    idm = "calibre" if version == 0 else "uuid"
    id_ = "urn:%s:%s" % (idm, item[FM["uuid"]])
    ans = E.entry(TITLE(title), E.author(E.name(authors)), ID(id_), UPDATED(updated))
    if len(extra):
        ans.append(E.content(extra, type="xhtml"))
    formats = item[FM["formats"]]
    if formats:
        for fmt in formats.split(","):
            fmt = fmt.lower()
            mt = guess_type("a." + fmt)[0]
            href = prefix + "/get/%s/%s" % (fmt, item[FM["id"]])
            if mt:
                link = E.link(type=mt, href=href)
                if version > 0:
                    link.set("rel", "http://opds-spec.org/acquisition")
                ans.append(link)
    ans.append(
        E.link(
            type="image/jpeg",
            href=prefix + "/get/cover/%s" % item[FM["id"]],
            rel="x-stanza-cover-image" if version == 0 else "http://opds-spec.org/cover",
        )
    )
    ans.append(
        E.link(
            type="image/jpeg",
            href=prefix + "/get/thumb/%s" % item[FM["id"]],
            rel="x-stanza-cover-image-thumbnail" if version == 0 else "http://opds-spec.org/thumbnail",
        )
    )

    return ans
Пример #47
0
def ACQUISITION_ENTRY(item, version, db, updated, CFM, CKEYS, prefix):
    FM = db.FIELD_MAP
    title = item[FM['title']]
    if not title:
        title = _('Unknown')
    authors = item[FM['authors']]
    if not authors:
        authors = _('Unknown')
    authors = ' & '.join([i.replace('|', ',') for i in
                                    authors.split(',')])
    extra = []
    rating = item[FM['rating']]
    if rating > 0:
        rating = u''.join(repeat(u'\u2605', int(rating/2.)))
        extra.append(_('RATING: %s<br />')%rating)
    tags = item[FM['tags']]
    if tags:
        extra.append(_('TAGS: %s<br />')%xml(format_tag_string(tags, ',',
                                                           ignore_max=True,
                                                           no_tag_count=True)))
    series = item[FM['series']]
    if series:
        extra.append(_('SERIES: %(series)s [%(sidx)s]<br />')%\
                dict(series=xml(series),
                sidx=fmt_sidx(float(item[FM['series_index']]))))
    for key in CKEYS:
        mi = db.get_metadata(item[CFM['id']['rec_index']], index_is_id=True)
        name, val = mi.format_field(key)
        if val:
            datatype = CFM[key]['datatype']
            if datatype == 'text' and CFM[key]['is_multiple']:
                extra.append('%s: %s<br />'%
                             (xml(name),
                              xml(format_tag_string(val,
                                    CFM[key]['is_multiple']['ui_to_list'],
                                    ignore_max=True, no_tag_count=True,
                                    joinval=CFM[key]['is_multiple']['list_to_ui']))))
            elif datatype == 'comments' or (CFM[key]['datatype'] == 'composite' and
                            CFM[key]['display'].get('contains_html', False)):
                extra.append('%s: %s<br />'%(xml(name), comments_to_html(unicode(val))))
            else:
                extra.append('%s: %s<br />'%(xml(name), xml(unicode(val))))
    comments = item[FM['comments']]
    if comments:
        comments = comments_to_html(comments)
        extra.append(comments)
    if extra:
        extra = html_to_lxml('\n'.join(extra))
    idm = 'calibre' if version == 0 else 'uuid'
    id_ = 'urn:%s:%s'%(idm, item[FM['uuid']])
    ans = E.entry(TITLE(title), E.author(E.name(authors)), ID(id_),
            UPDATED(updated))
    if len(extra):
        ans.append(E.content(extra, type='xhtml'))
    formats = item[FM['formats']]
    if formats:
        for fmt in formats.split(','):
            fmt = fmt.lower()
            mt = guess_type('a.'+fmt)[0]
            href = prefix + '/get/%s/%s'%(fmt, item[FM['id']])
            if mt:
                link = E.link(type=mt, href=href)
                if version > 0:
                    link.set('rel', "http://opds-spec.org/acquisition")
                ans.append(link)
    ans.append(E.link(type='image/jpeg', href=prefix+'/get/cover/%s'%item[FM['id']],
        rel="x-stanza-cover-image" if version == 0 else
        "http://opds-spec.org/cover"))
    ans.append(E.link(type='image/jpeg', href=prefix+'/get/thumb/%s'%item[FM['id']],
        rel="x-stanza-cover-image-thumbnail" if version == 0 else
        "http://opds-spec.org/thumbnail"))

    return ans
Пример #48
0
    def mobile(self, start="1", num="25", sort="date", search="", _=None, order="descending"):
        """
        Serves metadata from the calibre database as XML.

        :param sort: Sort results by ``sort``. Can be one of `title,author,rating`.
        :param search: Filter results by ``search`` query. See :class:`SearchQueryParser` for query syntax
        :param start,num: Return the slice `[start:start+num]` of the sorted and filtered results
        :param _: Firefox seems to sometimes send this when using XMLHttpRequest with no caching
        """
        try:
            start = int(start)
        except ValueError:
            raise cherrypy.HTTPError(400, "start: %s is not an integer" % start)
        try:
            num = int(num)
        except ValueError:
            raise cherrypy.HTTPError(400, "num: %s is not an integer" % num)
        if not search:
            search = ""
        if isbytestring(search):
            search = search.decode("UTF-8")
        ids = self.search_for_books(search)
        FM = self.db.FIELD_MAP
        items = [r for r in iter(self.db) if r[FM["id"]] in ids]
        if sort is not None:
            self.sort(items, sort, (order.lower().strip() == "ascending"))

        CFM = self.db.field_metadata
        CKEYS = [key for key in sorted(custom_fields_to_display(self.db), key=lambda x: sort_key(CFM[x]["name"]))]
        # This method uses its own book dict, not the Metadata dict. The loop
        # below could be changed to use db.get_metadata instead of reading
        # info directly from the record made by the view, but it doesn't seem
        # worth it at the moment.
        books = []
        for record in items[(start - 1) : (start - 1) + num]:
            book = {"formats": record[FM["formats"]], "size": record[FM["size"]]}
            if not book["formats"]:
                book["formats"] = ""
            if not book["size"]:
                book["size"] = 0
            book["size"] = human_readable(book["size"])

            aus = record[FM["authors"]] if record[FM["authors"]] else __builtin__._("Unknown")
            aut_is = CFM["authors"]["is_multiple"]
            authors = aut_is["list_to_ui"].join([i.replace("|", ",") for i in aus.split(",")])
            book["authors"] = authors
            book["series_index"] = fmt_sidx(float(record[FM["series_index"]]))
            book["series"] = record[FM["series"]]
            book["tags"] = format_tag_string(record[FM["tags"]], ",", no_tag_count=True)
            book["title"] = record[FM["title"]]
            for x in ("timestamp", "pubdate"):
                book[x] = strftime("%d %b, %Y", as_local_time(record[FM[x]]))
            book["id"] = record[FM["id"]]
            books.append(book)
            for key in CKEYS:

                def concat(name, val):
                    return "%s:#:%s" % (name, unicode(val))

                mi = self.db.get_metadata(record[CFM["id"]["rec_index"]], index_is_id=True)
                name, val = mi.format_field(key)
                if not val:
                    continue
                datatype = CFM[key]["datatype"]
                if datatype in ["comments"]:
                    continue
                if datatype == "text" and CFM[key]["is_multiple"]:
                    book[key] = concat(
                        name,
                        format_tag_string(
                            val,
                            CFM[key]["is_multiple"]["ui_to_list"],
                            no_tag_count=True,
                            joinval=CFM[key]["is_multiple"]["list_to_ui"],
                        ),
                    )
                else:
                    book[key] = concat(name, val)

        updated = self.db.last_modified()

        cherrypy.response.headers["Content-Type"] = "text/html; charset=utf-8"
        cherrypy.response.headers["Last-Modified"] = self.last_modified(updated)

        q = {
            b"search": search.encode("utf-8"),
            b"order": order.encode("utf-8"),
            b"sort": sort.encode("utf-8"),
            b"num": str(num).encode("utf-8"),
        }
        url_base = "/mobile?" + urlencode(q)
        ua = cherrypy.request.headers.get("User-Agent", "").strip()
        have_kobo_browser = self.is_kobo_browser(ua)

        raw = html.tostring(
            build_index(
                books,
                num,
                search,
                sort,
                order,
                start,
                len(ids),
                url_base,
                CKEYS,
                self.opts.url_prefix,
                have_kobo_browser=have_kobo_browser,
            ),
            encoding="utf-8",
            pretty_print=True,
        )
        # tostring's include_meta_content_type is broken
        raw = raw.replace("<head>", "<head>\n" '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">')
        return raw
Пример #49
0
def get_components(template, mi, id, timefmt='%b %Y', length=250,
        sanitize_func=ascii_filename, replace_whitespace=False,
        to_lowercase=False, safe_format=True):

    tsorder = tweaks['save_template_title_series_sorting']
    format_args = FORMAT_ARGS.copy()
    format_args.update(mi.all_non_none_fields())
    if mi.title:
        if tsorder == 'strictly_alphabetic':
            v = mi.title
        else:
            # title_sort might be missing or empty. Check both conditions
            v = mi.get('title_sort', None)
            if not v:
                v = title_sort(mi.title, order=tsorder)
        format_args['title'] = v
    if mi.authors:
        format_args['authors'] = mi.format_authors()
        format_args['author'] = format_args['authors']
    if mi.tags:
        format_args['tags'] = mi.format_tags()
        if format_args['tags'].startswith('/'):
            format_args['tags'] = format_args['tags'][1:]
    else:
        format_args['tags'] = ''
    if mi.series:
        format_args['series'] = title_sort(mi.series, order=tsorder)
        if mi.series_index is not None:
            format_args['series_index'] = mi.format_series_index()
    else:
        template = re.sub(r'\{series_index[^}]*?\}', '', template)
    if mi.rating is not None:
        format_args['rating'] = mi.format_rating(divide_by=2.0)
    if mi.identifiers:
        format_args['identifiers'] = mi.format_field_extended('identifiers')[1]
    else:
        format_args['identifiers'] = ''

    if hasattr(mi.timestamp, 'timetuple'):
        format_args['timestamp'] = strftime(timefmt, mi.timestamp.timetuple())
    if hasattr(mi.pubdate, 'timetuple'):
        format_args['pubdate'] = strftime(timefmt, mi.pubdate.timetuple())
    if hasattr(mi, 'last_modified') and hasattr(mi.last_modified, 'timetuple'):
        format_args['last_modified'] = strftime(timefmt, mi.last_modified.timetuple())

    format_args['id'] = str(id)
    # Now format the custom fields
    custom_metadata = mi.get_all_user_metadata(make_copy=False)
    for key in custom_metadata:
        if key in format_args:
            cm = custom_metadata[key]
            if cm['datatype'] == 'series':
                format_args[key] = title_sort(format_args[key], order=tsorder)
                if key+'_index' in format_args:
                    format_args[key+'_index'] = fmt_sidx(format_args[key+'_index'])
            elif cm['datatype'] == 'datetime':
                format_args[key] = strftime(timefmt, format_args[key].timetuple())
            elif cm['datatype'] == 'bool':
                format_args[key] = _('yes') if format_args[key] else _('no')
            elif cm['datatype'] == 'rating':
                format_args[key] = mi.format_rating(format_args[key],
                        divide_by=2.0)
            elif cm['datatype'] in ['int', 'float']:
                if format_args[key] != 0:
                    format_args[key] = unicode(format_args[key])
                else:
                    format_args[key] = ''
    if safe_format:
        components = Formatter().safe_format(template, format_args,
                                            'G_C-EXCEPTION!', mi)
    else:
        components = Formatter().unsafe_format(template, format_args, mi)
    components = [x.strip() for x in components.split('/')]
    components = [sanitize_func(x) for x in components if x]
    if not components:
        components = [str(id)]
    if to_lowercase:
        components = [x.lower() for x in components]
    if replace_whitespace:
        components = [re.sub(r'\s', '_', x) for x in components]

    return shorten_components_to(length, components)
Пример #50
0
    def mobile(self, start='1', num='25', sort='date', search='',
                _=None, order='descending'):
        '''
        Serves metadata from the calibre database as XML.

        :param sort: Sort results by ``sort``. Can be one of `title,author,rating`.
        :param search: Filter results by ``search`` query. See :class:`SearchQueryParser` for query syntax
        :param start,num: Return the slice `[start:start+num]` of the sorted and filtered results
        :param _: Firefox seems to sometimes send this when using XMLHttpRequest with no caching
        '''
        try:
            start = int(start)
        except ValueError:
            raise cherrypy.HTTPError(400, 'start: %s is not an integer'%start)
        try:
            num = int(num)
        except ValueError:
            raise cherrypy.HTTPError(400, 'num: %s is not an integer'%num)
        if not search:
            search = ''
        if isbytestring(search):
            search = search.decode('UTF-8')
        ids = self.db.search_getting_ids(search.strip(), self.search_restriction)
        FM = self.db.FIELD_MAP
        items = [r for r in iter(self.db) if r[FM['id']] in ids]
        if sort is not None:
            self.sort(items, sort, (order.lower().strip() == 'ascending'))

        CFM = self.db.field_metadata
        CKEYS = [key for key in sorted(custom_fields_to_display(self.db),
                                       key=lambda x:sort_key(CFM[x]['name']))]
        # This method uses its own book dict, not the Metadata dict. The loop
        # below could be changed to use db.get_metadata instead of reading
        # info directly from the record made by the view, but it doesn't seem
        # worth it at the moment.
        books = []
        for record in items[(start-1):(start-1)+num]:
            book = {'formats':record[FM['formats']], 'size':record[FM['size']]}
            if not book['formats']:
                book['formats'] = ''
            if not book['size']:
                book['size'] = 0
            book['size'] = human_readable(book['size'])

            aus = record[FM['authors']] if record[FM['authors']] else __builtin__._('Unknown')
            aut_is = CFM['authors']['is_multiple']
            authors = aut_is['list_to_ui'].join([i.replace('|', ',') for i in aus.split(',')])
            book['authors'] = authors
            book['series_index'] = fmt_sidx(float(record[FM['series_index']]))
            book['series'] = record[FM['series']]
            book['tags'] = format_tag_string(record[FM['tags']], ',',
                                             no_tag_count=True)
            book['title'] = record[FM['title']]
            for x in ('timestamp', 'pubdate'):
                book[x] = strftime('%d %b, %Y', record[FM[x]])
            book['id'] = record[FM['id']]
            books.append(book)
            for key in CKEYS:
                def concat(name, val):
                    return '%s:#:%s'%(name, unicode(val))
                mi = self.db.get_metadata(record[CFM['id']['rec_index']], index_is_id=True)
                name, val = mi.format_field(key)
                if not val:
                    continue
                datatype = CFM[key]['datatype']
                if datatype in ['comments']:
                    continue
                if datatype == 'text' and CFM[key]['is_multiple']:
                    book[key] = concat(name,
                                       format_tag_string(val,
                                           CFM[key]['is_multiple']['ui_to_list'],
                                           no_tag_count=True,
                                           joinval=CFM[key]['is_multiple']['list_to_ui']))
                else:
                    book[key] = concat(name, val)

        updated = self.db.last_modified()

        cherrypy.response.headers['Content-Type'] = 'text/html; charset=utf-8'
        cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)

        url_base = "/mobile?search=" + search+";order="+order+";sort="+sort+";num="+str(num)

        raw = html.tostring(build_index(books, num, search, sort, order,
                             start, len(ids), url_base, CKEYS,
                             self.opts.url_prefix),
                             encoding='utf-8',
                             pretty_print=True)
        # tostring's include_meta_content_type is broken
        raw = raw.replace('<head>', '<head>\n'
                '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">')
        return raw