Exemple #1
0
 def to_html(self):
     '''
     A HTML representation of this object.
     '''
     from calibre.ebooks.metadata import authors_to_string
     from calibre.utils.date import isoformat
     ans = [(_('Title'), unicode(self.title))]
     ans += [(_('Author(s)'), (authors_to_string(self.authors) if self.authors else _('Unknown')))]
     ans += [(_('Publisher'), unicode(self.publisher))]
     ans += [(_('Producer'), unicode(self.book_producer))]
     ans += [(_('Comments'), unicode(self.comments))]
     ans += [('ISBN', unicode(self.isbn))]
     ans += [(_('Tags'), u', '.join([unicode(t) for t in self.tags]))]
     if self.series:
         ans += [(_('Series'), unicode(self.series) + ' #%s'%self.format_series_index())]
     ans += [(_('Languages'), u', '.join(self.languages))]
     if self.timestamp is not None:
         ans += [(_('Timestamp'), unicode(isoformat(self.timestamp, as_utc=False, sep=' ')))]
     if self.pubdate is not None:
         ans += [(_('Published'), unicode(isoformat(self.pubdate, as_utc=False, sep=' ')))]
     if self.rights is not None:
         ans += [(_('Rights'), unicode(self.rights))]
     for key in self.custom_field_keys():
         val = self.get(key, None)
         if val:
             (name, val) = self.format_field(key)
             ans += [(name, val)]
     for i, x in enumerate(ans):
         ans[i] = u'<tr><td><b>%s</b></td><td>%s</td></tr>'%x
     return u'<table>%s</table>'%u'\n'.join(ans)
Exemple #2
0
    def __unicode__representation__(self):
        '''
        A string representation of this object, suitable for printing to
        console
        '''
        from calibre.utils.date import isoformat
        from calibre.ebooks.metadata import authors_to_string
        ans = []

        def fmt(x, y):
            ans.append(u'%-20s: %s'%(unicode_type(x), unicode_type(y)))

        fmt('Title', self.title)
        if self.title_sort:
            fmt('Title sort', self.title_sort)
        if self.authors:
            fmt('Author(s)',  authors_to_string(self.authors) +
               ((' [' + self.author_sort + ']')
                if self.author_sort and self.author_sort != _('Unknown') else ''))
        if self.publisher:
            fmt('Publisher', self.publisher)
        if getattr(self, 'book_producer', False):
            fmt('Book Producer', self.book_producer)
        if self.tags:
            fmt('Tags', u', '.join([unicode_type(t) for t in self.tags]))
        if self.series:
            fmt('Series', self.series + ' #%s'%self.format_series_index())
        if not self.is_null('languages'):
            fmt('Languages', ', '.join(self.languages))
        if self.rating is not None:
            fmt('Rating', (u'%.2g'%(float(self.rating)/2.0)) if self.rating
                    else u'')
        if self.timestamp is not None:
            fmt('Timestamp', isoformat(self.timestamp))
        if self.pubdate is not None:
            fmt('Published', isoformat(self.pubdate))
        if self.rights is not None:
            fmt('Rights', unicode_type(self.rights))
        if self.identifiers:
            fmt('Identifiers', u', '.join(['%s:%s'%(k, v) for k, v in
                iteritems(self.identifiers)]))
        if self.comments:
            fmt('Comments', self.comments)

        for key in self.custom_field_keys():
            val = self.get(key, None)
            if val:
                (name, val) = self.format_field(key)
                fmt(name, unicode_type(val))
        return u'\n'.join(ans)
Exemple #3
0
 def _update_drive_info(self, storage, location_code, name=None):
     from calibre.utils.date import isoformat, now
     from calibre.utils.config import from_json, to_json
     import uuid
     f = storage.find_path(self.calibre_file_paths['driveinfo'].split('/'))
     dinfo = {}
     if f is not None:
         try:
             stream = self.get_mtp_file(f)
             dinfo = json.load(stream, object_hook=from_json)
         except:
             prints('Failed to load existing driveinfo.calibre file, with error:')
             traceback.print_exc()
             dinfo = {}
     if dinfo.get('device_store_uuid', None) is None:
         dinfo['device_store_uuid'] = unicode(uuid.uuid4())
     if dinfo.get('device_name', None) is None:
         dinfo['device_name'] = self.current_friendly_name
     if name is not None:
         dinfo['device_name'] = name
     dinfo['location_code'] = location_code
     dinfo['last_library_uuid'] = getattr(self, 'current_library_uuid', None)
     dinfo['calibre_version'] = '.'.join([unicode(i) for i in numeric_version])
     dinfo['date_last_connected'] = isoformat(now())
     dinfo['mtp_prefix'] = storage.storage_prefix
     raw = json.dumps(dinfo, default=to_json)
     self.put_calibre_file(storage, 'driveinfo', BytesIO(raw), len(raw))
     self.driveinfo[location_code] = dinfo
Exemple #4
0
def stringify(data, metadata, for_machine):
    for field, m in iteritems(metadata):
        if field == 'authors':
            data[field] = {
                k: authors_to_string(v)
                for k, v in iteritems(data[field])
            }
        else:
            dt = m['datatype']
            if dt == 'datetime':
                data[field] = {
                    k: isoformat(v, as_utc=for_machine) if v else 'None'
                    for k, v in iteritems(data[field])
                }
            elif not for_machine:
                ism = m['is_multiple']
                if ism:
                    data[field] = {
                        k: ism['list_to_ui'].join(v)
                        for k, v in iteritems(data[field])
                    }
                    if field == 'formats':
                        data[field] = {
                            k: '[' + v + ']'
                            for k, v in iteritems(data[field])
                        }
Exemple #5
0
def set_pubdate(root, prefixes, refines, val):
    for date in XPath('./opf:metadata/dc:date')(root):
        remove_element(date, refines)
    if not is_date_undefined(val):
        val = isoformat(val)
        m = XPath('./opf:metadata')(root)[0]
        d = m.makeelement(DC('date'))
        d.text = val
        m.append(d)
Exemple #6
0
def to_json(obj):
    if isinstance(obj, bytearray):
        return {'__class__': 'bytearray',
                '__value__': base64.standard_b64encode(bytes(obj))}
    if isinstance(obj, datetime.datetime):
        from calibre.utils.date import isoformat
        return {'__class__': 'datetime.datetime',
                '__value__': isoformat(obj, as_utc=True)}
    raise TypeError(repr(obj) + ' is not JSON serializable')
Exemple #7
0
def encode_datetime(dateval):
    if dateval is None:
        return "None"
    if not isinstance(dateval, datetime):
        dateval = datetime.combine(dateval, time())
    if hasattr(dateval, 'tzinfo') and dateval.tzinfo is None:
        dateval = dateval.replace(tzinfo=local_tz)
    if dateval <= UNDEFINED_DATE:
        return None
    return isoformat(dateval)
Exemple #8
0
def datetime_to_string(dateval):
    from calibre.utils.date import isoformat, UNDEFINED_DATE, local_tz
    if dateval is None:
        return "None"
    if not isinstance(dateval, datetime):
        dateval = datetime.combine(dateval, time())
    if hasattr(dateval, 'tzinfo') and dateval.tzinfo is None:
        dateval = dateval.replace(tzinfo=local_tz)
    if dateval <= UNDEFINED_DATE:
        return "None"
    return isoformat(dateval)
Exemple #9
0
    def open(self, device, library_uuid):
        self.current_library_uuid = library_uuid
        self.location_paths = None
        self.driveinfo = {}
        BASE.open(self, device, library_uuid)
        h = self.prefs['history']
        if self.current_serial_num:
            h[self.current_serial_num] = (self.current_friendly_name,
                    isoformat(utcnow()))
            self.prefs['history'] = h

        self.current_device_defaults = self.device_defaults(device, self)
Exemple #10
0
def set_timestamp(root, prefixes, refines, val):
    ensure_prefix(root, prefixes, 'calibre', CALIBRE_PREFIX)
    ensure_prefix(root, prefixes, 'dcterms')
    pq = '%s:timestamp' % CALIBRE_PREFIX
    for meta in XPath('./opf:metadata/opf:meta')(root):
        prop = expand_prefix(meta.get('property'), prefixes)
        if prop.lower() == pq or meta.get('name') == 'calibre:timestamp':
            remove_element(meta, refines)
    if not is_date_undefined(val):
        val = isoformat(val)
        m = XPath('./opf:metadata')(root)[0]
        d = m.makeelement(OPF('meta'), attrib={'property':'calibre:timestamp', 'scheme':'dcterms:W3CDTF'})
        d.text = val
        m.append(d)
Exemple #11
0
    def open(self, device, library_uuid):
        from calibre.utils.date import isoformat, utcnow
        self.current_library_uuid = library_uuid
        self.location_paths = None
        self.driveinfo = {}
        BASE.open(self, device, library_uuid)
        h = self.prefs['history']
        if self.current_serial_num:
            h[self.current_serial_num] = (self.current_friendly_name,
                    isoformat(utcnow()))
            self.prefs['history'] = h

        self.current_device_defaults = self.device_defaults(device, self)
        self.calibre_file_paths = self.current_device_defaults.get(
            'calibre_file_paths', {'metadata':self.METADATA_CACHE, 'driveinfo':self.DRIVEINFO})
Exemple #12
0
            def tpl_replace(objtplname) :

                tpl_field = re.sub(u'[\{\}]', u'', objtplname.group())

                if tpl_field in TEMPLATE_ALLOWED_FIELDS :
                    if tpl_field in ['pubdate', 'timestamp'] :
                        tpl_field = isoformat(entry[tpl_field]).partition('T')[0]
                    elif tpl_field in ['tags', 'authors'] :
                        tpl_field =entry[tpl_field][0]
                    elif tpl_field in ['id', 'series_index'] :
                        tpl_field = str(entry[tpl_field])
                    else :
                        tpl_field = entry[tpl_field]
                    return tpl_field
                else:
                    return u''
Exemple #13
0
 def _update_driveinfo_record(self, dinfo, prefix, location_code, name=None):
     import uuid
     if not isinstance(dinfo, dict):
         dinfo = {}
     if dinfo.get('device_store_uuid', None) is None:
         dinfo['device_store_uuid'] = unicode(uuid.uuid4())
     if dinfo.get('device_name') is None:
         dinfo['device_name'] = self.get_gui_name()
     if name is not None:
         dinfo['device_name'] = name
     dinfo['location_code'] = location_code
     dinfo['last_library_uuid'] = getattr(self, 'current_library_uuid', None)
     dinfo['calibre_version'] = '.'.join([unicode(i) for i in numeric_version])
     dinfo['date_last_connected'] = isoformat(now())
     dinfo['prefix'] = prefix.replace('\\', '/')
     return dinfo
Exemple #14
0
 def update_last_downloaded(self, recipe_id):
     with self.lock:
         now = utcnow()
         for x in self.iter_recipes():
             if x.get('id', False) == recipe_id:
                 typ, sch, last_downloaded = self.un_serialize_schedule(x)
                 if typ == 'interval':
                     # Prevent downloads more frequent than once an hour
                     actual_interval = now - last_downloaded
                     nominal_interval = timedelta(days=sch)
                     if abs(actual_interval - nominal_interval) < \
                             timedelta(hours=1):
                         now = last_downloaded + nominal_interval
                 x.set('last_downloaded', isoformat(now))
                 break
         self.write_scheduler_file()
Exemple #15
0
def to_json(obj):
    import datetime
    if isinstance(obj, bytearray):
        from base64 import standard_b64encode
        return {'__class__': 'bytearray',
                '__value__': standard_b64encode(bytes(obj)).decode('ascii')}
    if isinstance(obj, datetime.datetime):
        from calibre.utils.date import isoformat
        return {'__class__': 'datetime.datetime',
                '__value__': isoformat(obj, as_utc=True)}
    if isinstance(obj, (set, frozenset)):
        return {'__class__': 'set', '__value__': tuple(obj)}
    if isinstance(obj, bytes):
        return obj.decode('utf-8')
    if hasattr(obj, 'toBase64'):  # QByteArray
        return {'__class__': 'bytearray',
                '__value__': bytes(obj.toBase64()).decode('ascii')}
    raise TypeError(repr(obj) + ' is not JSON serializable')
Exemple #16
0
 def schedule_recipe(self, recipe, schedule_type, schedule, last_downloaded=None):
     with self.lock:
         for x in list(self.iter_recipes()):
             if x.get('id', False) == recipe.get('id'):
                 ld = x.get('last_downloaded', None)
                 if ld and last_downloaded is None:
                     try:
                         last_downloaded = parse_date(ld)
                     except:
                         pass
                 self.root.remove(x)
                 break
         if last_downloaded is None:
             last_downloaded = fromordinal(1)
         sr = E.scheduled_recipe({
             'id' : recipe.get('id'),
             'title': recipe.get('title'),
             'last_downloaded':isoformat(last_downloaded),
             }, self.serialize_schedule(schedule_type, schedule))
         self.root.append(sr)
         self.write_scheduler_file()
Exemple #17
0
    def set_metadata(self, stream, mi, type_):
        from calibre_plugins.kfx_output.kfxlib import (set_logger, YJ_Book, YJ_Metadata)
        from calibre.ebooks import normalize as normalize_unicode
        from calibre.ebooks.metadata import author_to_author_sort
        from calibre.utils.config_base import tweaks
        from calibre.utils.date import (is_date_undefined, isoformat)
        from calibre.utils.logging import Log
        from calibre.utils.localization import (canonicalize_lang, lang_as_iso639_1)

        def mapped_author_to_author_sort(author):
            if hasattr(mi, "author_sort_map"):
                author_sort = mi.author_sort_map.get(author)    # use mapping if provided
                if author_sort:
                    return author_sort

            return author_to_author_sort(author)

        def normalize(s):
            if not isinstance(s, type("")):
                s = s.decode("utf8", "ignore")

            return normalize_unicode(s)

        log = set_logger(Log())

        filename = stream.name if hasattr(stream, "name") else "stream"
        log.info("KFX metadata writer activated for %s" % filename)

        try:
            from calibre.ebooks.conversion.config import load_defaults
            prefs = load_defaults('kfx_output')
        except Exception:
            prefs = {}
            log.info("Failed to read default KFX Output preferences")

        md = YJ_Metadata(author_sort_fn=mapped_author_to_author_sort)

        md.title = normalize(mi.title)

        md.authors = [normalize(author) for author in mi.authors]

        if mi.publisher:
            md.publisher = normalize(mi.publisher)

        if mi.pubdate and not is_date_undefined(mi.pubdate):
            md.issue_date = str(isoformat(mi.pubdate)[:10])

        if mi.comments:
            # Strip user annotations
            a_offset = mi.comments.find('<div class="user_annotations">')
            ad_offset = mi.comments.find('<hr class="annotations_divider" />')

            if a_offset >= 0:
                mi.comments = mi.comments[:a_offset]
            if ad_offset >= 0:
                mi.comments = mi.comments[:ad_offset]

            md.description = normalize(mi.comments)

        if not mi.is_null('language'):
            lang = canonicalize_lang(mi.language)
            lang = lang_as_iso639_1(lang) or lang
            if lang:
                md.language = normalize(lang)

        if mi.cover_data[1]:
            md.cover_image_data = mi.cover_data
        elif mi.cover:
            md.cover_image_data = ("jpg", open(mi.cover, 'rb').read())

        if not tweaks.get("kfx_output_ignore_asin_metadata", False):
            value = mi.identifiers.get("mobi-asin")
            if value is not None and re.match(ASIN_RE, value):
                md.asin = value
            else:
                for ident, value in mi.identifiers.items():
                    if ident.startswith("amazon") and re.match(ASIN_RE, value):
                        md.asin = value
                        break
                else:
                    value = mi.identifiers.get("asin")
                    if value is not None and re.match(ASIN_RE, value):
                        md.asin = value

        if md.asin:
            md.cde_content_type = "EBOK"

        if prefs.get("approximate_pages", False):
            page_count = 0
            number_of_pages_field = prefs.get("number_of_pages_field", AUTO_PAGES)
            if number_of_pages_field and number_of_pages_field != AUTO_PAGES:
                number_of_pages = mi.get(number_of_pages_field, "")
                try:
                    page_count = int(number_of_pages)
                except Exception:
                    pass
        else:
            page_count = -1

        book = YJ_Book(stream, log)
        book.decode_book(set_metadata=md, set_approximate_pages=page_count)
        new_data = book.convert_to_single_kfx()
        set_logger()

        stream.seek(0)
        stream.truncate()
        stream.write(new_data)
        stream.seek(0)
Exemple #18
0
    def upgrade_version_18(self):
        '''
        Add a library UUID.
        Add an identifiers table.
        Add a languages table.
        Add a last_modified column.
        NOTE: You cannot downgrade after this update, if you do
        any changes you make to book isbns will be lost.
        '''
        script = '''
        DROP TABLE IF EXISTS library_id;
        CREATE TABLE library_id ( id   INTEGER PRIMARY KEY,
                                  uuid TEXT NOT NULL,
                                  UNIQUE(uuid)
        );

        DROP TABLE IF EXISTS identifiers;
        CREATE TABLE identifiers  ( id     INTEGER PRIMARY KEY,
                                    book   INTEGER NON NULL,
                                    type   TEXT NON NULL DEFAULT "isbn" COLLATE NOCASE,
                                    val    TEXT NON NULL COLLATE NOCASE,
                                    UNIQUE(book, type)
        );

        DROP TABLE IF EXISTS languages;
        CREATE TABLE languages    ( id        INTEGER PRIMARY KEY,
                                    lang_code TEXT NON NULL COLLATE NOCASE,
                                    UNIQUE(lang_code)
        );

        DROP TABLE IF EXISTS books_languages_link;
        CREATE TABLE books_languages_link ( id INTEGER PRIMARY KEY,
                                            book INTEGER NOT NULL,
                                            lang_code INTEGER NOT NULL,
                                            item_order INTEGER NOT NULL DEFAULT 0,
                                            UNIQUE(book, lang_code)
        );

        DROP TRIGGER IF EXISTS fkc_delete_on_languages;
        CREATE TRIGGER fkc_delete_on_languages
        BEFORE DELETE ON languages
        BEGIN
            SELECT CASE
                WHEN (SELECT COUNT(id) FROM books_languages_link WHERE lang_code=OLD.id) > 0
                THEN RAISE(ABORT, 'Foreign key violation: language is still referenced')
            END;
        END;

        DROP TRIGGER IF EXISTS fkc_delete_on_languages_link;
        CREATE TRIGGER fkc_delete_on_languages_link
        BEFORE INSERT ON books_languages_link
        BEGIN
          SELECT CASE
              WHEN (SELECT id from books WHERE id=NEW.book) IS NULL
              THEN RAISE(ABORT, 'Foreign key violation: book not in books')
              WHEN (SELECT id from languages WHERE id=NEW.lang_code) IS NULL
              THEN RAISE(ABORT, 'Foreign key violation: lang_code not in languages')
          END;
        END;

        DROP TRIGGER IF EXISTS fkc_update_books_languages_link_a;
        CREATE TRIGGER fkc_update_books_languages_link_a
        BEFORE UPDATE OF book ON books_languages_link
        BEGIN
            SELECT CASE
                WHEN (SELECT id from books WHERE id=NEW.book) IS NULL
                THEN RAISE(ABORT, 'Foreign key violation: book not in books')
            END;
        END;
        DROP TRIGGER IF EXISTS fkc_update_books_languages_link_b;
        CREATE TRIGGER fkc_update_books_languages_link_b
        BEFORE UPDATE OF lang_code ON books_languages_link
        BEGIN
            SELECT CASE
                WHEN (SELECT id from languages WHERE id=NEW.lang_code) IS NULL
                THEN RAISE(ABORT, 'Foreign key violation: lang_code not in languages')
            END;
        END;

        DROP INDEX IF EXISTS books_languages_link_aidx;
        CREATE INDEX books_languages_link_aidx ON books_languages_link (lang_code);
        DROP INDEX IF EXISTS books_languages_link_bidx;
        CREATE INDEX books_languages_link_bidx ON books_languages_link (book);
        DROP INDEX IF EXISTS languages_idx;
        CREATE INDEX languages_idx ON languages (lang_code COLLATE NOCASE);

        DROP TRIGGER IF EXISTS books_delete_trg;
        CREATE TRIGGER books_delete_trg
            AFTER DELETE ON books
            BEGIN
                DELETE FROM books_authors_link WHERE book=OLD.id;
                DELETE FROM books_publishers_link WHERE book=OLD.id;
                DELETE FROM books_ratings_link WHERE book=OLD.id;
                DELETE FROM books_series_link WHERE book=OLD.id;
                DELETE FROM books_tags_link WHERE book=OLD.id;
                DELETE FROM books_languages_link WHERE book=OLD.id;
                DELETE FROM data WHERE book=OLD.id;
                DELETE FROM comments WHERE book=OLD.id;
                DELETE FROM conversion_options WHERE book=OLD.id;
                DELETE FROM books_plugin_data WHERE book=OLD.id;
                DELETE FROM identifiers WHERE book=OLD.id;
        END;

        INSERT INTO identifiers (book, val) SELECT id,isbn FROM books WHERE isbn;

        ALTER TABLE books ADD COLUMN last_modified TIMESTAMP NOT NULL DEFAULT "%s";

        ''' % isoformat(DEFAULT_DATE, sep=' ')
        # Sqlite does not support non constant default values in alter
        # statements
        self.conn.executescript(script)
Exemple #19
0
def sqlite_datetime(x):
    return isoformat(x, sep=' ') if isinstance(x, datetime) else x
Exemple #20
0
    def run(self, path_to_output, opts, db, notification=DummyReporter()):
        from calibre.library import current_library_name
        from calibre.utils.date import isoformat
        from calibre.utils.html2text import html2text
        from calibre.utils.logging import default_log as log
        from lxml import etree
        from calibre.ebooks.metadata import authors_to_string

        self.fmt = path_to_output.rpartition('.')[2]
        self.notification = notification
        current_library = current_library_name()
        if getattr(opts, 'library_path', None):
            current_library = os.path.basename(opts.library_path)

        if opts.verbose:
            opts_dict = vars(opts)
            log("%s('%s'): Generating %s" % (self.name, current_library, self.fmt.upper()))
            if opts.connected_device['is_device_connected']:
                log(" connected_device: %s" % opts.connected_device['name'])
            if opts_dict['search_text']:
                log(" --search='%s'" % opts_dict['search_text'])

            if opts_dict['ids']:
                log(" Book count: %d" % len(opts_dict['ids']))
                if opts_dict['search_text']:
                    log(" (--search ignored when a subset of the database is specified)")

            if opts_dict['fields']:
                if opts_dict['fields'] == 'all':
                    log(" Fields: %s" % ', '.join(FIELDS[1:]))
                else:
                    log(" Fields: %s" % opts_dict['fields'])

        # If a list of ids are provided, don't use search_text
        if opts.ids:
            opts.search_text = None

        data = self.search_sort_db(db, opts)

        if not len(data):
            log.error("\nNo matching database entries for search criteria '%s'" % opts.search_text)
            # raise SystemExit(1)

        # Get the requested output fields as a list
        fields = self.get_output_fields(db, opts)

        # If connected device, add 'On Device' values to data
        if opts.connected_device['is_device_connected'] and 'ondevice' in fields:
            for entry in data:
                entry['ondevice'] = db.catalog_plugin_on_device_temp_mapping[entry['id']]['ondevice']

        fm = {x: db.field_metadata.get(x, {}) for x in fields}

        if self.fmt == 'csv':
            outfile = codecs.open(path_to_output, 'w', 'utf8')

            # Write a UTF-8 BOM
            outfile.write('\xef\xbb\xbf')

            # Output the field headers
            outfile.write(u'%s\n' % u','.join(fields))

            # Output the entry fields
            for entry in data:
                outstr = []
                for field in fields:
                    if field.startswith('#'):
                        item = db.get_field(entry['id'], field, index_is_id=True)
                        if isinstance(item, (list, tuple)):
                            if fm.get(field, {}).get('display', {}).get('is_names', False):
                                item = ' & '.join(item)
                            else:
                                item = ', '.join(item)
                    elif field == 'library_name':
                        item = current_library
                    elif field == 'title_sort':
                        item = entry['sort']
                    else:
                        item = entry[field]

                    if item is None:
                        outstr.append('""')
                        continue
                    elif field == 'formats':
                        fmt_list = []
                        for format in item:
                            fmt_list.append(format.rpartition('.')[2].lower())
                        item = ', '.join(fmt_list)
                    elif field == 'authors':
                        item = authors_to_string(item)
                    elif field == 'tags':
                        item = ', '.join(item)
                    elif field == 'isbn':
                        # Could be 9, 10 or 13 digits, with hyphens, possibly ending in 'X'
                        item = u'%s' % re.sub(r'[^\dX-]', '', item)
                    elif fm.get(field, {}).get('datatype') == 'datetime':
                        item = isoformat(item, as_utc=False)
                    elif field == 'comments':
                        item = item.replace(u'\r\n', u' ')
                        item = item.replace(u'\n', u' ')
                    elif fm.get(field, {}).get('datatype', None) == 'rating' and item:
                        item = u'%.2g' % (item / 2.0)

                    # Convert HTML to markdown text
                    if type(item) is unicode:
                        opening_tag = re.search('<(\w+)(\x20|>)', item)
                        if opening_tag:
                            closing_tag = re.search('<\/%s>$' % opening_tag.group(1), item)
                            if closing_tag:
                                item = html2text(item)

                    outstr.append(u'"%s"' % unicode(item).replace('"', '""'))

                outfile.write(u','.join(outstr) + u'\n')
            outfile.close()

        elif self.fmt == 'xml':
            from lxml.builder import E

            root = E.calibredb()
            for r in data:
                record = E.record()
                root.append(record)

                for field in fields:
                    if field.startswith('#'):
                        val = db.get_field(r['id'], field, index_is_id=True)
                        if not isinstance(val, (str, unicode)):
                            val = unicode(val)
                        item = getattr(E, field.replace('#', '_'))(val)
                        record.append(item)

                for field in ('id', 'uuid', 'publisher', 'rating', 'size',
                              'isbn', 'ondevice', 'identifiers'):
                    if field in fields:
                        val = r[field]
                        if not val:
                            continue
                        if not isinstance(val, (str, unicode)):
                            if (fm.get(field, {}).get('datatype', None) ==
                                    'rating' and val):
                                val = u'%.2g' % (val / 2.0)
                            val = unicode(val)
                        item = getattr(E, field)(val)
                        record.append(item)

                if 'title' in fields:
                    title = E.title(r['title'], sort=r['sort'])
                    record.append(title)

                if 'authors' in fields:
                    aus = E.authors(sort=r['author_sort'])
                    for au in r['authors']:
                        aus.append(E.author(au))
                    record.append(aus)

                for field in ('timestamp', 'pubdate'):
                    if field in fields:
                        record.append(getattr(E, field)(isoformat(r[field], as_utc=False)))

                if 'tags' in fields and r['tags']:
                    tags = E.tags()
                    for tag in r['tags']:
                        tags.append(E.tag(tag))
                    record.append(tags)

                if 'comments' in fields and r['comments']:
                    record.append(E.comments(r['comments']))

                if 'series' in fields and r['series']:
                    record.append(E.series(r['series'],
                        index=str(r['series_index'])))

                if 'cover' in fields and r['cover']:
                    record.append(E.cover(r['cover'].replace(os.sep, '/')))

                if 'formats' in fields and r['formats']:
                    fmt = E.formats()
                    for f in r['formats']:
                        fmt.append(E.format(f.replace(os.sep, '/')))
                    record.append(fmt)

                if 'library_name' in fields:
                    record.append(E.library_name(current_library))

            with open(path_to_output, 'w') as f:
                f.write(etree.tostring(root, encoding='utf-8',
                    xml_declaration=True, pretty_print=True))
Exemple #21
0
    def upgrade_version_18(self):
        '''
        Add a library UUID.
        Add an identifiers table.
        Add a languages table.
        Add a last_modified column.
        NOTE: You cannot downgrade after this update, if you do
        any changes you make to book isbns will be lost.
        '''
        script = '''
        DROP TABLE IF EXISTS library_id;
        CREATE TABLE library_id ( id   INTEGER PRIMARY KEY,
                                  uuid TEXT NOT NULL,
                                  UNIQUE(uuid)
        );

        DROP TABLE IF EXISTS identifiers;
        CREATE TABLE identifiers  ( id     INTEGER PRIMARY KEY,
                                    book   INTEGER NOT NULL,
                                    type   TEXT NOT NULL DEFAULT "isbn" COLLATE NOCASE,
                                    val    TEXT NOT NULL COLLATE NOCASE,
                                    UNIQUE(book, type)
        );

        DROP TABLE IF EXISTS languages;
        CREATE TABLE languages    ( id        INTEGER PRIMARY KEY,
                                    lang_code TEXT NOT NULL COLLATE NOCASE,
                                    UNIQUE(lang_code)
        );

        DROP TABLE IF EXISTS books_languages_link;
        CREATE TABLE books_languages_link ( id INTEGER PRIMARY KEY,
                                            book INTEGER NOT NULL,
                                            lang_code INTEGER NOT NULL,
                                            item_order INTEGER NOT NULL DEFAULT 0,
                                            UNIQUE(book, lang_code)
        );

        DROP TRIGGER IF EXISTS fkc_delete_on_languages;
        CREATE TRIGGER fkc_delete_on_languages
        BEFORE DELETE ON languages
        BEGIN
            SELECT CASE
                WHEN (SELECT COUNT(id) FROM books_languages_link WHERE lang_code=OLD.id) > 0
                THEN RAISE(ABORT, 'Foreign key violation: language is still referenced')
            END;
        END;

        DROP TRIGGER IF EXISTS fkc_delete_on_languages_link;
        CREATE TRIGGER fkc_delete_on_languages_link
        BEFORE INSERT ON books_languages_link
        BEGIN
          SELECT CASE
              WHEN (SELECT id from books WHERE id=NEW.book) IS NULL
              THEN RAISE(ABORT, 'Foreign key violation: book not in books')
              WHEN (SELECT id from languages WHERE id=NEW.lang_code) IS NULL
              THEN RAISE(ABORT, 'Foreign key violation: lang_code not in languages')
          END;
        END;

        DROP TRIGGER IF EXISTS fkc_update_books_languages_link_a;
        CREATE TRIGGER fkc_update_books_languages_link_a
        BEFORE UPDATE OF book ON books_languages_link
        BEGIN
            SELECT CASE
                WHEN (SELECT id from books WHERE id=NEW.book) IS NULL
                THEN RAISE(ABORT, 'Foreign key violation: book not in books')
            END;
        END;
        DROP TRIGGER IF EXISTS fkc_update_books_languages_link_b;
        CREATE TRIGGER fkc_update_books_languages_link_b
        BEFORE UPDATE OF lang_code ON books_languages_link
        BEGIN
            SELECT CASE
                WHEN (SELECT id from languages WHERE id=NEW.lang_code) IS NULL
                THEN RAISE(ABORT, 'Foreign key violation: lang_code not in languages')
            END;
        END;

        DROP INDEX IF EXISTS books_languages_link_aidx;
        CREATE INDEX books_languages_link_aidx ON books_languages_link (lang_code);
        DROP INDEX IF EXISTS books_languages_link_bidx;
        CREATE INDEX books_languages_link_bidx ON books_languages_link (book);
        DROP INDEX IF EXISTS languages_idx;
        CREATE INDEX languages_idx ON languages (lang_code COLLATE NOCASE);

        DROP TRIGGER IF EXISTS books_delete_trg;
        CREATE TRIGGER books_delete_trg
            AFTER DELETE ON books
            BEGIN
                DELETE FROM books_authors_link WHERE book=OLD.id;
                DELETE FROM books_publishers_link WHERE book=OLD.id;
                DELETE FROM books_ratings_link WHERE book=OLD.id;
                DELETE FROM books_series_link WHERE book=OLD.id;
                DELETE FROM books_tags_link WHERE book=OLD.id;
                DELETE FROM books_languages_link WHERE book=OLD.id;
                DELETE FROM data WHERE book=OLD.id;
                DELETE FROM comments WHERE book=OLD.id;
                DELETE FROM conversion_options WHERE book=OLD.id;
                DELETE FROM books_plugin_data WHERE book=OLD.id;
                DELETE FROM identifiers WHERE book=OLD.id;
        END;

        INSERT INTO identifiers (book, val) SELECT id,isbn FROM books WHERE isbn;

        ALTER TABLE books ADD COLUMN last_modified TIMESTAMP NOT NULL DEFAULT "%s";

        '''%isoformat(DEFAULT_DATE, sep=' ')
        # Sqlite does not support non constant default values in alter
        # statements
        self.db.execute(script)
Exemple #22
0
def metadata_to_xmp_packet(mi):
    A = ElementMaker(namespace=NS_MAP["x"], nsmap=nsmap("x"))
    R = ElementMaker(namespace=NS_MAP["rdf"], nsmap=nsmap("rdf"))
    root = A.xmpmeta(R.RDF)
    rdf = root[0]
    dc = rdf.makeelement(expand("rdf:Description"), nsmap=nsmap("dc"))
    dc.set(expand("rdf:about"), "")
    rdf.append(dc)
    for prop, tag in {"title": "dc:title", "comments": "dc:description"}.iteritems():
        val = mi.get(prop) or ""
        create_alt_property(dc, tag, val)
    for prop, (tag, ordered) in {
        "authors": ("dc:creator", True),
        "tags": ("dc:subject", False),
        "publisher": ("dc:publisher", False),
    }.iteritems():
        val = mi.get(prop) or ()
        if isinstance(val, basestring):
            val = [val]
        create_sequence_property(dc, tag, val, ordered)
    if not mi.is_null("pubdate"):
        create_sequence_property(
            dc, "dc:date", [isoformat(mi.pubdate, as_utc=False)]
        )  # Adobe spec recommends local time
    if not mi.is_null("languages"):
        langs = filter(None, map(lambda x: lang_as_iso639_1(x) or canonicalize_lang(x), mi.languages))
        if langs:
            create_sequence_property(dc, "dc:language", langs, ordered=False)

    xmp = rdf.makeelement(expand("rdf:Description"), nsmap=nsmap("xmp", "xmpidq"))
    xmp.set(expand("rdf:about"), "")
    rdf.append(xmp)
    extra_ids = {}
    for x in ("prism", "pdfx"):
        p = extra_ids[x] = rdf.makeelement(expand("rdf:Description"), nsmap=nsmap(x))
        p.set(expand("rdf:about"), "")
        rdf.append(p)

    identifiers = mi.get_identifiers()
    if identifiers:
        create_identifiers(xmp, identifiers)
        for scheme, val in identifiers.iteritems():
            if scheme in {"isbn", "doi"}:
                for prefix, parent in extra_ids.iteritems():
                    ie = parent.makeelement(expand("%s:%s" % (prefix, scheme)))
                    ie.text = val
                    parent.append(ie)

    d = xmp.makeelement(expand("xmp:MetadataDate"))
    d.text = isoformat(now(), as_utc=False)
    xmp.append(d)

    calibre = rdf.makeelement(expand("rdf:Description"), nsmap=nsmap("calibre", "calibreSI", "calibreCC"))
    calibre.set(expand("rdf:about"), "")
    rdf.append(calibre)
    if not mi.is_null("rating"):
        try:
            r = float(mi.rating)
        except (TypeError, ValueError):
            pass
        else:
            create_simple_property(calibre, "calibre:rating", "%g" % r)
    if not mi.is_null("series"):
        create_series(calibre, mi.series, mi.series_index)
    if not mi.is_null("timestamp"):
        create_simple_property(calibre, "calibre:timestamp", isoformat(mi.timestamp, as_utc=False))
    for x in ("author_link_map", "user_categories"):
        val = getattr(mi, x, None)
        if val:
            create_simple_property(calibre, "calibre:" + x, dump_dict(val))

    for x in ("title_sort", "author_sort"):
        if not mi.is_null(x):
            create_simple_property(calibre, "calibre:" + x, getattr(mi, x))

    all_user_metadata = mi.get_all_user_metadata(True)
    if all_user_metadata:
        create_user_metadata(calibre, all_user_metadata)
    return serialize_xmp_packet(root)
Exemple #23
0
def sqlite_datetime(x):
    return isoformat(x, sep=' ') if isinstance(x, datetime) else x
Exemple #24
0
def book_to_json(ctx, rd, db, book_id,
                 get_category_urls=True, device_compatible=False, device_for_template=None):
    mi = db.get_metadata(book_id, get_cover=False)
    codec = JsonCodec(db.field_metadata)
    if not device_compatible:
        try:
            mi.rating = mi.rating/2.
        except Exception:
            mi.rating = 0.0
    data = codec.encode_book_metadata(mi)
    for x in ('publication_type', 'size', 'db_id', 'lpath', 'mime',
            'rights', 'book_producer'):
        data.pop(x, None)

    get = partial(ctx.url_for, get_content, book_id=book_id, library_id=db.server_library_id)
    data['cover'] = get(what='cover')
    data['thumbnail'] = get(what='thumb')

    if not device_compatible:
        mi.format_metadata = {k.lower():dict(v) for k, v in
                mi.format_metadata.iteritems()}
        for v in mi.format_metadata.itervalues():
            mtime = v.get('mtime', None)
            if mtime is not None:
                v['mtime'] = isoformat(mtime, as_utc=True)
        data['format_metadata'] = mi.format_metadata
        fmts = set(x.lower() for x in mi.format_metadata.iterkeys())
        pf = prefs['output_format'].lower()
        other_fmts = list(fmts)
        try:
            fmt = pf if pf in fmts else other_fmts[0]
        except:
            fmt = None
        if fmts and fmt:
            other_fmts = [x for x in fmts if x != fmt]
        data['formats'] = sorted(fmts)
        if fmt:
            data['main_format'] = {fmt:get(what=fmt)}
        else:
            data['main_format'] = None
        data['other_formats'] = {fmt:get(what=fmt) for fmt in other_fmts}

        if get_category_urls:
            category_urls = data['category_urls'] = {}
            all_cats = ctx.get_categories(rd, db)
            for key in mi.all_field_keys():
                fm = mi.metadata_for_field(key)
                if (fm and fm['is_category'] and not fm['is_csp'] and
                        key != 'formats' and fm['datatype'] != 'rating'):
                    categories = mi.get(key) or []
                    if isinstance(categories, basestring):
                        categories = [categories]
                    category_urls[key] = dbtags = {}
                    for category in categories:
                        for tag in all_cats.get(key, ()):
                            if tag.original_name == category:
                                dbtags[category] = ctx.url_for(
                                    books_in,
                                    encoded_category=encode_name(tag.category if tag.category else key),
                                    encoded_item=encode_name(tag.original_name if tag.id is None else unicode(tag.id)),
                                    library_id=db.server_library_id
                                )
                                break
    else:
        series = data.get('series', None) or ''
        if series:
            tsorder = tweaks['save_template_title_series_sorting']
            series = title_sort(series, order=tsorder)
        data['_series_sort_'] = series
        if device_for_template:
            import posixpath
            from calibre.devices.utils import create_upload_path
            from calibre.utils.filenames import ascii_filename as sanitize
            from calibre.customize.ui import device_plugins

            for device_class in device_plugins():
                if device_class.__class__.__name__ == device_for_template:
                    template = device_class.save_template()
                    data['_filename_'] = create_upload_path(mi, book_id,
                            template, sanitize, path_type=posixpath)
                    break

    return data, mi.last_modified
Exemple #25
0
def do_list(db, fields, afields, sort_by, ascending, search_text, line_width, separator,
            prefix, subtitle='Books in the calibre database'):
    from calibre.constants import terminal_controller as tc
    terminal_controller = tc()
    if sort_by:
        db.sort(sort_by, ascending)
    if search_text:
        db.search(search_text)
    data = db.get_data_as_dict(prefix, authors_as_string=True)
    fields = ['id'] + fields
    title_fields = fields
    def field_name(f):
        ans = f
        if f[0] == '*':
            if f.endswith('_index'):
                fkey = f[1:-len('_index')]
                num = db.custom_column_label_map[fkey]['num']
                ans = '%d_index'%num
            else:
                ans = db.custom_column_label_map[f[1:]]['num']
        return ans
    fields = list(map(field_name, fields))

    for f in data:
        fmts = [x for x in f['formats'] if x is not None]
        f['formats'] = u'[%s]'%u','.join(fmts)
    widths = list(map(lambda x : 0, fields))
    for record in data:
        for f in record.keys():
            if hasattr(record[f], 'isoformat'):
                record[f] = isoformat(record[f], as_utc=False)
            else:
                record[f] = unicode(record[f])
            record[f] = record[f].replace('\n', ' ')
    for i in data:
        for j, field in enumerate(fields):
            widths[j] = max(widths[j], len(unicode(i[field])))

    screen_width = terminal_controller.COLS if line_width < 0 else line_width
    if not screen_width:
        screen_width = 80
    field_width = screen_width//len(fields)
    base_widths = map(lambda x: min(x+1, field_width), widths)

    while sum(base_widths) < screen_width:
        adjusted = False
        for i in range(len(widths)):
            if base_widths[i] < widths[i]:
                base_widths[i] += min(screen_width-sum(base_widths), widths[i]-base_widths[i])
                adjusted = True
                break
        if not adjusted:
            break

    widths = list(base_widths)
    titles = map(lambda x, y: '%-*s%s'%(x-len(separator), y, separator),
            widths, title_fields)
    print terminal_controller.GREEN + ''.join(titles)+terminal_controller.NORMAL

    wrappers = map(lambda x: TextWrapper(x-1), widths)
    o = cStringIO.StringIO()

    for record in data:
        text = [wrappers[i].wrap(unicode(record[field]).encode('utf-8')) for i, field in enumerate(fields)]
        lines = max(map(len, text))
        for l in range(lines):
            for i, field in enumerate(text):
                ft = text[i][l] if l < len(text[i]) else ''
                filler = '%*s'%(widths[i]-len(ft)-1, '')
                o.write(ft)
                o.write(filler+separator)
            print >>o
    return o.getvalue()
Exemple #26
0
def meta_info_to_oeb_metadata(mi, m, log, override_input_metadata=False):
    from calibre.ebooks.oeb.base import OPF
    if not mi.is_null('title'):
        m.clear('title')
        m.add('title', mi.title)
    if mi.title_sort:
        if not m.title:
            m.add('title', mi.title_sort)
        m.clear('title_sort')
        m.add('title_sort', mi.title_sort)
    if not mi.is_null('authors'):
        m.filter('creator', lambda x: x.role.lower() in ['aut', ''])
        for a in mi.authors:
            attrib = {'role': 'aut'}
            if mi.author_sort:
                attrib[OPF('file-as')] = mi.author_sort
            m.add('creator', a, attrib=attrib)
    if not mi.is_null('book_producer'):
        m.filter('contributor', lambda x: x.role.lower() == 'bkp')
        m.add('contributor', mi.book_producer, role='bkp')
    elif override_input_metadata:
        m.filter('contributor', lambda x: x.role.lower() == 'bkp')
    if not mi.is_null('comments'):
        m.clear('description')
        m.add('description', mi.comments)
    elif override_input_metadata:
        m.clear('description')
    if not mi.is_null('publisher'):
        m.clear('publisher')
        m.add('publisher', mi.publisher)
    elif override_input_metadata:
        m.clear('publisher')
    if not mi.is_null('series'):
        m.clear('series')
        m.add('series', mi.series)
    elif override_input_metadata:
        m.clear('series')
    identifiers = mi.get_identifiers()
    set_isbn = False
    for typ, val in identifiers.iteritems():
        has = False
        if typ.lower() == 'isbn':
            set_isbn = True
        for x in m.identifier:
            if x.scheme.lower() == typ.lower():
                x.content = val
                has = True
        if not has:
            m.add('identifier', val, scheme=typ.upper())
    if override_input_metadata and not set_isbn:
        m.filter('identifier', lambda x: x.scheme.lower() == 'isbn')
    if not mi.is_null('languages'):
        m.clear('language')
        for lang in mi.languages:
            if lang and lang.lower() not in ('und', ''):
                m.add('language', lang)
    if not mi.is_null('series_index'):
        m.clear('series_index')
        m.add('series_index', mi.format_series_index())
    elif override_input_metadata:
        m.clear('series_index')
    if not mi.is_null('rating'):
        m.clear('rating')
        m.add('rating', '%.2f' % mi.rating)
    elif override_input_metadata:
        m.clear('rating')
    if not mi.is_null('tags'):
        m.clear('subject')
        for t in mi.tags:
            m.add('subject', t)
    elif override_input_metadata:
        m.clear('subject')
    if not mi.is_null('pubdate'):
        m.clear('date')
        m.add('date', isoformat(mi.pubdate))
    if not mi.is_null('timestamp'):
        m.clear('timestamp')
        m.add('timestamp', isoformat(mi.timestamp))
    if not mi.is_null('rights'):
        m.clear('rights')
        m.add('rights', mi.rights)
    if not mi.is_null('publication_type'):
        m.clear('publication_type')
        m.add('publication_type', mi.publication_type)

    if not m.timestamp:
        m.add('timestamp', isoformat(now()))
Exemple #27
0
    def run(self, path_to_output, opts, db, notification=DummyReporter()):
        from calibre.library import current_library_name
        from calibre.utils.date import isoformat
        from calibre.utils.html2text import html2text
        from calibre.utils.logging import default_log as log
        from lxml import etree
        from calibre.ebooks.metadata import authors_to_string

        self.fmt = path_to_output.rpartition('.')[2]
        self.notification = notification
        current_library = current_library_name()
        if getattr(opts, 'library_path', None):
            current_library = os.path.basename(opts.library_path)

        if opts.verbose:
            opts_dict = vars(opts)
            log("%s('%s'): Generating %s" % (self.name, current_library, self.fmt.upper()))
            if opts.connected_device['is_device_connected']:
                log(" connected_device: %s" % opts.connected_device['name'])
            if opts_dict['search_text']:
                log(" --search='%s'" % opts_dict['search_text'])

            if opts_dict['ids']:
                log(" Book count: %d" % len(opts_dict['ids']))
                if opts_dict['search_text']:
                    log(" (--search ignored when a subset of the database is specified)")

            if opts_dict['fields']:
                if opts_dict['fields'] == 'all':
                    log(" Fields: %s" % ', '.join(FIELDS[1:]))
                else:
                    log(" Fields: %s" % opts_dict['fields'])

        # If a list of ids are provided, don't use search_text
        if opts.ids:
            opts.search_text = None

        data = self.search_sort_db(db, opts)

        if not len(data):
            log.error("\nNo matching database entries for search criteria '%s'" % opts.search_text)
            # raise SystemExit(1)

        # Get the requested output fields as a list
        fields = self.get_output_fields(db, opts)

        # If connected device, add 'On Device' values to data
        if opts.connected_device['is_device_connected'] and 'ondevice' in fields:
            for entry in data:
                entry['ondevice'] = db.catalog_plugin_on_device_temp_mapping[entry['id']]['ondevice']

        fm = {x: db.field_metadata.get(x, {}) for x in fields}

        if self.fmt == 'csv':
            outfile = codecs.open(path_to_output, 'w', 'utf8')

            # Write a UTF-8 BOM
            outfile.write('\xef\xbb\xbf')

            # Output the field headers
            outfile.write(u'%s\n' % u','.join(fields))

            # Output the entry fields
            for entry in data:
                outstr = []
                for field in fields:
                    if field.startswith('#'):
                        item = db.get_field(entry['id'], field, index_is_id=True)
                        if isinstance(item, (list, tuple)):
                            if fm.get(field, {}).get('display', {}).get('is_names', False):
                                item = ' & '.join(item)
                            else:
                                item = ', '.join(item)
                    elif field == 'library_name':
                        item = current_library
                    elif field == 'title_sort':
                        item = entry['sort']
                    else:
                        item = entry[field]

                    if item is None:
                        outstr.append('""')
                        continue
                    elif field == 'formats':
                        fmt_list = []
                        for format in item:
                            fmt_list.append(format.rpartition('.')[2].lower())
                        item = ', '.join(fmt_list)
                    elif field == 'authors':
                        item = authors_to_string(item)
                    elif field == 'tags':
                        item = ', '.join(item)
                    elif field == 'isbn':
                        # Could be 9, 10 or 13 digits, with hyphens, possibly ending in 'X'
                        item = u'%s' % re.sub(r'[^\dX-]', '', item)
                    elif fm.get(field, {}).get('datatype') == 'datetime':
                        item = isoformat(item, as_utc=False)
                    elif field == 'comments':
                        item = item.replace(u'\r\n', u' ')
                        item = item.replace(u'\n', u' ')
                    elif fm.get(field, {}).get('datatype', None) == 'rating' and item:
                        item = u'%.2g' % (item / 2.0)

                    # Convert HTML to markdown text
                    if isinstance(item, unicode_type):
                        opening_tag = re.search('<(\\w+)(\x20|>)', item)
                        if opening_tag:
                            closing_tag = re.search('<\\/%s>$' % opening_tag.group(1), item)
                            if closing_tag:
                                item = html2text(item)

                    outstr.append(u'"%s"' % unicode_type(item).replace('"', '""'))

                outfile.write(u','.join(outstr) + u'\n')
            outfile.close()

        elif self.fmt == 'xml':
            from lxml.builder import E

            root = E.calibredb()
            for r in data:
                record = E.record()
                root.append(record)

                for field in fields:
                    if field.startswith('#'):
                        val = db.get_field(r['id'], field, index_is_id=True)
                        if not isinstance(val, unicode_type):
                            val = unicode_type(val)
                        item = getattr(E, field.replace('#', '_'))(val)
                        record.append(item)

                for field in ('id', 'uuid', 'publisher', 'rating', 'size',
                              'isbn', 'ondevice', 'identifiers'):
                    if field in fields:
                        val = r[field]
                        if not val:
                            continue
                        if not isinstance(val, (bytes, unicode_type)):
                            if (fm.get(field, {}).get('datatype', None) ==
                                    'rating' and val):
                                val = u'%.2g' % (val / 2.0)
                            val = unicode_type(val)
                        item = getattr(E, field)(val)
                        record.append(item)

                if 'title' in fields:
                    title = E.title(r['title'], sort=r['sort'])
                    record.append(title)

                if 'authors' in fields:
                    aus = E.authors(sort=r['author_sort'])
                    for au in r['authors']:
                        aus.append(E.author(au))
                    record.append(aus)

                for field in ('timestamp', 'pubdate'):
                    if field in fields:
                        record.append(getattr(E, field)(isoformat(r[field], as_utc=False)))

                if 'tags' in fields and r['tags']:
                    tags = E.tags()
                    for tag in r['tags']:
                        tags.append(E.tag(tag))
                    record.append(tags)

                if 'comments' in fields and r['comments']:
                    record.append(E.comments(r['comments']))

                if 'series' in fields and r['series']:
                    record.append(E.series(r['series'],
                        index=str(r['series_index'])))

                if 'cover' in fields and r['cover']:
                    record.append(E.cover(r['cover'].replace(os.sep, '/')))

                if 'formats' in fields and r['formats']:
                    fmt = E.formats()
                    for f in r['formats']:
                        fmt.append(E.format(f.replace(os.sep, '/')))
                    record.append(fmt)

                if 'library_name' in fields:
                    record.append(E.library_name(current_library))

            with open(path_to_output, 'w') as f:
                f.write(etree.tostring(root, encoding='utf-8',
                    xml_declaration=True, pretty_print=True))
Exemple #28
0
def meta_info_to_oeb_metadata(mi, m, log, override_input_metadata=False):
    from calibre.ebooks.oeb.base import OPF
    if not mi.is_null('title'):
        m.clear('title')
        m.add('title', mi.title)
    if mi.title_sort:
        if not m.title:
            m.add('title', mi.title_sort)
        m.clear('title_sort')
        m.add('title_sort', mi.title_sort)
    if not mi.is_null('authors'):
        m.filter('creator', lambda x : x.role.lower() in ['aut', ''])
        for a in mi.authors:
            attrib = {'role':'aut'}
            if mi.author_sort:
                attrib[OPF('file-as')] = mi.author_sort
            m.add('creator', a, attrib=attrib)
    if not mi.is_null('book_producer'):
        m.filter('contributor', lambda x : x.role.lower() == 'bkp')
        m.add('contributor', mi.book_producer, role='bkp')
    elif override_input_metadata:
        m.filter('contributor', lambda x : x.role.lower() == 'bkp')
    if not mi.is_null('comments'):
        m.clear('description')
        m.add('description', mi.comments)
    elif override_input_metadata:
        m.clear('description')
    if not mi.is_null('publisher'):
        m.clear('publisher')
        m.add('publisher', mi.publisher)
    elif override_input_metadata:
        m.clear('publisher')
    if not mi.is_null('series'):
        m.clear('series')
        m.add('series', mi.series)
    elif override_input_metadata:
        m.clear('series')
    identifiers = mi.get_identifiers()
    set_isbn = False
    for typ, val in identifiers.iteritems():
        has = False
        if typ.lower() == 'isbn':
            set_isbn = True
        for x in m.identifier:
            if x.scheme.lower() == typ.lower():
                x.content = val
                has = True
        if not has:
            m.add('identifier', val, scheme=typ.upper())
    if override_input_metadata and not set_isbn:
        m.filter('identifier', lambda x: x.scheme.lower() == 'isbn')
    if not mi.is_null('languages'):
        m.clear('language')
        for lang in mi.languages:
            if lang and lang.lower() not in ('und', ''):
                m.add('language', lang)
    if not mi.is_null('series_index'):
        m.clear('series_index')
        m.add('series_index', mi.format_series_index())
    elif override_input_metadata:
        m.clear('series_index')
    if not mi.is_null('rating'):
        m.clear('rating')
        m.add('rating', '%.2f'%mi.rating)
    elif override_input_metadata:
        m.clear('rating')
    if not mi.is_null('tags'):
        m.clear('subject')
        for t in mi.tags:
            m.add('subject', t)
    elif override_input_metadata:
        m.clear('subject')
    if not mi.is_null('pubdate'):
        m.clear('date')
        m.add('date', isoformat(mi.pubdate))
    if not mi.is_null('timestamp'):
        m.clear('timestamp')
        m.add('timestamp', isoformat(mi.timestamp))
    if not mi.is_null('rights'):
        m.clear('rights')
        m.add('rights', mi.rights)
    if not mi.is_null('publication_type'):
        m.clear('publication_type')
        m.add('publication_type', mi.publication_type)

    if not m.timestamp:
        m.add('timestamp', isoformat(now()))
Exemple #29
0
    def ajax_book_to_json(self, book_id, get_category_urls=True,
                          device_compatible=False, device_for_template=None):
        mi = self.db.get_metadata(book_id, index_is_id=True)

        if not device_compatible:
            try:
                mi.rating = mi.rating/2.
            except:
                mi.rating = 0.0

        data = self.ajax_json_codec.encode_book_metadata(mi)
        for x in ('publication_type', 'size', 'db_id', 'lpath', 'mime',
                'rights', 'book_producer'):
            data.pop(x, None)

        data['cover'] = absurl(self.opts.url_prefix, u'/get/cover/%d'%book_id)
        data['thumbnail'] = absurl(self.opts.url_prefix, u'/get/thumb/%d'%book_id)

        if not device_compatible:
            mi.format_metadata = {k.lower():dict(v) for k, v in
                    mi.format_metadata.iteritems()}
            for v in mi.format_metadata.itervalues():
                mtime = v.get('mtime', None)
                if mtime is not None:
                    v['mtime'] = isoformat(mtime, as_utc=True)
            data['format_metadata'] = mi.format_metadata
            fmts = set(x.lower() for x in mi.format_metadata.iterkeys())
            pf = prefs['output_format'].lower()
            other_fmts = list(fmts)
            try:
                fmt = pf if pf in fmts else other_fmts[0]
            except:
                fmt = None
            if fmts and fmt:
                other_fmts = [x for x in fmts if x != fmt]
            data['formats'] = sorted(fmts)
            if fmt:
                data['main_format'] = {fmt: absurl(self.opts.url_prefix, u'/get/%s/%d'%(fmt, book_id))}
            else:
                data['main_format'] = None
            data['other_formats'] = {fmt: absurl(self.opts.url_prefix, u'/get/%s/%d'%(fmt, book_id)) for fmt
                    in other_fmts}

            if get_category_urls:
                category_urls = data['category_urls'] = {}
                ccache = self.categories_cache()
                for key in mi.all_field_keys():
                    fm = mi.metadata_for_field(key)
                    if (fm and fm['is_category'] and not fm['is_csp'] and
                            key != 'formats' and fm['datatype'] not in ['rating']):
                        categories = mi.get(key)
                        if isinstance(categories, basestring):
                            categories = [categories]
                        if categories is None:
                            categories = []
                        dbtags = {}
                        for category in categories:
                            for tag in ccache.get(key, []):
                                if tag.original_name == category:
                                    dbtags[category] = books_in_url(self.opts.url_prefix,
                                        tag.category if tag.category else key,
                                        tag.original_name if tag.id is None else
                                        unicode(tag.id))
                                    break
                        category_urls[key] = dbtags
        else:
            series = data.get('series', None)
            if series:
                tsorder = tweaks['save_template_title_series_sorting']
                series = title_sort(series, order=tsorder)
            else:
                series = ''
            data['_series_sort_'] = series
            if device_for_template:
                import posixpath
                from calibre.devices.utils import create_upload_path
                from calibre.utils.filenames import ascii_filename as sanitize
                from calibre.customize.ui import device_plugins

                for device_class in device_plugins():
                    if device_class.__class__.__name__ == device_for_template:
                        template = device_class.save_template()
                        data['_filename_'] = create_upload_path(mi, book_id,
                                template, sanitize, path_type=posixpath)
                        break

        return data, mi.last_modified
Exemple #30
0
        def create_bibtex_entry(entry, fields, mode, template_citation,
                                    bibtexdict, db, citation_bibtex=True, calibre_files=True):

            #Bibtex doesn't like UTF-8 but keep unicode until writing
            #Define starting chain or if book valid strict and not book return a Fail string

            bibtex_entry = []
            if mode != "misc" and check_entry_book_valid(entry) :
                bibtex_entry.append(u'@book{')
            elif mode != "book" :
                bibtex_entry.append(u'@misc{')
            else :
                #case strict book
                return ''

            if citation_bibtex :
                # Citation tag
                bibtex_entry.append(make_bibtex_citation(entry, template_citation,
                    bibtexdict))
                bibtex_entry = [u' '.join(bibtex_entry)]

            for field in fields:
                if field.startswith('#'):
                    item = db.get_field(entry['id'],field,index_is_id=True)
                    if isinstance(item, (bool, float, int)):
                        item = repr(item)
                elif field == 'title_sort':
                    item = entry['sort']
                elif field == 'library_name':
                    item = library_name
                else:
                    item = entry[field]

                #check if the field should be included (none or empty)
                if item is None:
                    continue
                try:
                    if len(item) == 0 :
                        continue
                except TypeError:
                    pass

                if field == 'authors' :
                    bibtex_entry.append(u'author = "%s"' % bibtexdict.bibtex_author_format(item))

                elif field == 'id' :
                    bibtex_entry.append(u'calibreid = "%s"' % int(item))

                elif field == 'rating' :
                    bibtex_entry.append(u'rating = "%s"' % int(item))

                elif field == 'size' :
                    bibtex_entry.append(u'%s = "%s octets"' % (field, int(item)))

                elif field == 'tags' :
                    #A list to flatten
                    bibtex_entry.append(u'tags = "%s"' % bibtexdict.utf8ToBibtex(u', '.join(item)))

                elif field == 'comments' :
                    #\n removal
                    item = item.replace(u'\r\n',u' ')
                    item = item.replace(u'\n',u' ')
                    # unmatched brace removal (users should use \leftbrace or \rightbrace for single braces)
                    item = bibtexdict.stripUnmatchedSyntax(item, u'{', u'}')
                    #html to text
                    try:
                        item = html2text(item)
                    except:
                        log.warn("Failed to convert comments to text")
                    bibtex_entry.append(u'note = "%s"' % bibtexdict.utf8ToBibtex(item))

                elif field == 'isbn' :
                    # Could be 9, 10 or 13 digits
                    bibtex_entry.append(u'isbn = "%s"' % format_isbn(item))

                elif field == 'formats' :
                    #Add file path if format is selected
                    formats = [format.rpartition('.')[2].lower() for format in item]
                    bibtex_entry.append(u'formats = "%s"' % u', '.join(formats))
                    if calibre_files:
                        files = [u':%s:%s' % (format, format.rpartition('.')[2].upper())\
                            for format in item]
                        bibtex_entry.append(u'file = "%s"' % u', '.join(files))

                elif field == 'series_index' :
                    bibtex_entry.append(u'volume = "%s"' % int(item))

                elif field == 'timestamp' :
                    bibtex_entry.append(u'timestamp = "%s"' % isoformat(item).partition('T')[0])

                elif field == 'pubdate' :
                    bibtex_entry.append(u'year = "%s"' % item.year)
                    bibtex_entry.append(u'month = "%s"' % bibtexdict.utf8ToBibtex(strftime("%b", item)))

                elif field.startswith('#') and isinstance(item, basestring):
                    bibtex_entry.append(u'custom_%s = "%s"' % (field[1:],
                        bibtexdict.utf8ToBibtex(item)))

                elif isinstance(item, basestring):
                    # elif field in ['title', 'publisher', 'cover', 'uuid', 'ondevice',
                        # 'author_sort', 'series', 'title_sort'] :
                    bibtex_entry.append(u'%s = "%s"' % (field, bibtexdict.utf8ToBibtex(item)))

            bibtex_entry = u',\n    '.join(bibtex_entry)
            bibtex_entry += u' }\n\n'

            return bibtex_entry
Exemple #31
0
    def ajax_book_to_json(self, book_id, get_category_urls=True):
        mi = self.db.get_metadata(book_id, index_is_id=True)
        try:
            mi.rating = mi.rating/2.
        except:
            mi.rating = 0.0
        data = self.ajax_json_codec.encode_book_metadata(mi)
        for x in ('publication_type', 'size', 'db_id', 'lpath', 'mime',
                'rights', 'book_producer'):
            data.pop(x, None)

        data['cover'] = absurl(self.opts.url_prefix, u'/get/cover/%d'%book_id)
        data['thumbnail'] = absurl(self.opts.url_prefix, u'/get/thumb/%d'%book_id)
        mi.format_metadata = {k.lower():dict(v) for k, v in
                mi.format_metadata.iteritems()}
        for v in mi.format_metadata.itervalues():
            mtime = v.get('mtime', None)
            if mtime is not None:
                v['mtime'] = isoformat(mtime, as_utc=True)
        data['format_metadata'] = mi.format_metadata
        fmts = set(x.lower() for x in mi.format_metadata.iterkeys())
        pf = prefs['output_format'].lower()
        other_fmts = list(fmts)
        try:
            fmt = pf if pf in fmts else other_fmts[0]
        except:
            fmt = None
        if fmts and fmt:
            other_fmts = [x for x in fmts if x != fmt]
        data['formats'] = sorted(fmts)
        if fmt:
            data['main_format'] = {fmt: absurl(self.opts.url_prefix, u'/get/%s/%d'%(fmt, book_id))}
        else:
            data['main_format'] = None
        data['other_formats'] = {fmt: absurl(self.opts.url_prefix, u'/get/%s/%d'%(fmt, book_id)) for fmt
                in other_fmts}

        if get_category_urls:
            category_urls = data['category_urls'] = {}
            ccache = self.categories_cache()
            for key in mi.all_field_keys():
                fm = mi.metadata_for_field(key)
                if (fm and fm['is_category'] and not fm['is_csp'] and
                        key != 'formats' and fm['datatype'] not in ['rating']):
                    categories = mi.get(key)
                    if isinstance(categories, basestring):
                        categories = [categories]
                    if categories is None:
                        categories = []
                    dbtags = {}
                    for category in categories:
                        for tag in ccache.get(key, []):
                            if tag.original_name == category:
                                dbtags[category] = books_in_url(self.opts.url_prefix,
                                    tag.category if tag.category else key,
                                    tag.original_name if tag.id is None else
                                    unicode(tag.id))
                                break
                    category_urls[key] = dbtags

        return data, mi.last_modified
Exemple #32
0
def metadata_to_xmp_packet(mi):
    A = ElementMaker(namespace=NS_MAP['x'], nsmap=nsmap('x'))
    R = ElementMaker(namespace=NS_MAP['rdf'], nsmap=nsmap('rdf'))
    root = A.xmpmeta(R.RDF)
    rdf = root[0]
    dc = rdf.makeelement(expand('rdf:Description'), nsmap=nsmap('dc'))
    dc.set(expand('rdf:about'), '')
    rdf.append(dc)
    for prop, tag in iteritems({
            'title': 'dc:title',
            'comments': 'dc:description'
    }):
        val = mi.get(prop) or ''
        create_alt_property(dc, tag, val)
    for prop, (tag, ordered) in iteritems({
            'authors': ('dc:creator', True),
            'tags': ('dc:subject', False),
            'publisher': ('dc:publisher', False),
    }):
        val = mi.get(prop) or ()
        if isinstance(val, string_or_bytes):
            val = [val]
        create_sequence_property(dc, tag, val, ordered)
    if not mi.is_null('pubdate'):
        create_sequence_property(dc, 'dc:date',
                                 [isoformat(mi.pubdate, as_utc=False)
                                  ])  # Adobe spec recommends local time
    if not mi.is_null('languages'):
        langs = list(
            filter(
                None,
                map(lambda x: lang_as_iso639_1(x) or canonicalize_lang(x),
                    mi.languages)))
        if langs:
            create_sequence_property(dc, 'dc:language', langs, ordered=False)

    xmp = rdf.makeelement(expand('rdf:Description'),
                          nsmap=nsmap('xmp', 'xmpidq'))
    xmp.set(expand('rdf:about'), '')
    rdf.append(xmp)
    extra_ids = {}
    for x in ('prism', 'pdfx'):
        p = extra_ids[x] = rdf.makeelement(expand('rdf:Description'),
                                           nsmap=nsmap(x))
        p.set(expand('rdf:about'), '')
        rdf.append(p)

    identifiers = mi.get_identifiers()
    if identifiers:
        create_identifiers(xmp, identifiers)
        for scheme, val in iteritems(identifiers):
            if scheme in {'isbn', 'doi'}:
                for prefix, parent in iteritems(extra_ids):
                    ie = parent.makeelement(expand('%s:%s' % (prefix, scheme)))
                    ie.text = val
                    parent.append(ie)

    d = xmp.makeelement(expand('xmp:MetadataDate'))
    d.text = isoformat(now(), as_utc=False)
    xmp.append(d)

    calibre = rdf.makeelement(expand('rdf:Description'),
                              nsmap=nsmap('calibre', 'calibreSI', 'calibreCC'))
    calibre.set(expand('rdf:about'), '')
    rdf.append(calibre)
    if not mi.is_null('rating'):
        try:
            r = float(mi.rating)
        except (TypeError, ValueError):
            pass
        else:
            create_simple_property(calibre, 'calibre:rating', '%g' % r)
    if not mi.is_null('series'):
        create_series(calibre, mi.series, mi.series_index)
    if not mi.is_null('timestamp'):
        create_simple_property(calibre, 'calibre:timestamp',
                               isoformat(mi.timestamp, as_utc=False))
    for x in ('author_link_map', 'user_categories'):
        val = getattr(mi, x, None)
        if val:
            create_simple_property(calibre, 'calibre:' + x, dump_dict(val))

    for x in ('title_sort', 'author_sort'):
        if not mi.is_null(x):
            create_simple_property(calibre, 'calibre:' + x, getattr(mi, x))

    all_user_metadata = mi.get_all_user_metadata(True)
    if all_user_metadata:
        create_user_metadata(calibre, all_user_metadata)
    return serialize_xmp_packet(root)
Exemple #33
0
def adapt_datetime(dt):
    return isoformat(dt, sep=' ')
Exemple #34
0
    def ajax_book_to_json(self, book_id, get_category_urls=True,
                          device_compatible=False):
        mi = self.db.get_metadata(book_id, index_is_id=True)

        if not device_compatible:
            try:
                mi.rating = mi.rating/2.
            except:
                mi.rating = 0.0

        data = self.ajax_json_codec.encode_book_metadata(mi)
        for x in ('publication_type', 'size', 'db_id', 'lpath', 'mime',
                'rights', 'book_producer'):
            data.pop(x, None)

        data['cover'] = absurl(self.opts.url_prefix, u'/get/cover/%d'%book_id)
        data['thumbnail'] = absurl(self.opts.url_prefix, u'/get/thumb/%d'%book_id)

        if not device_compatible:
            mi.format_metadata = {k.lower():dict(v) for k, v in
                    mi.format_metadata.iteritems()}
            for v in mi.format_metadata.itervalues():
                mtime = v.get('mtime', None)
                if mtime is not None:
                    v['mtime'] = isoformat(mtime, as_utc=True)
            data['format_metadata'] = mi.format_metadata
            fmts = set(x.lower() for x in mi.format_metadata.iterkeys())
            pf = prefs['output_format'].lower()
            other_fmts = list(fmts)
            try:
                fmt = pf if pf in fmts else other_fmts[0]
            except:
                fmt = None
            if fmts and fmt:
                other_fmts = [x for x in fmts if x != fmt]
            data['formats'] = sorted(fmts)
            if fmt:
                data['main_format'] = {fmt: absurl(self.opts.url_prefix, u'/get/%s/%d'%(fmt, book_id))}
            else:
                data['main_format'] = None
            data['other_formats'] = {fmt: absurl(self.opts.url_prefix, u'/get/%s/%d'%(fmt, book_id)) for fmt
                    in other_fmts}

            if get_category_urls:
                category_urls = data['category_urls'] = {}
                ccache = self.categories_cache()
                for key in mi.all_field_keys():
                    fm = mi.metadata_for_field(key)
                    if (fm and fm['is_category'] and not fm['is_csp'] and
                            key != 'formats' and fm['datatype'] not in ['rating']):
                        categories = mi.get(key)
                        if isinstance(categories, basestring):
                            categories = [categories]
                        if categories is None:
                            categories = []
                        dbtags = {}
                        for category in categories:
                            for tag in ccache.get(key, []):
                                if tag.original_name == category:
                                    dbtags[category] = books_in_url(self.opts.url_prefix,
                                        tag.category if tag.category else key,
                                        tag.original_name if tag.id is None else
                                        unicode(tag.id))
                                    break
                        category_urls[key] = dbtags
        else:
            series = data.get('series', None)
            if series:
                tsorder = tweaks['save_template_title_series_sorting']
                series = title_sort(series, order=tsorder)
            else:
                series = ''
            data['_series_sort_'] = series

        return data, mi.last_modified
Exemple #35
0
def do_list(db, fields, afields, sort_by, ascending, search_text, line_width, separator,
            prefix, subtitle='Books in the calibre database'):
    from calibre.constants import terminal_controller as tc
    terminal_controller = tc()
    if sort_by:
        db.sort(sort_by, ascending)
    if search_text:
        db.search(search_text)
    data = db.get_data_as_dict(prefix, authors_as_string=True)
    fields = ['id'] + fields
    title_fields = fields
    def field_name(f):
        ans = f
        if f[0] == '*':
            if f.endswith('_index'):
                fkey = f[1:-len('_index')]
                num = db.custom_column_label_map[fkey]['num']
                ans = '%d_index'%num
            else:
                ans = db.custom_column_label_map[f[1:]]['num']
        return ans
    fields = list(map(field_name, fields))

    for f in data:
        fmts = [x for x in f['formats'] if x is not None]
        f['formats'] = u'[%s]'%u','.join(fmts)
    widths = list(map(lambda x : 0, fields))
    for record in data:
        for f in record.keys():
            if hasattr(record[f], 'isoformat'):
                record[f] = isoformat(record[f], as_utc=False)
            else:
                record[f] = unicode(record[f])
            record[f] = record[f].replace('\n', ' ')
    for i in data:
        for j, field in enumerate(fields):
            widths[j] = max(widths[j], len(unicode(i[field])))

    screen_width = terminal_controller.COLS if line_width < 0 else line_width
    if not screen_width:
        screen_width = 80
    field_width = screen_width//len(fields)
    base_widths = map(lambda x: min(x+1, field_width), widths)

    while sum(base_widths) < screen_width:
        adjusted = False
        for i in range(len(widths)):
            if base_widths[i] < widths[i]:
                base_widths[i] += min(screen_width-sum(base_widths), widths[i]-base_widths[i])
                adjusted = True
                break
        if not adjusted:
            break

    widths = list(base_widths)
    titles = map(lambda x, y: '%-*s%s'%(x-len(separator), y, separator),
            widths, title_fields)
    print terminal_controller.GREEN + ''.join(titles)+terminal_controller.NORMAL

    wrappers = map(lambda x: TextWrapper(x-1), widths)
    o = cStringIO.StringIO()

    for record in data:
        text = [wrappers[i].wrap(unicode(record[field]).encode('utf-8')) for i, field in enumerate(fields)]
        lines = max(map(len, text))
        for l in range(lines):
            for i, field in enumerate(text):
                ft = text[i][l] if l < len(text[i]) else ''
                filler = '%*s'%(widths[i]-len(ft)-1, '')
                o.write(ft)
                o.write(filler+separator)
            print >>o
    return o.getvalue()
Exemple #36
0
def metadata_to_xmp_packet(mi):
    A = ElementMaker(namespace=NS_MAP['x'], nsmap=nsmap('x'))
    R = ElementMaker(namespace=NS_MAP['rdf'], nsmap=nsmap('rdf'))
    root = A.xmpmeta(R.RDF)
    rdf = root[0]
    dc = rdf.makeelement(expand('rdf:Description'), nsmap=nsmap('dc'))
    dc.set(expand('rdf:about'), '')
    rdf.append(dc)
    for prop, tag in {'title':'dc:title', 'comments':'dc:description'}.iteritems():
        val = mi.get(prop) or ''
        create_alt_property(dc, tag, val)
    for prop, (tag, ordered) in {
        'authors':('dc:creator', True), 'tags':('dc:subject', False), 'publisher':('dc:publisher', False),
    }.iteritems():
        val = mi.get(prop) or ()
        if isinstance(val, basestring):
            val = [val]
        create_sequence_property(dc, tag, val, ordered)
    if not mi.is_null('pubdate'):
        create_sequence_property(dc, 'dc:date', [isoformat(mi.pubdate, as_utc=False)])  # Adobe spec recommends local time
    if not mi.is_null('languages'):
        langs = filter(None, map(lambda x:lang_as_iso639_1(x) or canonicalize_lang(x), mi.languages))
        if langs:
            create_sequence_property(dc, 'dc:language', langs, ordered=False)

    xmp = rdf.makeelement(expand('rdf:Description'), nsmap=nsmap('xmp', 'xmpidq'))
    xmp.set(expand('rdf:about'), '')
    rdf.append(xmp)
    extra_ids = {}
    for x in ('prism', 'pdfx'):
        p = extra_ids[x] = rdf.makeelement(expand('rdf:Description'), nsmap=nsmap(x))
        p.set(expand('rdf:about'), '')
        rdf.append(p)

    identifiers = mi.get_identifiers()
    if identifiers:
        create_identifiers(xmp, identifiers)
        for scheme, val in identifiers.iteritems():
            if scheme in {'isbn', 'doi'}:
                for prefix, parent in extra_ids.iteritems():
                    ie = parent.makeelement(expand('%s:%s'%(prefix, scheme)))
                    ie.text = val
                    parent.append(ie)

    d = xmp.makeelement(expand('xmp:MetadataDate'))
    d.text = isoformat(now(), as_utc=False)
    xmp.append(d)

    calibre = rdf.makeelement(expand('rdf:Description'), nsmap=nsmap('calibre', 'calibreSI', 'calibreCC'))
    calibre.set(expand('rdf:about'), '')
    rdf.append(calibre)
    if not mi.is_null('rating'):
        try:
            r = float(mi.rating)
        except (TypeError, ValueError):
            pass
        else:
            create_simple_property(calibre, 'calibre:rating', '%g' % r)
    if not mi.is_null('series'):
        create_series(calibre, mi.series, mi.series_index)
    if not mi.is_null('timestamp'):
        create_simple_property(calibre, 'calibre:timestamp', isoformat(mi.timestamp, as_utc=False))
    for x in ('author_link_map', 'user_categories'):
        val = getattr(mi, x, None)
        if val:
            create_simple_property(calibre, 'calibre:'+x, dump_dict(val))

    for x in ('title_sort', 'author_sort'):
        if not mi.is_null(x):
            create_simple_property(calibre, 'calibre:'+x, getattr(mi, x))

    all_user_metadata = mi.get_all_user_metadata(True)
    if all_user_metadata:
        create_user_metadata(calibre, all_user_metadata)
    return serialize_xmp_packet(root)
Exemple #37
0
def book_to_json(ctx, rd, db, book_id,
                 get_category_urls=True, device_compatible=False, device_for_template=None):
    mi = db.get_metadata(book_id, get_cover=False)
    codec = JsonCodec(db.field_metadata)
    if not device_compatible:
        try:
            mi.rating = mi.rating/2.
        except Exception:
            mi.rating = 0.0
    data = codec.encode_book_metadata(mi)
    for x in ('publication_type', 'size', 'db_id', 'lpath', 'mime',
            'rights', 'book_producer'):
        data.pop(x, None)

    get = partial(ctx.url_for, get_content, book_id=book_id, library_id=db.server_library_id)
    data['cover'] = get(what='cover')
    data['thumbnail'] = get(what='thumb')

    if not device_compatible:
        mi.format_metadata = {k.lower():dict(v) for k, v in
                mi.format_metadata.iteritems()}
        for v in mi.format_metadata.itervalues():
            mtime = v.get('mtime', None)
            if mtime is not None:
                v['mtime'] = isoformat(mtime, as_utc=True)
        data['format_metadata'] = mi.format_metadata
        fmts = set(x.lower() for x in mi.format_metadata.iterkeys())
        pf = prefs['output_format'].lower()
        other_fmts = list(fmts)
        try:
            fmt = pf if pf in fmts else other_fmts[0]
        except:
            fmt = None
        if fmts and fmt:
            other_fmts = [x for x in fmts if x != fmt]
        data['formats'] = sorted(fmts)
        if fmt:
            data['main_format'] = {fmt:get(what=fmt)}
        else:
            data['main_format'] = None
        data['other_formats'] = {fmt:get(what=fmt) for fmt in other_fmts}

        if get_category_urls:
            category_urls = data['category_urls'] = {}
            all_cats = ctx.get_categories(rd, db)
            for key in mi.all_field_keys():
                fm = mi.metadata_for_field(key)
                if (fm and fm['is_category'] and not fm['is_csp'] and
                        key != 'formats' and fm['datatype'] != 'rating'):
                    categories = mi.get(key) or []
                    if isinstance(categories, basestring):
                        categories = [categories]
                    category_urls[key] = dbtags = {}
                    for category in categories:
                        for tag in all_cats.get(key, ()):
                            if tag.original_name == category:
                                dbtags[category] = ctx.url_for(
                                    books_in,
                                    encoded_category=encode_name(tag.category if tag.category else key),
                                    encoded_item=encode_name(tag.original_name if tag.id is None else unicode(tag.id)),
                                    library_id=db.server_library_id
                                )
                                break
    else:
        series = data.get('series', None) or ''
        if series:
            tsorder = tweaks['save_template_title_series_sorting']
            series = title_sort(series, order=tsorder)
        data['_series_sort_'] = series
        if device_for_template:
            import posixpath
            from calibre.devices.utils import create_upload_path
            from calibre.utils.filenames import ascii_filename as sanitize
            from calibre.customize.ui import device_plugins

            for device_class in device_plugins():
                if device_class.__class__.__name__ == device_for_template:
                    template = device_class.save_template()
                    data['_filename_'] = create_upload_path(mi, book_id,
                            template, sanitize, path_type=posixpath)
                    break

    return data, mi.last_modified
Exemple #38
0
        def create_bibtex_entry(entry, fields, mode, template_citation,
                                    bibtexdict, db, citation_bibtex=True, calibre_files=True):

            # Bibtex doesn't like UTF-8 but keep unicode until writing
            # Define starting chain or if book valid strict and not book return a Fail string

            bibtex_entry = []
            if mode != "misc" and check_entry_book_valid(entry) :
                bibtex_entry.append('@book{')
            elif mode != "book" :
                bibtex_entry.append('@misc{')
            else :
                # case strict book
                return ''

            if citation_bibtex :
                # Citation tag
                bibtex_entry.append(make_bibtex_citation(entry, template_citation,
                    bibtexdict))
                bibtex_entry = [' '.join(bibtex_entry)]

            for field in fields:
                if field.startswith('#'):
                    item = db.get_field(entry['id'],field,index_is_id=True)
                    if isinstance(item, (bool, numbers.Number)):
                        item = repr(item)
                elif field == 'title_sort':
                    item = entry['sort']
                elif field == 'library_name':
                    item = library_name
                else:
                    item = entry[field]

                # check if the field should be included (none or empty)
                if item is None:
                    continue
                try:
                    if len(item) == 0 :
                        continue
                except TypeError:
                    pass

                if field == 'authors' :
                    bibtex_entry.append('author = "%s"' % bibtexdict.bibtex_author_format(item))

                elif field == 'id' :
                    bibtex_entry.append('calibreid = "%s"' % int(item))

                elif field == 'rating' :
                    bibtex_entry.append('rating = "%s"' % int(item))

                elif field == 'size' :
                    bibtex_entry.append('%s = "%s octets"' % (field, int(item)))

                elif field == 'tags' :
                    # A list to flatten
                    bibtex_entry.append('tags = "%s"' % bibtexdict.utf8ToBibtex(', '.join(item)))

                elif field == 'comments' :
                    # \n removal
                    item = item.replace('\r\n', ' ')
                    item = item.replace('\n', ' ')
                    # unmatched brace removal (users should use \leftbrace or \rightbrace for single braces)
                    item = bibtexdict.stripUnmatchedSyntax(item, '{', '}')
                    # html to text
                    try:
                        item = html2text(item)
                    except:
                        log.warn("Failed to convert comments to text")
                    bibtex_entry.append('note = "%s"' % bibtexdict.utf8ToBibtex(item))

                elif field == 'isbn' :
                    # Could be 9, 10 or 13 digits
                    bibtex_entry.append('isbn = "%s"' % format_isbn(item))

                elif field == 'formats' :
                    # Add file path if format is selected
                    formats = [format.rpartition('.')[2].lower() for format in item]
                    bibtex_entry.append('formats = "%s"' % ', '.join(formats))
                    if calibre_files:
                        files = [':%s:%s' % (format, format.rpartition('.')[2].upper())
                            for format in item]
                        bibtex_entry.append('file = "%s"' % ', '.join(files))

                elif field == 'series_index' :
                    bibtex_entry.append('volume = "%s"' % int(item))

                elif field == 'timestamp' :
                    bibtex_entry.append('timestamp = "%s"' % isoformat(item).partition('T')[0])

                elif field == 'pubdate' :
                    bibtex_entry.append('year = "%s"' % item.year)
                    bibtex_entry.append('month = "%s"' % bibtexdict.utf8ToBibtex(strftime("%b", item)))

                elif field.startswith('#') and isinstance(item, string_or_bytes):
                    bibtex_entry.append('custom_%s = "%s"' % (field[1:],
                        bibtexdict.utf8ToBibtex(item)))

                elif isinstance(item, string_or_bytes):
                    # elif field in ['title', 'publisher', 'cover', 'uuid', 'ondevice',
                    # 'author_sort', 'series', 'title_sort'] :
                    bibtex_entry.append('%s = "%s"' % (field, bibtexdict.utf8ToBibtex(item)))

            bibtex_entry = ',\n    '.join(bibtex_entry)
            bibtex_entry += ' }\n\n'

            return bibtex_entry
Exemple #39
0
def adapt_datetime(dt):
    return isoformat(dt, sep=' ')
Exemple #40
0
    def run(self, path_to_output, opts, db, notification=DummyReporter()):
        from calibre.library import current_library_name
        from calibre.utils.date import isoformat
        from calibre.utils.html2text import html2text
        from calibre.utils.logging import default_log as log
        from lxml import etree

        self.fmt = path_to_output.rpartition(".")[2]
        self.notification = notification
        current_library = current_library_name()
        if getattr(opts, "library_path", None):
            current_library = os.path.basename(opts.library_path)

        if opts.verbose:
            opts_dict = vars(opts)
            log("%s('%s'): Generating %s" % (self.name, current_library, self.fmt.upper()))
            if opts.connected_device["is_device_connected"]:
                log(" connected_device: %s" % opts.connected_device["name"])
            if opts_dict["search_text"]:
                log(" --search='%s'" % opts_dict["search_text"])

            if opts_dict["ids"]:
                log(" Book count: %d" % len(opts_dict["ids"]))
                if opts_dict["search_text"]:
                    log(" (--search ignored when a subset of the database is specified)")

            if opts_dict["fields"]:
                if opts_dict["fields"] == "all":
                    log(" Fields: %s" % ", ".join(FIELDS[1:]))
                else:
                    log(" Fields: %s" % opts_dict["fields"])

        # If a list of ids are provided, don't use search_text
        if opts.ids:
            opts.search_text = None

        data = self.search_sort_db(db, opts)

        if not len(data):
            log.error("\nNo matching database entries for search criteria '%s'" % opts.search_text)
            # raise SystemExit(1)

        # Get the requested output fields as a list
        fields = self.get_output_fields(db, opts)

        # If connected device, add 'On Device' values to data
        if opts.connected_device["is_device_connected"] and "ondevice" in fields:
            for entry in data:
                entry["ondevice"] = db.catalog_plugin_on_device_temp_mapping[entry["id"]]["ondevice"]

        fm = {x: db.field_metadata.get(x, {}) for x in fields}

        if self.fmt == "csv":
            outfile = codecs.open(path_to_output, "w", "utf8")

            # Write a UTF-8 BOM
            outfile.write("\xef\xbb\xbf")

            # Output the field headers
            outfile.write(u"%s\n" % u",".join(fields))

            # Output the entry fields
            for entry in data:
                outstr = []
                for field in fields:
                    if field.startswith("#"):
                        item = db.get_field(entry["id"], field, index_is_id=True)
                    elif field == "library_name":
                        item = current_library
                    elif field == "title_sort":
                        item = entry["sort"]
                    else:
                        item = entry[field]

                    if item is None:
                        outstr.append('""')
                        continue
                    elif field == "formats":
                        fmt_list = []
                        for format in item:
                            fmt_list.append(format.rpartition(".")[2].lower())
                        item = ", ".join(fmt_list)
                    elif field in ["authors", "tags"]:
                        item = ", ".join(item)
                    elif field == "isbn":
                        # Could be 9, 10 or 13 digits, with hyphens, possibly ending in 'X'
                        item = u"%s" % re.sub(r"[^\dX-]", "", item)
                    elif field in ["pubdate", "timestamp"]:
                        item = isoformat(item, as_utc=False)
                    elif field == "comments":
                        item = item.replace(u"\r\n", u" ")
                        item = item.replace(u"\n", u" ")
                    elif fm.get(field, {}).get("datatype", None) == "rating" and item:
                        item = u"%.2g" % (item / 2.0)

                    # Convert HTML to markdown text
                    if type(item) is unicode:
                        opening_tag = re.search("<(\w+)(\x20|>)", item)
                        if opening_tag:
                            closing_tag = re.search("<\/%s>$" % opening_tag.group(1), item)
                            if closing_tag:
                                item = html2text(item)

                    outstr.append(u'"%s"' % unicode(item).replace('"', '""'))

                outfile.write(u",".join(outstr) + u"\n")
            outfile.close()

        elif self.fmt == "xml":
            from lxml.builder import E

            root = E.calibredb()
            for r in data:
                record = E.record()
                root.append(record)

                for field in fields:
                    if field.startswith("#"):
                        val = db.get_field(r["id"], field, index_is_id=True)
                        if not isinstance(val, (str, unicode)):
                            val = unicode(val)
                        item = getattr(E, field.replace("#", "_"))(val)
                        record.append(item)

                for field in ("id", "uuid", "publisher", "rating", "size", "isbn", "ondevice", "identifiers"):
                    if field in fields:
                        val = r[field]
                        if not val:
                            continue
                        if not isinstance(val, (str, unicode)):
                            if fm.get(field, {}).get("datatype", None) == "rating" and val:
                                val = u"%.2g" % (val / 2.0)
                            val = unicode(val)
                        item = getattr(E, field)(val)
                        record.append(item)

                if "title" in fields:
                    title = E.title(r["title"], sort=r["sort"])
                    record.append(title)

                if "authors" in fields:
                    aus = E.authors(sort=r["author_sort"])
                    for au in r["authors"]:
                        aus.append(E.author(au))
                    record.append(aus)

                for field in ("timestamp", "pubdate"):
                    if field in fields:
                        record.append(getattr(E, field)(isoformat(r[field], as_utc=False)))

                if "tags" in fields and r["tags"]:
                    tags = E.tags()
                    for tag in r["tags"]:
                        tags.append(E.tag(tag))
                    record.append(tags)

                if "comments" in fields and r["comments"]:
                    record.append(E.comments(r["comments"]))

                if "series" in fields and r["series"]:
                    record.append(E.series(r["series"], index=str(r["series_index"])))

                if "cover" in fields and r["cover"]:
                    record.append(E.cover(r["cover"].replace(os.sep, "/")))

                if "formats" in fields and r["formats"]:
                    fmt = E.formats()
                    for f in r["formats"]:
                        fmt.append(E.format(f.replace(os.sep, "/")))
                    record.append(fmt)

                if "library_name" in fields:
                    record.append(E.library_name(current_library))

            with open(path_to_output, "w") as f:
                f.write(etree.tostring(root, encoding="utf-8", xml_declaration=True, pretty_print=True))