Beispiel #1
0
def set_title(book_id_val_map, db, field, *args):
    ans = one_one_in_books(book_id_val_map, db, field, *args)
    # Set the title sort field if the title changed
    field.title_sort_field.writer.set_books(
        {k: title_sort(v)
         for k, v in book_id_val_map.items() if k in ans}, db)
    return ans
Beispiel #2
0
 def commit(self, save_defaults=False):
     '''
     Settings are stored in two attributes: `opf_file` and `cover_file`.
     Both may be None. Also returns a recommendation dictionary.
     '''
     recs = self.commit_options(save_defaults)
     self.user_mi = mi = self.get_metadata()
     self.cover_file = self.opf_file = None
     if self.db is not None:
         if mi.title == self.db.title(self.book_id, index_is_id=True):
             mi.title_sort = self.db.title_sort(self.book_id,
                                                index_is_id=True)
         else:
             # Regenerate title sort taking into account book language
             languages = self.db.languages(self.book_id, index_is_id=True)
             if languages:
                 lang = languages.split(',')[0]
             else:
                 lang = None
             mi.title_sort = title_sort(mi.title, lang=lang)
         self.db.set_metadata(self.book_id, self.user_mi)
         self.mi, self.opf_file = create_opf_file(self.db, self.book_id)
         if self.cover_changed and self.cover_data is not None:
             self.db.set_cover(self.book_id, self.cover_data)
         cover = self.db.cover(self.book_id, index_is_id=True)
         if cover:
             cf = PersistentTemporaryFile('.jpeg')
             cf.write(cover)
             cf.close()
             self.cover_file = cf
     return recs
Beispiel #3
0
 def commit(self, save_defaults=False):
     '''
     Settings are stored in two attributes: `opf_file` and `cover_file`.
     Both may be None. Also returns a recommendation dictionary.
     '''
     recs = self.commit_options(save_defaults)
     self.user_mi = mi = self.get_metadata()
     self.cover_file = self.opf_file = None
     if self.db is not None:
         if mi.title == self.db.title(self.book_id, index_is_id=True):
             mi.title_sort = self.db.title_sort(self.book_id, index_is_id=True)
         else:
             # Regenerate title sort taking into account book language
             languages = self.db.languages(self.book_id, index_is_id=True)
             if languages:
                 lang = languages.split(',')[0]
             else:
                 lang = None
             mi.title_sort = title_sort(mi.title, lang=lang)
         self.db.set_metadata(self.book_id, self.user_mi)
         self.mi, self.opf_file = create_opf_file(self.db, self.book_id)
         if self.cover_changed and self.cover_data is not None:
             self.db.set_cover(self.book_id, self.cover_data)
         cover = self.db.cover(self.book_id, index_is_id=True)
         if cover:
             cf = PersistentTemporaryFile('.jpeg')
             cf.write(cover)
             cf.close()
             self.cover_file = cf
     return recs
    def _get_metadata(self, book, pdf_stats):
        '''
        Return a populated Book object with available metadata
        '''
        from calibre.ebooks.metadata import author_to_author_sort, authors_to_string, title_sort
        from calibre.ebooks.metadata.pdf import get_metadata
        self._log_location(repr(book))

        pdf_path = os.path.join(self.temp_dir, pdf_stats['path'])
        with open(pdf_path, 'rb') as f:
            stream = cStringIO.StringIO(f.read())

        mi = get_metadata(stream, cover=False)
        this_book = Book(mi.title, authors=mi.authors)
        this_book.author_sort = author_to_author_sort(mi.authors[0])
        this_book.datetime = datetime.fromtimestamp(
            int(pdf_stats['stats']['st_birthtime'])).timetuple()
        this_book.dateadded = int(pdf_stats['stats']['st_birthtime'])
        this_book.path = book
        this_book.size = int(pdf_stats['stats']['st_size'])
        this_book.thumbnail = self._get_goodreader_thumb(book)
        if this_book.thumbnail:
            this_book.thumb_data = base64.b64encode(this_book.thumbnail)
        else:
            this_book.thumb_data = None
        this_book.title_sort = title_sort(mi.title)
        this_book.uuid = None

        return this_book
    def _get_metadata(self, book, pdf_stats):
        """
        Return a populated Book object with available metadata
        """
        from calibre.ebooks.metadata import author_to_author_sort, authors_to_string, title_sort
        from calibre.ebooks.metadata.pdf import get_metadata

        self._log_location(repr(book))

        pdf_path = os.path.join(self.temp_dir, pdf_stats["path"])
        with open(pdf_path, "rb") as f:
            stream = cStringIO.StringIO(f.read())

        mi = get_metadata(stream, cover=False)
        this_book = Book(mi.title, " & ".join(mi.authors))
        this_book.author_sort = author_to_author_sort(mi.authors[0])
        this_book.datetime = datetime.fromtimestamp(int(pdf_stats["stats"]["st_birthtime"])).timetuple()
        this_book.dateadded = int(pdf_stats["stats"]["st_birthtime"])
        this_book.path = book
        this_book.size = int(pdf_stats["stats"]["st_size"])
        this_book.thumbnail = self._get_goodreader_thumb(book)
        if this_book.thumbnail:
            this_book.thumb_data = base64.b64encode(this_book.thumbnail)
        else:
            this_book.thumb_data = None
        this_book.title_sort = title_sort(mi.title)
        this_book.uuid = None

        return this_book
Beispiel #6
0
 def to_mi(self, mi):
     val = self.value
     mi.set(self.field, val)
     if self.field == 'title':
         mi.set('title_sort', title_sort(val, lang=mi.language))
     elif self.field == 'authors':
         mi.set('author_sort', authors_to_sort_string(val))
Beispiel #7
0
 def to_mi(self, mi):
     val = self.value
     mi.set(self.field, val)
     if self.field == 'title':
         mi.set('title_sort', title_sort(val, lang=mi.language))
     elif self.field == 'authors':
         mi.set('author_sort', authors_to_sort_string(val))
def set_title(book_id_val_map, db, field, *args):
    ans = one_one_in_books(book_id_val_map, db, field, *args)
    # Set the title sort field
    field.title_sort_field.writer.set_books(
        {k: title_sort(v)
         for k, v in iteritems(book_id_val_map)}, db)
    return ans
Beispiel #9
0
 def __getitem__(self, col):
     if self._must_do:
         is_comp = False
         if isinstance(col, slice):
             start = 0 if col.start is None else col.start
             step = 1 if col.stop is None else col.stop
             for c in range(start, col.stop, step):
                 if c in self._composites:
                     is_comp = True
                     break
         elif col in self._composites:
             is_comp = True
         if is_comp:
             id_ = list.__getitem__(self, 0)
             self._must_do = False
             mi = self.db.get_metadata(id_, index_is_id=True,
                                       get_user_categories=False)
             for c in self._composites:
                 self[c] =  mi.get(self._composites[c])
     if col == self._series_sort_col and self._series_sort is None:
         if self[self._series_col]:
             self._series_sort = title_sort(self[self._series_col])
             self[self._series_sort_col] = self._series_sort
         else:
             self._series_sort = ''
             self[self._series_sort_col] = ''
     return list.__getitem__(self, col)
Beispiel #10
0
    def do_tags_list_edit(self, tag, category):
        '''
        Open the 'manage_X' dialog where X == category. If tag is not None, the
        dialog will position the editor on that item.
        '''

        tags_model = self.tags_view.model()
        result = tags_model.get_category_editor_data(category)
        if result is None:
            return

        if category == 'series':
            key = lambda x: sort_key(title_sort(x))
        else:
            key = sort_key

        db = self.library_view.model().db
        d = TagListEditor(self,
                          cat_name=db.field_metadata[category]['name'],
                          tag_to_match=tag,
                          data=result,
                          sorter=key)
        d.exec_()
        if d.result() == d.Accepted:
            to_rename = d.to_rename  # dict of old id to new name
            to_delete = d.to_delete  # list of ids
            orig_name = d.original_names  # dict of id: name

            rename_func = None
            if category == 'tags':
                rename_func = db.rename_tag
                delete_func = db.delete_tag_using_id
            elif category == 'series':
                rename_func = db.rename_series
                delete_func = db.delete_series_using_id
            elif category == 'publisher':
                rename_func = db.rename_publisher
                delete_func = db.delete_publisher_using_id
            else:  # must be custom
                cc_label = db.field_metadata[category]['label']
                rename_func = partial(db.rename_custom_item, label=cc_label)
                delete_func = partial(db.delete_custom_item_using_id,
                                      label=cc_label)
            m = self.tags_view.model()
            if rename_func:
                for item in to_delete:
                    delete_func(item)
                    m.delete_item_from_all_user_categories(
                        orig_name[item], category)
                for old_id in to_rename:
                    rename_func(old_id, new_name=unicode(to_rename[old_id]))
                    m.rename_item_in_all_user_categories(
                        orig_name[old_id], category,
                        unicode(to_rename[old_id]))

            # Clean up the library view
            self.do_tag_item_renamed()
            self.tags_view.recount()
Beispiel #11
0
 def sort_key_for_series(self, book_id, lang_map, series_sort_order):
     sid = self.table.book_col_map.get(book_id, None)
     if sid is None:
         return self._default_sort_key
     lang = lang_map.get(book_id, None) or None
     if lang:
         lang = lang[0]
     return self._sort_key(title_sort(self.table.id_map[sid],
                                      order=series_sort_order, lang=lang))
Beispiel #12
0
 def get_series_sort(self, idx, index_is_id=True, default_value=''):
     book_id = idx if index_is_id else self.index_to_id(idx)
     with self.cache.read_lock:
         lang_map = self.cache.fields['languages'].book_value_map
         lang = lang_map.get(book_id, None) or None
         if lang:
             lang = lang[0]
         return title_sort(self.cache._field_for('series', book_id, default_value=''),
                           order=tweaks['title_series_sorting'], lang=lang)
Beispiel #13
0
 def get_sort(book_id):
     if args.languages:
         lang = args.languages[0]
     else:
         try:
             lang = lang_map[book_id][0]
         except (KeyError, IndexError, TypeError, AttributeError):
             lang = 'eng'
     return title_sort(title_map[book_id], lang=lang)
Beispiel #14
0
 def get_series_sort(self, idx, index_is_id=True, default_value=''):
     book_id = idx if index_is_id else self.index_to_id(idx)
     with self.cache.safe_read_lock:
         lang_map = self.cache.fields['languages'].book_value_map
         lang = lang_map.get(book_id, None) or None
         if lang:
             lang = lang[0]
         return title_sort(self.cache._field_for('series', book_id, default_value=''),
                           order=tweaks['title_series_sorting'], lang=lang)
    def sync_booklists(self, booklists, end_session=True):
        '''
        Update metadata on device.
        @param booklists: A tuple containing the result of calls to
                                (L{books}(oncard=None), L{books}(oncard='carda'),
                                L{books}(oncard='cardb')).

        prefs['manage_device_metadata']: ['manual'|'on_send'|'on_connect']

        booklist will reflect library metadata only when
        manage_device_metadata=='on_connect', otherwise booklist metadata comes from
        device
        '''
        from calibre.ebooks.metadata import author_to_author_sort, authors_to_string, title_sort

        self._log_location()

        for booklist in booklists:
            if not booklist:
                continue

            # Update db title/author from booklist title/author
            con = sqlite3.connect(self.local_metadata)
            with con:
                con.row_factory = sqlite3.Row
                cur = con.cursor()
                for book in booklist:
                    cur.execute('''SELECT
                                    authors,
                                    filename,
                                    title
                                   FROM metadata
                                   WHERE filename = {0}
                                '''.format(
                        self._quote_sqlite_identifier(book.path)))
                    cached_book = cur.fetchone()
                    if cached_book:
                        if (book.title != cached_book[b'title'] or
                            book.authors != [cached_book[b'authors']]):
                            self._log("updating metadata for %s" % repr(book.path))
                            cur.execute('''UPDATE metadata
                                           SET authors = "{0}",
                                               author_sort = "{1}",
                                               title = "{2}",
                                               title_sort = "{3}"
                                           WHERE filename = {4}
                                        '''.format(self._escape_delimiters(' & '.join(book.authors)),
                                                   self._escape_delimiters(author_to_author_sort(book.authors[0])),
                                                   self._escape_delimiters(book.title),
                                                   self._escape_delimiters(title_sort(book.title)),
                                                   self._quote_sqlite_identifier(book.path)))

                con.commit()

            # Copy the updated db to the iDevice
            self._log("updating remote_metadata")
            self.ios.copy_to_idevice(str(self.local_metadata), str(self.remote_metadata))
Beispiel #16
0
 def sort_key_for_series(self, book_id, lang_map, series_sort_order):
     sid = self.table.book_col_map.get(book_id, None)
     if sid is None:
         return self._default_sort_key
     lang = lang_map.get(book_id, None) or None
     if lang:
         lang = lang[0]
     return self._sort_key(title_sort(self.table.id_map[sid],
                                      order=series_sort_order, lang=lang))
    def sync_booklists(self, booklists, end_session=True):
        '''
        Update metadata on device.
        @param booklists: A tuple containing the result of calls to
                                (L{books}(oncard=None), L{books}(oncard='carda'),
                                L{books}(oncard='cardb')).

        prefs['manage_device_metadata']: ['manual'|'on_send'|'on_connect']

        booklist will reflect library metadata only when
        manage_device_metadata=='on_connect', otherwise booklist metadata comes from
        device
        '''
        from calibre.ebooks.metadata import author_to_author_sort, authors_to_string, title_sort

        self._log_location()

        for booklist in booklists:
            if not booklist:
                continue

            # Update db title/author from booklist title/author
            con = sqlite3.connect(self.local_metadata)
            with con:
                con.row_factory = sqlite3.Row
                cur = con.cursor()
                for book in booklist:
                    cur.execute('''SELECT
                                    authors,
                                    filename,
                                    title
                                   FROM metadata
                                   WHERE filename = {0}
                                '''.format(json.dumps(book.path)))
                    cached_book = cur.fetchone()
                    if cached_book:
                        if (book.title != cached_book[b'title'] or
                            book.authors != [cached_book[b'authors']]):
                            self._log("updating metadata for %s" % repr(book.path))
                            cur.execute('''UPDATE metadata
                                           SET authors = "{0}",
                                               author_sort = "{1}",
                                               title = "{2}",
                                               title_sort = "{3}"
                                           WHERE filename = {4}
                                        '''.format(self._escape_delimiters('; '.join(book.authors)),
                                                   self._escape_delimiters(author_to_author_sort(book.authors[0])),
                                                   self._escape_delimiters(book.title),
                                                   self._escape_delimiters(title_sort(book.title)),
                                                   json.dumps(book.path)))

                con.commit()

            # Copy the updated db to the iDevice
            self._log("updating remote_metadata")
            self.ios.copy_to_idevice(str(self.local_metadata), str(self.remote_metadata))
Beispiel #18
0
    def do_tags_list_edit(self, tag, category):
        '''
        Open the 'manage_X' dialog where X == category. If tag is not None, the
        dialog will position the editor on that item.
        '''

        db = self.current_db

        def get_book_ids(use_virtual_library):
            book_ids = None if not use_virtual_library else self.tags_view.model(
            ).get_book_ids_to_use()
            data = db.new_api.get_categories(book_ids=book_ids)
            if category in data:
                result = [(t.id, t.original_name, t.count)
                          for t in data[category] if t.count > 0]
            else:
                result = None
            return result

        if category == 'series':
            key = lambda x: sort_key(title_sort(x))
        else:
            key = sort_key

        d = TagListEditor(self,
                          cat_name=db.field_metadata[category]['name'],
                          tag_to_match=tag,
                          get_book_ids=get_book_ids,
                          sorter=key)
        d.exec_()
        if d.result() == d.Accepted:
            to_rename = d.to_rename  # dict of old id to new name
            to_delete = d.to_delete  # list of ids
            orig_name = d.original_names  # dict of id: name

            if (category in ['tags', 'series', 'publisher']
                    or db.new_api.field_metadata.is_custom_field(category)):
                m = self.tags_view.model()
                for item in to_delete:
                    m.delete_item_from_all_user_categories(
                        orig_name[item], category)
                for old_id in to_rename:
                    m.rename_item_in_all_user_categories(
                        orig_name[old_id], category,
                        unicode_type(to_rename[old_id]))

                db.new_api.remove_items(category, to_delete)
                db.new_api.rename_items(category,
                                        to_rename,
                                        change_index=False)

                # Clean up the library view
                self.do_tag_item_renamed()
                self.tags_view.recount()
Beispiel #19
0
def one_one_in_books(book_id_val_map, db, field, *args):
    'Set a one-one field in the books table'
    if book_id_val_map:
        sequence = ((sqlite_datetime(v), k) for k, v in book_id_val_map.iteritems())
        db.conn.executemany(
            'UPDATE books SET %s=? WHERE id=?'%field.metadata['column'], sequence)
        field.table.book_col_map.update(book_id_val_map)
    if field.name == 'title':
        # Set the title sort field
        field.title_sort_field.writer.set_books(
            {k:title_sort(v) for k, v in book_id_val_map.iteritems()}, db)
    return set(book_id_val_map)
Beispiel #20
0
 def update_state(self, *args):
     ts = title_sort(self.title_edit.current_val, lang=self.book_lang)
     normal = ts == self.current_val
     if normal:
         col = 'rgb(0, 255, 0, 20%)'
     else:
         col = 'rgb(255, 0, 0, 20%)'
     self.setStyleSheet('QLineEdit { color: black; '
                        'background-color: %s; }' % col)
     tt = self.tooltips[0 if normal else 1]
     self.setToolTip(tt)
     self.setWhatsThis(tt)
Beispiel #21
0
 def update_state(self, *args):
     ts = title_sort(self.title_edit.current_val, lang=self.book_lang)
     normal = ts == self.current_val
     if normal:
         col = 'rgb(0, 255, 0, 20%)'
     else:
         col = 'rgb(255, 0, 0, 20%)'
     self.setStyleSheet('QLineEdit { color: black; '
                           'background-color: %s; }'%col)
     tt = self.tooltips[0 if normal else 1]
     self.setToolTip(tt)
     self.setWhatsThis(tt)
Beispiel #22
0
def do_set_metadata(opts, mi, stream, stream_type):
    mi = MetaInformation(mi)
    for x in ('guide', 'toc', 'manifest', 'spine'):
        setattr(mi, x, None)

    from_opf = getattr(opts, 'from_opf', None)
    if from_opf is not None:
        from calibre.ebooks.metadata.opf2 import OPF
        opf_mi = OPF(open(from_opf, 'rb')).to_book_metadata()
        mi.smart_update(opf_mi)

    for pref in config().option_set.preferences:
        if pref.name in ('to_opf', 'from_opf', 'authors', 'title_sort',
                         'author_sort', 'get_cover', 'cover', 'tags',
                         'lrf_bookid', 'identifiers'):
            continue
        val = getattr(opts, pref.name, None)
        if val is not None:
            setattr(mi, pref.name, val)
    if getattr(opts, 'authors', None) is not None:
        mi.authors = string_to_authors(opts.authors)
        mi.author_sort = authors_to_sort_string(mi.authors)
    if getattr(opts, 'author_sort', None) is not None:
        mi.author_sort = opts.author_sort
    if getattr(opts, 'title_sort', None) is not None:
        mi.title_sort = opts.title_sort
    elif getattr(opts, 'title', None) is not None:
        mi.title_sort = title_sort(opts.title)
    if getattr(opts, 'tags', None) is not None:
        mi.tags = [t.strip() for t in opts.tags.split(',')]
    if getattr(opts, 'series', None) is not None:
        mi.series = opts.series.strip()
    if getattr(opts, 'series_index', None) is not None:
        mi.series_index = float(opts.series_index.strip())
    if getattr(opts, 'pubdate', None) is not None:
        mi.pubdate = parse_date(opts.pubdate, assume_utc=False, as_utc=False)
    if getattr(opts, 'identifiers', None):
        val = {
            k.strip(): v.strip()
            for k, v in (x.partition(':')[0::2] for x in opts.identifiers)
        }
        if val:
            orig = mi.get_identifiers()
            orig.update(val)
            val = {k: v for k, v in orig.iteritems() if k and v}
            mi.set_identifiers(val)

    if getattr(opts, 'cover', None) is not None:
        ext = os.path.splitext(opts.cover)[1].replace('.', '').upper()
        mi.cover_data = (ext, open(opts.cover, 'rb').read())

    with force_identifiers:
        set_metadata(stream, mi, stream_type)
Beispiel #23
0
    def do_tags_list_edit(self, tag, category):
        '''
        Open the 'manage_X' dialog where X == category. If tag is not None, the
        dialog will position the editor on that item.
        '''

        tags_model = self.tags_view.model()
        result = tags_model.get_category_editor_data(category)
        if result is None:
            return

        if category == 'series':
            key = lambda x:sort_key(title_sort(x))
        else:
            key = sort_key

        db=self.library_view.model().db
        d = TagListEditor(self, cat_name=db.field_metadata[category]['name'],
                          tag_to_match=tag, data=result, sorter=key)
        d.exec_()
        if d.result() == d.Accepted:
            to_rename = d.to_rename  # dict of old id to new name
            to_delete = d.to_delete  # list of ids
            orig_name = d.original_names  # dict of id: name

            rename_func = None
            if category == 'tags':
                rename_func = db.rename_tag
                delete_func = db.delete_tag_using_id
            elif category == 'series':
                rename_func = db.rename_series
                delete_func = db.delete_series_using_id
            elif category == 'publisher':
                rename_func = db.rename_publisher
                delete_func = db.delete_publisher_using_id
            else:  # must be custom
                cc_label = db.field_metadata[category]['label']
                rename_func = partial(db.rename_custom_item, label=cc_label)
                delete_func = partial(db.delete_custom_item_using_id, label=cc_label)
            m = self.tags_view.model()
            if rename_func:
                for item in to_delete:
                    delete_func(item)
                    m.delete_item_from_all_user_categories(orig_name[item], category)
                for old_id in to_rename:
                    rename_func(old_id, new_name=unicode(to_rename[old_id]))
                    m.rename_item_in_all_user_categories(orig_name[old_id],
                                            category, unicode(to_rename[old_id]))

            # Clean up the library view
            self.do_tag_item_renamed()
            self.tags_view.recount()
Beispiel #24
0
 def to_mi(self, mi):
     val = unicode(self.text()).strip()
     ism = self.metadata['is_multiple']
     if ism:
         if not val:
             val = []
         else:
             val = [x.strip() for x in val.split(ism['list_to_ui']) if x.strip()]
     mi.set(self.field, val)
     if self.field == 'title':
         mi.set('title_sort', title_sort(val, lang=mi.language))
     elif self.field == 'authors':
         mi.set('author_sort', authors_to_sort_string(val))
Beispiel #25
0
 def to_mi(self, mi):
     val = unicode(self.text()).strip()
     ism = self.metadata['is_multiple']
     if ism:
         if not val:
             val = []
         else:
             val = [x.strip() for x in val.split(ism['list_to_ui']) if x.strip()]
     mi.set(self.field, val)
     if self.field == 'title':
         mi.set('title_sort', title_sort(val, lang=mi.language))
     elif self.field == 'authors':
         mi.set('author_sort', authors_to_sort_string(val))
Beispiel #26
0
 def to_mi(self, mi):
     val = unicode(self.text()).strip()
     ism = self.metadata["is_multiple"]
     if ism:
         if not val:
             val = []
         else:
             val = [x.strip() for x in val.split(ism["list_to_ui"]) if x.strip()]
     mi.set(self.field, val)
     if self.field == "title":
         mi.set("title_sort", title_sort(val, lang=mi.language))
     elif self.field == "authors":
         mi.set("author_sort", authors_to_sort_string(val))
Beispiel #27
0
    def _get_metadata(self, book, book_stats):
        '''
        Return a populated Book object with available metadata
        '''
        from calibre.ebooks.metadata import author_to_author_sort, authors_to_string, title_sort
        self._log_location(repr(book))
        format = book.rsplit('.')[1].lower()
        if format == 'mobi':
            from calibre.ebooks.metadata.mobi import get_metadata as get_mobi_metadata
            path = os.path.join(self.temp_dir, book_stats['path'])
            with open(path, 'rb') as f:
                stream = cStringIO.StringIO(f.read())
            mi = get_mobi_metadata(stream)

        elif format == 'pdf':
            from calibre.ebooks.metadata.pdf import get_metadata as get_pdf_metadata
            path = os.path.join(self.temp_dir, book_stats['path'])
            with open(path, 'rb') as f:
                stream = cStringIO.StringIO(f.read())
            mi = get_pdf_metadata(stream)

        else:
            self._log("unsupported format: '{}'".format(format))
            return Book()

        if False:
            ''' Perform a bit of voodoo to match Kindle multiple author style '''
            ks_authors = []
            for a in mi.authors:
                if "," in a:
                    # Already ln, fn
                    ks_authors.append(a)
                else:
                    ks_authors.append(author_to_author_sort(a))

        this_book = Book(mi.title, '; '.join(mi.authors))
        this_book.author_sort = '; '.join(mi.authors)
        this_book.datetime = datetime.fromtimestamp(
            int(book_stats['stats']['st_birthtime'])).timetuple()
        this_book.dateadded = int(book_stats['stats']['st_birthtime'])
        this_book.path = book
        this_book.size = int(book_stats['stats']['st_size'])
        this_book.thumbnail = self._get_kindle_thumb(mi.cover_data[1])
        if this_book.thumbnail:
            this_book.thumb_data = base64.b64encode(this_book.thumbnail)
        else:
            this_book.thumb_data = None
        this_book.title_sort = title_sort(mi.title)
        this_book.uuid = None

        return this_book
Beispiel #28
0
def do_set_metadata(opts, mi, stream, stream_type):
    mi = MetaInformation(mi)
    for x in ('guide', 'toc', 'manifest', 'spine'):
        setattr(mi, x, None)

    from_opf = getattr(opts, 'from_opf', None)
    if from_opf is not None:
        from calibre.ebooks.metadata.opf2 import OPF
        opf_mi = OPF(open(from_opf, 'rb')).to_book_metadata()
        mi.smart_update(opf_mi)

    for pref in config().option_set.preferences:
        if pref.name in ('to_opf', 'from_opf', 'authors', 'title_sort',
                         'author_sort', 'get_cover', 'cover', 'tags',
                         'lrf_bookid', 'identifiers'):
            continue
        val = getattr(opts, pref.name, None)
        if val is not None:
            setattr(mi, pref.name, val)
    if getattr(opts, 'authors', None) is not None:
        mi.authors = string_to_authors(opts.authors)
        mi.author_sort = authors_to_sort_string(mi.authors)
    if getattr(opts, 'author_sort', None) is not None:
        mi.author_sort = opts.author_sort
    if getattr(opts, 'title_sort', None) is not None:
        mi.title_sort = opts.title_sort
    elif getattr(opts, 'title', None) is not None:
        mi.title_sort = title_sort(opts.title)
    if getattr(opts, 'tags', None) is not None:
        mi.tags = [t.strip() for t in opts.tags.split(',')]
    if getattr(opts, 'series', None) is not None:
        mi.series = opts.series.strip()
    if getattr(opts, 'series_index', None) is not None:
        mi.series_index = float(opts.series_index.strip())
    if getattr(opts, 'pubdate', None) is not None:
        mi.pubdate = parse_date(opts.pubdate, assume_utc=False, as_utc=False)
    if getattr(opts, 'identifiers', None):
        val = {k.strip():v.strip() for k, v in (x.partition(':')[0::2] for x in opts.identifiers)}
        if val:
            orig = mi.get_identifiers()
            orig.update(val)
            val = {k:v for k, v in iteritems(orig) if k and v}
            mi.set_identifiers(val)

    if getattr(opts, 'cover', None) is not None:
        ext = os.path.splitext(opts.cover)[1].replace('.', '').upper()
        mi.cover_data = (ext, open(opts.cover, 'rb').read())

    with force_identifiers:
        set_metadata(stream, mi, stream_type)
    def _get_metadata(self, book, book_stats):
        '''
        Return a populated Book object with available metadata
        '''
        from calibre.ebooks.metadata import author_to_author_sort, authors_to_string, title_sort
        self._log_location(repr(book))
        format = book.rsplit('.')[1].lower()
        if format == 'mobi':
            from calibre.ebooks.metadata.mobi import get_metadata as get_mobi_metadata
            path = os.path.join(self.temp_dir, book_stats['path'])
            with open(path, 'rb') as f:
                stream = cStringIO.StringIO(f.read())
            mi = get_mobi_metadata(stream)

        elif format == 'pdf':
            from calibre.ebooks.metadata.pdf import get_metadata as get_pdf_metadata
            path = os.path.join(self.temp_dir, book_stats['path'])
            with open(path, 'rb') as f:
                stream = cStringIO.StringIO(f.read())
            mi = get_pdf_metadata(stream)

        else:
            self._log("unsupported format: '{}'".format(format))
            return Book()

        if False:
            ''' Perform a bit of voodoo to match Kindle multiple author style '''
            ks_authors = []
            for a in mi.authors:
                if "," in a:
                    # Already ln, fn
                    ks_authors.append(a)
                else:
                    ks_authors.append(author_to_author_sort(a))

        this_book = Book(mi.title, '; '.join(mi.authors))
        this_book.author_sort = '; '.join(mi.authors)
        this_book.datetime = datetime.fromtimestamp(int(book_stats['stats']['st_birthtime'])).timetuple()
        this_book.dateadded = int(book_stats['stats']['st_birthtime'])
        this_book.path = book
        this_book.size = int(book_stats['stats']['st_size'])
        this_book.thumbnail = self._get_kindle_thumb(mi.cover_data[1])
        if this_book.thumbnail:
            this_book.thumb_data = base64.b64encode(this_book.thumbnail)
        else:
            this_book.thumb_data = None
        this_book.title_sort = title_sort(mi.title)
        this_book.uuid = None

        return this_book
Beispiel #30
0
def one_one_in_books(book_id_val_map, db, field, *args):
    'Set a one-one field in the books table'
    if book_id_val_map:
        sequence = ((sqlite_datetime(v), k)
                    for k, v in book_id_val_map.iteritems())
        db.conn.executemany(
            'UPDATE books SET %s=? WHERE id=?' % field.metadata['column'],
            sequence)
        field.table.book_col_map.update(book_id_val_map)
    if field.name == 'title':
        # Set the title sort field
        field.title_sort_field.writer.set_books(
            {k: title_sort(v)
             for k, v in book_id_val_map.iteritems()}, db)
    return set(book_id_val_map)
Beispiel #31
0
    def category_sort_value(self, item_id, book_ids, lang_map):
        lang = None
        tss = tweaks['title_series_sorting']
        if tss != 'strictly_alphabetic':
            c = Counter()

            for book_id in book_ids:
                l = lang_map.get(book_id, None)
                if l:
                    c[l[0]] += 1

            if c:
                lang = c.most_common(1)[0][0]
        val = self.table.id_map[item_id]
        return title_sort(val, order=tss, lang=lang)
Beispiel #32
0
    def category_sort_value(self, item_id, book_ids, lang_map):
        lang = None
        tss = tweaks['title_series_sorting']
        if tss != 'strictly_alphabetic':
            c = Counter()

            for book_id in book_ids:
                l = lang_map.get(book_id, None)
                if l:
                    c[l[0]] += 1

            if c:
                lang = c.most_common(1)[0][0]
        val = self.table.id_map[item_id]
        return title_sort(val, order=tss, lang=lang)
Beispiel #33
0
    def do_tags_list_edit(self, tag, category, is_first_letter=False):
        '''
        Open the 'manage_X' dialog where X == category. If tag is not None, the
        dialog will position the editor on that item.
        '''

        db = self.current_db
        if category == 'series':
            key = lambda x: sort_key(title_sort(x))
        else:
            key = sort_key

        d = TagListEditor(self,
                          category=category,
                          cat_name=db.field_metadata[category]['name'],
                          tag_to_match=tag,
                          get_book_ids=partial(self.get_book_ids,
                                               db=db,
                                               category=category),
                          sorter=key,
                          ttm_is_first_letter=is_first_letter,
                          fm=db.field_metadata[category])
        d.exec_()
        if d.result() == QDialog.DialogCode.Accepted:
            to_rename = d.to_rename  # dict of old id to new name
            to_delete = d.to_delete  # list of ids
            orig_name = d.original_names  # dict of id: name

            if (category in ['tags', 'series', 'publisher']
                    or db.new_api.field_metadata.is_custom_field(category)):
                m = self.tags_view.model()
                for item in to_delete:
                    m.delete_item_from_all_user_categories(
                        orig_name[item], category)
                for old_id in to_rename:
                    m.rename_item_in_all_user_categories(
                        orig_name[old_id], category,
                        unicode_type(to_rename[old_id]))

                db.new_api.remove_items(category, to_delete)
                db.new_api.rename_items(category,
                                        to_rename,
                                        change_index=False)

                # Clean up the library view
                self.do_tag_item_renamed()
                self.tags_view.recount()
Beispiel #34
0
    def do_tags_list_edit(self, tag, category):
        '''
        Open the 'manage_X' dialog where X == category. If tag is not None, the
        dialog will position the editor on that item.
        '''

        tags_model = self.tags_view.model()
        result = tags_model.get_category_editor_data(category)
        if result is None:
            return

        if category == 'series':
            key = lambda x: sort_key(title_sort(x))
        else:
            key = sort_key

        db = self.library_view.model().db
        d = TagListEditor(self,
                          cat_name=db.field_metadata[category]['name'],
                          tag_to_match=tag,
                          data=result,
                          sorter=key)
        d.exec_()
        if d.result() == d.Accepted:
            to_rename = d.to_rename  # dict of old id to new name
            to_delete = d.to_delete  # list of ids
            orig_name = d.original_names  # dict of id: name

            if (category in ['tags', 'series', 'publisher']
                    or db.new_api.field_metadata.is_custom_field(category)):
                m = self.tags_view.model()
                for item in to_delete:
                    m.delete_item_from_all_user_categories(
                        orig_name[item], category)
                for old_id in to_rename:
                    m.rename_item_in_all_user_categories(
                        orig_name[old_id], category,
                        unicode(to_rename[old_id]))

                db.new_api.remove_items(category, to_delete)
                db.new_api.rename_items(category,
                                        to_rename,
                                        change_index=False)

                # Clean up the library view
                self.do_tag_item_renamed()
                self.tags_view.recount()
Beispiel #35
0
 def pre_commit_check(self):
     if self.db is None:
         return True
     db = self.db.new_api
     title, authors = self.get_title_and_authors()
     try:
         if title != db.field_for('title', self.book_id):
             db.set_field('title', {self.book_id: title})
             langs = db.field_for('languages', self.book_id)
             if langs:
                 db.set_field('sort',
                              {self.book_id: title_sort(title, langs[0])})
         if list(authors) != list(db.field_for('authors', self.book_id)):
             db.set_field('authors', {self.book_id: authors})
         if self.cover_changed and self.cover_data is not None:
             self.db.set_cover(self.book_id, self.cover_data)
     except EnvironmentError as err:
         if getattr(err, 'errno',
                    None) == errno.EACCES:  # Permission denied
             import traceback
             fname = getattr(err, 'filename', None) or 'file'
             error_dialog(self,
                          _('Permission denied'),
                          _('Could not open %s. Is it being used by another'
                            ' program?') % fname,
                          det_msg=traceback.format_exc(),
                          show=True)
             return False
     publisher = self.publisher.text().strip()
     if publisher != db.field_for('publisher', self.book_id):
         db.set_field('publisher', {self.book_id: publisher})
     author_sort = self.author_sort.text().strip()
     if author_sort != db.field_for('author_sort', self.book_id):
         db.set_field('author_sort', {self.book_id: author_sort})
     tags = [t.strip() for t in self.tags.text().strip().split(',')]
     if tags != list(db.field_for('tags', self.book_id)):
         db.set_field('tags', {self.book_id: tags})
     series_index = float(self.series_index.value())
     series = self.series.currentText().strip()
     if series != db.field_for('series', self.book_id):
         db.set_field('series', {self.book_id: series})
     if series and series_index != db.field_for('series_index',
                                                self.book_id):
         db.set_field('series_index', {self.book_id: series_index})
     return True
Beispiel #36
0
    def do_tags_list_edit(self, tag, category):
        '''
        Open the 'manage_X' dialog where X == category. If tag is not None, the
        dialog will position the editor on that item.
        '''

        db = self.current_db

        def get_book_ids(use_virtual_library):
            book_ids = None if not use_virtual_library else self.tags_view.model().get_book_ids_to_use()
            data = db.new_api.get_categories(book_ids=book_ids)
            if category in data:
                result = [(t.id, t.original_name, t.count) for t in data[category] if t.count > 0]
            else:
                result = None
            return result

        if category == 'series':
            key = lambda x:sort_key(title_sort(x))
        else:
            key = sort_key

        d = TagListEditor(self, cat_name=db.field_metadata[category]['name'],
                          tag_to_match=tag, get_book_ids=get_book_ids, sorter=key)
        d.exec_()
        if d.result() == d.Accepted:
            to_rename = d.to_rename  # dict of old id to new name
            to_delete = d.to_delete  # list of ids
            orig_name = d.original_names  # dict of id: name

            if (category in ['tags', 'series', 'publisher'] or
                    db.new_api.field_metadata.is_custom_field(category)):
                m = self.tags_view.model()
                for item in to_delete:
                    m.delete_item_from_all_user_categories(orig_name[item], category)
                for old_id in to_rename:
                    m.rename_item_in_all_user_categories(orig_name[old_id],
                                            category, unicode_type(to_rename[old_id]))

                db.new_api.remove_items(category, to_delete)
                db.new_api.rename_items(category, to_rename, change_index=False)

                # Clean up the library view
                self.do_tag_item_renamed()
                self.tags_view.recount()
Beispiel #37
0
    def do_tags_list_edit(self, tag, category):
        '''
        Open the 'manage_X' dialog where X == category. If tag is not None, the
        dialog will position the editor on that item.
        '''

        tags_model = self.tags_view.model()
        result = tags_model.get_category_editor_data(category)
        if result is None:
            return

        if category == 'series':
            key = lambda x:sort_key(title_sort(x))
        else:
            key = sort_key

        db=self.library_view.model().db
        d = TagListEditor(self, cat_name=db.field_metadata[category]['name'],
                          tag_to_match=tag, data=result, sorter=key)
        d.exec_()
        if d.result() == d.Accepted:
            to_rename = d.to_rename  # dict of old id to new name
            to_delete = d.to_delete  # list of ids
            orig_name = d.original_names  # dict of id: name

            if (category in ['tags', 'series', 'publisher'] or
                    db.new_api.field_metadata.is_custom_field(category)):
                m = self.tags_view.model()
                for item in to_delete:
                    m.delete_item_from_all_user_categories(orig_name[item], category)
                for old_id in to_rename:
                    m.rename_item_in_all_user_categories(orig_name[old_id],
                                            category, unicode(to_rename[old_id]))

                db.new_api.remove_items(category, to_delete)
                db.new_api.rename_items(category, to_rename, change_index=False)

                # Clean up the library view
                self.do_tag_item_renamed()
                self.tags_view.recount()
Beispiel #38
0
 def pre_commit_check(self):
     if self.db is None:
         return True
     db = self.db.new_api
     title, authors = self.get_title_and_authors()
     try:
         if title != db.field_for('title', self.book_id):
             db.set_field('title', {self.book_id:title})
             langs = db.field_for('languages', self.book_id)
             if langs:
                 db.set_field('sort', {self.book_id:title_sort(title, langs[0])})
         if list(authors) != list(db.field_for('authors', self.book_id)):
             db.set_field('authors', {self.book_id:authors})
         if self.cover_changed and self.cover_data is not None:
             self.db.set_cover(self.book_id, self.cover_data)
     except EnvironmentError as err:
         if getattr(err, 'errno', None) == errno.EACCES:  # Permission denied
             import traceback
             fname = getattr(err, 'filename', None) or 'file'
             error_dialog(self, _('Permission denied'),
                     _('Could not open %s. Is it being used by another'
                     ' program?')%fname, det_msg=traceback.format_exc(), show=True)
             return False
     publisher = self.publisher.text().strip()
     if publisher != db.field_for('publisher', self.book_id):
         db.set_field('publisher', {self.book_id:publisher})
     author_sort = self.author_sort.text().strip()
     if author_sort != db.field_for('author_sort', self.book_id):
         db.set_field('author_sort', {self.book_id:author_sort})
     tags = [t.strip() for t in self.tags.text().strip().split(',')]
     if tags != list(db.field_for('tags', self.book_id)):
         db.set_field('tags', {self.book_id:tags})
     series_index = float(self.series_index.value())
     series = self.series.currentText().strip()
     if series != db.field_for('series', self.book_id):
         db.set_field('series', {self.book_id:series})
     if series and series_index != db.field_for('series_index', self.book_id):
         db.set_field('series_index', {self.book_id:series_index})
     return True
Beispiel #39
0
 def auto_generate(self, *args):
     self.current_val = title_sort(self.title_edit.current_val,
             lang=self.book_lang)
Beispiel #40
0
 def title_sorter(self):
     ans = getattr(self, 'title_sort', None)
     if not ans or self.is_null('title_sort') or ans == _('Unknown'):
         ans = ''
     return ans or title_sort(self.title or '')
Beispiel #41
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
Beispiel #42
0
    def itervals(self, record):
        for name, fm in self.entries:
            dt = fm['datatype']
            val = record[fm['rec_index']]
            if dt == 'composite':
                sb = fm['display'].get('composite_sort', 'text')
                if sb == 'date':
                    try:
                        val = parse_date(val)
                    except:
                        val = UNDEFINED_DATE
                    dt = 'datetime'
                elif sb == 'number':
                    try:
                        p = 1
                        for i, candidate in enumerate(
                                    ('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB')):
                            if val.endswith(candidate):
                                p = 1024**(i)
                                val = val[:-len(candidate)].strip()
                                break
                        val = locale.atof(val) * p
                    except:
                        val = 0.0
                    dt = 'float'
                elif sb == 'bool':
                    val = force_to_bool(val)
                    dt = 'bool'

            if dt == 'datetime':
                if val is None:
                    val = UNDEFINED_DATE
                if tweaks['sort_dates_using_visible_fields']:
                    format = None
                    if name == 'timestamp':
                        format = tweaks['gui_timestamp_display_format']
                    elif name == 'pubdate':
                        format = tweaks['gui_pubdate_display_format']
                    elif name == 'last_modified':
                        format = tweaks['gui_last_modified_display_format']
                    elif fm['is_custom']:
                        format = fm['display'].get('date_format', None)
                    val = clean_date_for_sort(val, format)
            elif dt == 'series':
                if val is None:
                    val = ('', 1)
                else:
                    if self.library_order:
                        try:
                            lang = record[self.lang_idx].partition(u',')[0]
                        except (AttributeError, ValueError, KeyError,
                                IndexError, TypeError):
                            lang = None
                        val = title_sort(val, order='library_order', lang=lang)
                    sidx_fm = self.field_metadata[name + '_index']
                    sidx = record[sidx_fm['rec_index']]
                    val = (self.string_sort_key(val), sidx)

            elif dt in ('text', 'comments', 'composite', 'enumeration'):
                if val:
                    if fm['is_multiple']:
                        jv = fm['is_multiple']['list_to_ui']
                        sv = fm['is_multiple']['cache_to_list']
                        if '&' in jv:
                            val = jv.join(
                                [author_to_author_sort(v) for v in val.split(sv)])
                        else:
                            val = jv.join(sorted(val.split(sv),
                                              key=self.string_sort_key))
                val = self.string_sort_key(val)

            elif dt == 'bool':
                if not self.db_prefs.get('bools_are_tristate'):
                    val = {True: 1, False: 2, None: 2}.get(val, 2)
                else:
                    val = {True: 1, False: 2, None: 3}.get(val, 3)

            yield val
Beispiel #43
0
def set_title(book_id_val_map, db, field, *args):
    ans = one_one_in_books(book_id_val_map, db, field, *args)
    # Set the title sort field
    field.title_sort_field.writer.set_books(
        {k:title_sort(v) for k, v in book_id_val_map.iteritems()}, db)
    return ans
 def title_sorter(self):
     return title_sort(self.title)
Beispiel #45
0
    def test_many_many_basic(self):  # {{{
        'Test the different code paths for writing to a many-many field'
        cl = self.cloned_library
        cache = self.init_cache(cl)
        ae, af, sf = self.assertEqual, self.assertFalse, cache.set_field

        # Tags
        ae(sf('#tags', {1:cache.field_for('tags', 1), 2:cache.field_for('tags', 2)}),
            {1, 2})
        for name in ('tags', '#tags'):
            f = cache.fields[name]
            af(sf(name, {1:('News', 'tag one')}, allow_case_change=False))
            ae(sf(name, {1:'tag one, News'}), {1, 2})
            ae(sf(name, {3:('tag two', 'sep,sep2')}), {2, 3})
            ae(len(f.table.id_map), 4)
            ae(sf(name, {1:None}), {1})
            cache2 = self.init_cache(cl)
            for c in (cache, cache2):
                ae(c.field_for(name, 3), ('tag two', 'sep;sep2'))
                ae(len(c.fields[name].table.id_map), 3)
                ae(len(c.fields[name].table.id_map), 3)
                ae(c.field_for(name, 1), ())
                ae(c.field_for(name, 2), ('tag two', 'tag one'))
            del cache2

        # Authors
        ae(sf('#authors', {k:cache.field_for('authors', k) for k in (1,2,3)}),
           {1,2,3})

        for name in ('authors', '#authors'):
            f = cache.fields[name]
            ae(len(f.table.id_map), 3)
            af(cache.set_field(name, {3:'Unknown'}))
            ae(cache.set_field(name, {3:'Kovid Goyal & Divok Layog'}), {3})
            ae(cache.set_field(name, {1:'', 2:'An, Author'}), {1,2})
            cache2 = self.init_cache(cl)
            for c in (cache, cache2):
                ae(len(c.fields[name].table.id_map), 4 if name =='authors' else 3)
                ae(c.field_for(name, 3), ('Kovid Goyal', 'Divok Layog'))
                ae(c.field_for(name, 2), ('An, Author',))
                ae(c.field_for(name, 1), (_('Unknown'),) if name=='authors' else ())
                if name == 'authors':
                    ae(c.field_for('author_sort', 1), author_to_author_sort(_('Unknown')))
                    ae(c.field_for('author_sort', 2), author_to_author_sort('An, Author'))
                    ae(c.field_for('author_sort', 3), author_to_author_sort('Kovid Goyal') + ' & ' + author_to_author_sort('Divok Layog'))
            del cache2
        ae(cache.set_field('authors', {1:'KoviD GoyaL'}), {1, 3})
        ae(cache.field_for('author_sort', 1), 'GoyaL, KoviD')
        ae(cache.field_for('author_sort', 3), 'GoyaL, KoviD & Layog, Divok')

        # Languages
        f = cache.fields['languages']
        ae(f.table.id_map, {1: 'eng', 2: 'deu'})
        ae(sf('languages', {1:''}), {1})
        ae(cache.field_for('languages', 1), ())
        ae(sf('languages', {2:('und',)}), {2})
        af(f.table.id_map)
        ae(sf('languages', {1:'eng,fra,deu', 2:'es,Dutch', 3:'English'}), {1, 2, 3})
        ae(cache.field_for('languages', 1), ('eng', 'fra', 'deu'))
        ae(cache.field_for('languages', 2), ('spa', 'nld'))
        ae(cache.field_for('languages', 3), ('eng',))
        ae(sf('languages', {3:None}), {3})
        ae(cache.field_for('languages', 3), ())
        ae(sf('languages', {1:'deu,fra,eng'}), {1}, 'Changing order failed')
        ae(sf('languages', {2:'deu,eng,eng'}), {2})
        cache2 = self.init_cache(cl)
        for c in (cache, cache2):
            ae(cache.field_for('languages', 1), ('deu', 'fra', 'eng'))
            ae(cache.field_for('languages', 2), ('deu', 'eng'))
        del cache2

        # Identifiers
        f = cache.fields['identifiers']
        ae(sf('identifiers', {3: 'one:1,two:2'}), {3})
        ae(sf('identifiers', {2:None}), {2})
        ae(sf('identifiers', {1: {'test':'1', 'two':'2'}}), {1})
        cache2 = self.init_cache(cl)
        for c in (cache, cache2):
            ae(c.field_for('identifiers', 3), {'one':'1', 'two':'2'})
            ae(c.field_for('identifiers', 2), {})
            ae(c.field_for('identifiers', 1), {'test':'1', 'two':'2'})
        del cache2

        # Test setting of title sort
        ae(sf('title', {1:'The Moose', 2:'Cat'}), {1, 2})
        cache2 = self.init_cache(cl)
        for c in (cache, cache2):
            ae(c.field_for('sort', 1), title_sort('The Moose'))
            ae(c.field_for('sort', 2), title_sort('Cat'))

        # Test setting with the same value repeated
        ae(sf('tags', {3: ('a', 'b', 'a')}), {3})
        ae(sf('tags', {3: ('x', 'X')}), {3}, 'Failed when setting tag twice with different cases')
        ae(('x',), cache.field_for('tags', 3))

        # Test setting of authors with | in their names (for legacy database
        # format compatibility | is replaced by ,)
        ae(sf('authors', {3: ('Some| Author',)}), {3})
        ae(('Some, Author',), cache.field_for('authors', 3))
def get_components(template, mi, id, timefmt='%b %Y', length=250,
        sanitize_func=ascii_filename, replace_whitespace=False,
        to_lowercase=False, safe_format=True, last_has_extension=True,
        single_dir=False):
    tsorder = tweaks['save_template_title_series_sorting']
    format_args = FORMAT_ARGS.copy()
    format_args.update(mi.all_non_none_fields())
    if mi.title:
        if tsorder == 'strictly_alphabetic':
            v = mi.title
        else:
            # title_sort might be missing or empty. Check both conditions
            v = mi.get('title_sort', None)
            if not v:
                v = title_sort(mi.title, order=tsorder)
        format_args['title'] = v
    if mi.authors:
        format_args['authors'] = mi.format_authors()
        format_args['author'] = format_args['authors']
    if mi.tags:
        format_args['tags'] = mi.format_tags()
        if format_args['tags'].startswith('/'):
            format_args['tags'] = format_args['tags'][1:]
    else:
        format_args['tags'] = ''
    if mi.series:
        format_args['series'] = title_sort(mi.series, order=tsorder)
        if mi.series_index is not None:
            format_args['series_index'] = mi.format_series_index()
    else:
        template = re.sub(r'\{series_index[^}]*?\}', '', template)
    if mi.rating is not None:
        format_args['rating'] = mi.format_rating(divide_by=2.0)
    if mi.identifiers:
        format_args['identifiers'] = mi.format_field_extended('identifiers')[1]
    else:
        format_args['identifiers'] = ''

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

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

    if single_dir:
        components = components[-1:]
    return shorten_components_to(length, components, last_has_extension=last_has_extension)
Beispiel #47
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
Beispiel #48
0
    def update_text_record(self, record, book, path, bl_index, gtz_count, ltz_count, use_tz_var):
        """
        Update the Sony database from the book. This is done if the timestamp in
        the db differs from the timestamp on the file.
        """

        # It seems that a Sony device can sometimes know what timezone it is in,
        # and apparently converts the dates to GMT when it writes them to its
        # DB. We can detect that a device is timezone-aware because there is a
        # 'tz' variable in the Sony DB, which we can set to "0" to tell the
        # device to ignore its own timezone when comparing mtime to the date in
        # the DB.

        # Unfortunately, if there is no tz variable in the DB, then we can't
        # tell when the device applies a timezone conversion. We use a horrible
        # heuristic to work around this problem. First, set dates only for new
        # books, trying to avoid upsetting the sony. Second, voting: if a book
        # is not new, compare its Sony DB date against localtime and gmtime.
        # Count the matches. When we must set a date, use the one with the most
        # matches. Use localtime if the case of a tie, and hope it is right.
        try:
            timestamp = os.path.getmtime(path)
        except:
            debug_print("Failed to get timestamp for:", path)
            timestamp = time.time()
        rec_date = record.get("date", None)

        def clean(x):
            if isbytestring(x):
                x = x.decode(preferred_encoding, "replace")
            x.replace(u"\0", "")
            return x

        def record_set(k, v):
            try:
                record.set(k, clean(v))
            except:
                # v is not suitable for XML, ignore
                pass

        if not getattr(book, "_new_book", False):  # book is not new
            if record.get("tz", None) is not None:
                use_tz_var = True
            if strftime(timestamp, zone=time.gmtime) == rec_date:
                gtz_count += 1
            elif strftime(timestamp, zone=time.localtime) == rec_date:
                ltz_count += 1
        else:  # book is new. Set the time using the current votes
            if use_tz_var:
                tz = time.localtime
                record.set("tz", "0")
                debug_print("Use localtime TZ and tz='0' for new book", book.lpath)
            elif ltz_count >= gtz_count:
                tz = time.localtime
                debug_print("Use localtime TZ for new book", book.lpath)
            else:
                tz = time.gmtime
                debug_print("Use GMT TZ for new book", book.lpath)
            date = strftime(timestamp, zone=tz)
            record.set("date", clean(date))
        try:
            record.set("size", clean(str(os.stat(path).st_size)))
        except:
            record.set("size", "0")
        title = book.title if book.title else _("Unknown")
        record_set("title", title)
        ts = book.title_sort
        if not ts:
            ts = title_sort(title)
        record_set("titleSorter", ts)
        if self.use_author_sort:
            if book.author_sort:
                aus = book.author_sort
            else:
                debug_print("Author_sort is None for book", book.lpath)
                aus = authors_to_sort_string(book.authors)
            record_set("author", aus)
        else:
            record_set("author", authors_to_string(book.authors))
        ext = os.path.splitext(path)[1]
        if ext:
            ext = ext[1:].lower()
            mime = MIME_MAP.get(ext, None)
            if mime is None:
                mime = guess_type("a." + ext)[0]
            if mime is not None:
                record.set("mime", clean(mime))
        if "sourceid" not in record.attrib:
            record.set("sourceid", "1")
        if "id" not in record.attrib:
            num = self.max_id(record.getroottree().getroot())
            record.set("id", str(num + 1))
        return (gtz_count, ltz_count, use_tz_var)
Beispiel #49
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
Beispiel #50
0
 def fget(self):
     return title_sort(self.title)
Beispiel #51
0
 def __init__(self, text):
     tableItem.__init__(self, text)
     self.sort = title_sort(text.lower())
Beispiel #52
0
    def update_text_record(self, record, book, path, bl_index,
                           gtz_count, ltz_count, use_tz_var):
        '''
        Update the Sony database from the book. This is done if the timestamp in
        the db differs from the timestamp on the file.
        '''

        # It seems that a Sony device can sometimes know what timezone it is in,
        # and apparently converts the dates to GMT when it writes them to its
        # DB. We can detect that a device is timezone-aware because there is a
        # 'tz' variable in the Sony DB, which we can set to "0" to tell the
        # device to ignore its own timezone when comparing mtime to the date in
        # the DB.

        # Unfortunately, if there is no tz variable in the DB, then we can't
        # tell when the device applies a timezone conversion. We use a horrible
        # heuristic to work around this problem. First, set dates only for new
        # books, trying to avoid upsetting the sony. Second, voting: if a book
        # is not new, compare its Sony DB date against localtime and gmtime.
        # Count the matches. When we must set a date, use the one with the most
        # matches. Use localtime if the case of a tie, and hope it is right.
        try:
            timestamp = os.path.getmtime(path)
        except:
            debug_print('Failed to get timestamp for:', path)
            timestamp = time.time()
        rec_date = record.get('date', None)

        def clean(x):
            if isbytestring(x):
                x = x.decode(preferred_encoding, 'replace')
            x.replace(u'\0', '')
            return x

        def record_set(k, v):
            try:
                record.set(k, clean(v))
            except:
                # v is not suitable for XML, ignore
                pass

        if not getattr(book, '_new_book', False):  # book is not new
            if record.get('tz', None) is not None:
                use_tz_var = True
            if strftime(timestamp, zone=time.gmtime) == rec_date:
                gtz_count += 1
            elif strftime(timestamp, zone=time.localtime) == rec_date:
                ltz_count += 1
        else:  # book is new. Set the time using the current votes
            if use_tz_var:
                tz = time.localtime
                record.set('tz', '0')
                debug_print("Use localtime TZ and tz='0' for new book", book.lpath)
            elif ltz_count >= gtz_count:
                tz = time.localtime
                debug_print("Use localtime TZ for new book", book.lpath)
            else:
                tz = time.gmtime
                debug_print("Use GMT TZ for new book", book.lpath)
            date = strftime(timestamp, zone=tz)
            record.set('date', clean(date))
        try:
            record.set('size', clean(str(os.stat(path).st_size)))
        except:
            record.set('size', '0')
        title = book.title if book.title else _('Unknown')
        record_set('title', title)
        ts = book.title_sort
        if not ts:
            ts = title_sort(title)
        record_set('titleSorter', ts)
        if self.use_author_sort:
            if book.author_sort:
                aus = book.author_sort
            else:
                debug_print('Author_sort is None for book', book.lpath)
                aus = authors_to_sort_string(book.authors)
            record_set('author', aus)
        else:
            record_set('author', authors_to_string(book.authors))
        ext = os.path.splitext(path)[1]
        if ext:
            ext = ext[1:].lower()
            mime = MIME_MAP.get(ext, None)
            if mime is None:
                mime = guess_type('a.'+ext)[0]
            if mime is not None:
                record.set('mime', clean(mime))
        if 'sourceid' not in record.attrib:
            record.set('sourceid', '1')
        if 'id' not in record.attrib:
            num = self.max_id(record.getroottree().getroot())
            record.set('id', str(num+1))
        return (gtz_count, ltz_count, use_tz_var)
Beispiel #53
0
def get_components(template, mi, id, timefmt='%b %Y', length=250,
        sanitize_func=ascii_filename, replace_whitespace=False,
        to_lowercase=False, safe_format=True):

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

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

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

    return shorten_components_to(length, components)
 def __init__(self, text):
     tableItem.__init__(self, text)
     self.sort = title_sort(text.lower())
Beispiel #55
0
 def title_sorter(self):
     '''String to sort the title. If absent, title is returned'''
     return title_sort(self.title)
 def title_sorter(self):
     return title_sort(self.title)