def process_duplicates(self, db, duplicates): ta = _('%(title)s by %(author)s') bf = QFont(self.dup_list.font()) bf.setBold(True) itf = QFont(self.dup_list.font()) itf.setItalic(True) for mi, cover, formats in duplicates: item = QTreeWidgetItem([ta%dict( title=mi.title, author=mi.format_field('authors')[1])] , 0) item.setCheckState(0, Qt.Checked) item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable) item.setData(0, Qt.FontRole, bf) item.setData(0, Qt.UserRole, (mi, cover, formats)) matching_books = db.books_with_same_title(mi) def add_child(text): c = QTreeWidgetItem([text], 1) c.setFlags(Qt.ItemIsEnabled) item.addChild(c) return c add_child(_('Already in calibre:')).setData(0, Qt.FontRole, itf) for book_id in matching_books: aut = [a.replace('|', ',') for a in (db.authors(book_id, index_is_id=True) or '').split(',')] add_child(ta%dict( title=db.title(book_id, index_is_id=True), author=authors_to_string(aut))) add_child('') yield item
def create_book(mi, path, fmt='epub', opf_name='metadata.opf', html_name='start.xhtml', toc_name='toc.ncx'): ''' Create an empty book in the specified format at the specified location. ''' path = os.path.abspath(path) lang = 'und' opf = metadata_to_opf(mi, as_string=False) for l in opf.xpath('//*[local-name()="language"]'): if l.text: lang = l.text break lang = lang_as_iso639_1(lang) or lang opfns = OPF_NAMESPACES['opf'] m = opf.makeelement('{%s}manifest' % opfns) opf.insert(1, m) i = m.makeelement('{%s}item' % opfns, href=html_name, id='start') i.set('media-type', guess_type('a.xhtml')) m.append(i) i = m.makeelement('{%s}item' % opfns, href=toc_name, id='ncx') i.set('media-type', guess_type(toc_name)) m.append(i) s = opf.makeelement('{%s}spine' % opfns, toc="ncx") opf.insert(2, s) i = s.makeelement('{%s}itemref' % opfns, idref='start') s.append(i) CONTAINER = '''\ <?xml version="1.0"?> <container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container"> <rootfiles> <rootfile full-path="{0}" media-type="application/oebps-package+xml"/> </rootfiles> </container> '''.format(prepare_string_for_xml(opf_name, True)).encode('utf-8') HTML = P('templates/new_book.html', data=True).decode('utf-8').replace( '_LANGUAGE_', prepare_string_for_xml(lang, True) ).replace( '_TITLE_', prepare_string_for_xml(mi.title) ).replace( '_AUTHORS_', prepare_string_for_xml(authors_to_string(mi.authors)) ).encode('utf-8') h = parse(HTML) pretty_html_tree(None, h) HTML = serialize(h, 'text/html') ncx = etree.tostring(create_toc(mi, opf, html_name, lang), encoding='utf-8', xml_declaration=True, pretty_print=True) pretty_xml_tree(opf) opf = etree.tostring(opf, encoding='utf-8', xml_declaration=True, pretty_print=True) if fmt == 'azw3': with TemporaryDirectory('create-azw3') as tdir, CurrentDir(tdir): for name, data in ((opf_name, opf), (html_name, HTML), (toc_name, ncx)): with open(name, 'wb') as f: f.write(data) c = Container(os.path.dirname(os.path.abspath(opf_name)), opf_name, DevNull()) opf_to_azw3(opf_name, path, c) else: with ZipFile(path, 'w', compression=ZIP_STORED) as zf: zf.writestr('mimetype', b'application/epub+zip', compression=ZIP_STORED) zf.writestr('META-INF/', b'', 0755) zf.writestr('META-INF/container.xml', CONTAINER) zf.writestr(opf_name, opf) zf.writestr(html_name, HTML) zf.writestr(toc_name, ncx)
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]) }
def _metadata(self, metadata): ''' Metadata takes the form: title\x00 author\x00 copyright\x00 publisher\x00 isbn\x00 ''' title = _('Unknown') author = _('Unknown') copyright = '' publisher = '' isbn = '' if metadata: if len(metadata.title) >= 1: title = metadata.title[0].value if len(metadata.creator) >= 1: from calibre.ebooks.metadata import authors_to_string author = authors_to_string([x.value for x in metadata.creator]) if len(metadata.rights) >= 1: copyright = metadata.rights[0].value if len(metadata.publisher) >= 1: publisher = metadata.publisher[0].value return '%s\x00%s\x00%s\x00%s\x00%s\x00' % (title, author, copyright, publisher, isbn)
def format_report(self): report = [] a = report.append def indent(text): text = force_unicode(text) return '\xa0\xa0\xa0\xa0' + '\n\xa0\xa0\xa0\xa0'.join(text.splitlines()) for book_id, errors in iteritems(self.errors): types = {t for t, data in errors} title, authors = self.book_id_data(book_id).title, authors_to_string(self.book_id_data(book_id).authors[:1]) if report: a('\n' + ('_'*70) + '\n') if 'critical' in types: a(_('Failed to save: {0} by {1} to disk, with error:').format(title, authors)) for t, tb in errors: if t == 'critical': a(indent(tb)) else: errs = defaultdict(list) for t, data in errors: errs[t].append(data) for fmt, tb in errs['fmt']: a(_('Failed to save the {2} format of: {0} by {1} to disk, with error:').format(title, authors, fmt.upper())) a(indent(tb)), a('') for fmt, tb in errs['metadata']: if fmt: a(_('Failed to update the metadata in the {2} format of: {0} by {1}, with error:').format(title, authors, fmt.upper())) else: a(_('Failed to update the metadata in all formats of: {0} by {1}, with error:').format(title, authors)) a(indent(tb)), a('') return '\n'.join(report)
def set_metadata_(path, opath, title, authors, bkp, tags): p = podofo.PDFDoc() p.open(path) title = prep(title) touched = False if title and title != p.title: p.title = title touched = True author = prep(authors_to_string(authors)) if author and author != p.author: p.author = author touched = True bkp = prep(bkp) if bkp and bkp != p.creator: p.creator = bkp touched = True try: tags = prep(u', '.join([x.strip() for x in tags if x.strip()])) if tags != p.keywords: p.keywords = tags touched = True except: pass if touched: p.save(opath) return True return False
def read_metadata(root): ans = Metadata(_('Unknown'), [_('Unknown')]) prefixes, refines = read_prefixes(root), read_refines(root) identifiers = read_identifiers(root, prefixes, refines) ids = {} for key, vals in identifiers.iteritems(): if key == 'calibre': ans.application_id = vals[0] elif key != 'uuid': ids[key] = vals[0] ans.set_identifiers(ids) ans.title = read_title(root, prefixes, refines) or ans.title ans.title_sort = read_title_sort(root, prefixes, refines) or ans.title_sort ans.languages = read_languages(root, prefixes, refines) or ans.languages auts, aus = [], [] for a in read_authors(root, prefixes, refines): auts.append(a.name), aus.append(a.sort) ans.authors = auts or ans.authors ans.author_sort = authors_to_string(aus) or ans.author_sort bkp = read_book_producers(root, prefixes, refines) if bkp: ans.book_producer = bkp[0] pd = read_pubdate(root, prefixes, refines) if not is_date_undefined(pd): ans.pubdate = pd ts = read_timestamp(root, prefixes, refines) if not is_date_undefined(ts): ans.timestamp = ts lm = read_last_modified(root, prefixes, refines) if not is_date_undefined(lm): ans.last_modified = lm return ans
def set_metadata_(tdir, title, authors, bkp, tags): podofo = get_podofo() os.chdir(tdir) p = podofo.PDFDoc() p.open(u'input.pdf') title = prep(title) touched = False if title and title != p.title: p.title = title touched = True author = prep(authors_to_string(authors)) if author and author != p.author: p.author = author touched = True bkp = prep(bkp) if bkp and bkp != p.creator: p.creator = bkp touched = True try: tags = prep(u', '.join([x.strip() for x in tags if x.strip()])) if tags != p.keywords: p.keywords = tags touched = True except: pass if touched: p.save(u'output.pdf') return touched
def default_cover(self): ''' Create a generic cover for books that dont have a cover ''' from calibre.ebooks.metadata import authors_to_string, fmt_sidx if self.no_default_cover: return None self.log('Generating default cover') m = self.oeb.metadata title = unicode(m.title[0]) authors = [unicode(x) for x in m.creator if x.role == 'aut'] series_string = None if m.series and m.series_index: series_string = _('Book %(sidx)s of %(series)s')%dict( sidx=fmt_sidx(m.series_index[0], use_roman=True), series=unicode(m.series[0])) try: from calibre.ebooks import calibre_cover img_data = calibre_cover(title, authors_to_string(authors), series_string=series_string) id, href = self.oeb.manifest.generate('cover', u'cover_image.jpg') item = self.oeb.manifest.add(id, href, guess_type('t.jpg')[0], data=img_data) m.clear('cover') m.add('cover', item.id) return item.href except: self.log.exception('Failed to generate default cover') return None
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)
def make_rdata(book_id=1, new_book_id=None, action='add'): return { 'title': src_db.field_for('title', book_id), 'authors': list(src_db.field_for('authors', book_id)), 'author': authors_to_string(src_db.field_for('authors', book_id)), 'book_id': book_id, 'new_book_id': new_book_id, 'action': action }
def finalize_apply(self): db = self.gui.current_db db.commit() if self.apply_pd is not None: self.apply_pd.hide() if self.apply_failures: msg = [] for i, tb in self.apply_failures: title = db.title(i, index_is_id=True) authors = db.authors(i, index_is_id=True) if authors: authors = [x.replace('|', ',') for x in authors.split(',')] title += ' - ' + authors_to_string(authors) msg.append(title+'\n\n'+tb+'\n'+('*'*80)) error_dialog(self.gui, _('Some failures'), _('Failed to apply updated metadata for some books' ' in your library. Click "Show Details" to see ' 'details.'), det_msg='\n\n'.join(msg), show=True) changed_books = len(self.applied_ids or ()) self.refresh_gui(self.applied_ids) self.apply_id_map = [] self.apply_pd = None try: if callable(self.apply_callback): self.apply_callback(list(self.applied_ids)) finally: self.apply_callback = None if changed_books: QApplication.alert(self.gui, 2000)
def show_details(self, index): f = rating_font() book = self.model().data(index, Qt.UserRole) parts = [ '<center>', '<h2>%s</h2>'%book.title, '<div><i>%s</i></div>'%authors_to_string(book.authors), ] if not book.is_null('series'): series = book.format_field('series') if series[1]: parts.append('<div>%s: %s</div>'%series) if not book.is_null('rating'): style = 'style=\'font-family:"%s"\''%f parts.append('<div %s>%s</div>'%(style, '\u2605'*int(book.rating))) parts.append('</center>') if book.identifiers: urls = urls_from_identifiers(book.identifiers) ids = ['<a href="%s">%s</a>'%(url, name) for name, ign, ign, url in urls] if ids: parts.append('<div><b>%s:</b> %s</div><br>'%(_('See at'), ', '.join(ids))) if book.tags: parts.append('<div>%s</div><div>\u00a0</div>'%', '.join(book.tags)) if book.comments: parts.append(comments_to_html(book.comments)) self.show_details_signal.emit(''.join(parts))
def ACQUISITION_ENTRY(book_id, updated, request_context): field_metadata = request_context.db.field_metadata mi = request_context.db.get_metadata(book_id) extra = [] if mi.rating > 0: rating = rating_to_stars(mi.rating) extra.append(_('RATING: %s<br />')%rating) if mi.tags: extra.append(_('TAGS: %s<br />')%xml(format_tag_string(mi.tags, None))) if mi.series: extra.append(_('SERIES: %(series)s [%(sidx)s]<br />')% dict(series=xml(mi.series), sidx=fmt_sidx(float(mi.series_index)))) for key in filter(request_context.ctx.is_field_displayable, field_metadata.ignorable_field_keys()): name, val = mi.format_field(key) if val: fm = field_metadata[key] datatype = fm['datatype'] if datatype == 'text' and fm['is_multiple']: extra.append('%s: %s<br />'% (xml(name), xml(format_tag_string(val, fm['is_multiple']['ui_to_list'], joinval=fm['is_multiple']['list_to_ui'])))) elif datatype == 'comments' or (fm['datatype'] == 'composite' and fm['display'].get('contains_html', False)): extra.append('%s: %s<br />'%(xml(name), comments_to_html(unicode(val)))) else: extra.append('%s: %s<br />'%(xml(name), xml(unicode(val)))) if mi.comments: comments = comments_to_html(mi.comments) extra.append(comments) if extra: extra = html_to_lxml('\n'.join(extra)) ans = E.entry(TITLE(mi.title), E.author(E.name(authors_to_string(mi.authors))), ID('urn:uuid:' + mi.uuid), UPDATED(mi.last_modified), E.published(mi.timestamp.isoformat())) if mi.pubdate and not is_date_undefined(mi.pubdate): ans.append(ans.makeelement('{%s}date' % DC_NS)) ans[-1].text = mi.pubdate.isoformat() if len(extra): ans.append(E.content(extra, type='xhtml')) get = partial(request_context.ctx.url_for, '/get', book_id=book_id, library_id=request_context.library_id) if mi.formats: fm = mi.format_metadata for fmt in mi.formats: fmt = fmt.lower() mt = guess_type('a.'+fmt)[0] if mt: link = E.link(type=mt, href=get(what=fmt), rel="http://opds-spec.org/acquisition") ffm = fm.get(fmt.upper()) if ffm: link.set('length', str(ffm['size'])) link.set('mtime', ffm['mtime'].isoformat()) ans.append(link) ans.append(E.link(type='image/jpeg', href=get(what='cover'), rel="http://opds-spec.org/cover")) ans.append(E.link(type='image/jpeg', href=get(what='thumb'), rel="http://opds-spec.org/thumbnail")) return ans
def ACQUISITION_ENTRY(book_id, updated, request_context): field_metadata = request_context.db.field_metadata mi = request_context.db.get_metadata(book_id) extra = [] if mi.rating > 0: rating = u"".join(repeat(u"\u2605", int(mi.rating / 2.0))) extra.append(_("RATING: %s<br />") % rating) if mi.tags: extra.append(_("TAGS: %s<br />") % xml(format_tag_string(mi.tags, None))) if mi.series: extra.append( _("SERIES: %(series)s [%(sidx)s]<br />") % dict(series=xml(mi.series), sidx=fmt_sidx(float(mi.series_index))) ) for key in filter(request_context.ctx.is_field_displayable, field_metadata.ignorable_field_keys()): name, val = mi.format_field(key) if val: fm = field_metadata[key] datatype = fm["datatype"] if datatype == "text" and fm["is_multiple"]: extra.append( "%s: %s<br />" % ( xml(name), xml( format_tag_string( val, fm["is_multiple"]["ui_to_list"], joinval=fm["is_multiple"]["list_to_ui"] ) ), ) ) elif datatype == "comments" or ( fm["datatype"] == "composite" and fm["display"].get("contains_html", False) ): extra.append("%s: %s<br />" % (xml(name), comments_to_html(unicode(val)))) else: extra.append("%s: %s<br />" % (xml(name), xml(unicode(val)))) if mi.comments: comments = comments_to_html(mi.comments) extra.append(comments) if extra: extra = html_to_lxml("\n".join(extra)) ans = E.entry( TITLE(mi.title), E.author(E.name(authors_to_string(mi.authors))), ID("urn:uuid:" + mi.uuid), UPDATED(updated) ) if len(extra): ans.append(E.content(extra, type="xhtml")) get = partial(request_context.ctx.url_for, "/get", book_id=book_id, library_id=request_context.library_id) if mi.formats: for fmt in mi.formats: fmt = fmt.lower() mt = guess_type("a." + fmt)[0] if mt: ans.append(E.link(type=mt, href=get(what=fmt), rel="http://opds-spec.org/acquisition")) ans.append(E.link(type="image/jpeg", href=get(what="cover"), rel="http://opds-spec.org/cover")) ans.append(E.link(type="image/jpeg", href=get(what="thumb"), rel="http://opds-spec.org/thumbnail")) return ans
def book_filename(rd, book_id, mi, fmt): au = authors_to_string(mi.authors or [_('Unknown')]) title = mi.title or _('Unknown') ext = (fmt or '').lower() if ext == 'kepub' and 'Kobo Touch' in rd.inheaders.get('User-Agent', ''): ext = 'kepub.epub' fname = '%s - %s_%s.%s' % (title[:30], au[:30], book_id, ext) fname = ascii_filename(fname).replace('"', '_') return fname
def search_kobo(query, max_results=10, timeout=60, write_html_to=None): from css_selectors import Select url = 'http://www.kobobooks.com/search/search.html?q=' + urllib.quote_plus(query) br = browser() with closing(br.open(url, timeout=timeout)) as f: raw = f.read() if write_html_to is not None: with open(write_html_to, 'wb') as f: f.write(raw) doc = html.fromstring(raw) select = Select(doc) for i, item in enumerate(select('.result-items .item-wrapper.book')): if i == max_results: break for img in select('.item-image img[src]', item): cover_url = img.get('src') if cover_url.startswith('//'): cover_url = 'http:' + cover_url break else: cover_url = None for p in select('p.title', item): title = etree.tostring(p, method='text', encoding=unicode).strip() for a in select('a[href]', p): url = 'http://store.kobobooks.com' + a.get('href') break else: url = None break else: title = None authors = [] for a in select('p.author a.contributor', item): authors.append(etree.tostring(a, method='text', encoding=unicode).strip()) authors = authors_to_string(authors) for p in select('p.price', item): price = etree.tostring(p, method='text', encoding=unicode).strip() break else: price = None if title and authors and url: s = SearchResult() s.cover_url = cover_url s.title = title s.author = authors s.price = price s.detail_item = url s.formats = 'EPUB' s.drm = SearchResult.DRM_UNKNOWN yield s
def mi_to_info(mi): ans = [] if mi.title: ans.extend(('InfoKey: Title', 'InfoValue: '+mi.title)) if mi.authors: from calibre.ebooks.metadata import authors_to_string ans.extend(('InfoKey: Author', 'InfoValue: ' + authors_to_string(mi.authors))) return u'\n'.join(ans)
def __init__(self, oeb_metadata=None): self.title = _('Unknown') self.author = _('Unknown') if oeb_metadata != None: if len(oeb_metadata.title) >= 1: self.title = oeb_metadata.title[0].value if len(oeb_metadata.creator) >= 1: self.author = authors_to_string([x.value for x in oeb_metadata.creator])
def get_format(self, id, format): format = format.upper() fm = self.db.format_metadata(id, format, allow_cache=False) if not fm: raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format)) update_metadata = format in {'MOBI', 'EPUB', 'AZW3'} mi = newmi = self.db.get_metadata( id, index_is_id=True, cover_as_data=True, get_cover=update_metadata) cherrypy.response.headers['Last-Modified'] = \ self.last_modified(max(fm['mtime'], mi.last_modified)) fmt = self.db.format(id, format, index_is_id=True, as_file=True, mode='rb') if fmt is None: raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format)) mt = guess_type('dummy.'+format.lower())[0] if mt is None: mt = 'application/octet-stream' cherrypy.response.headers['Content-Type'] = mt if format.lower() in plugboard_content_server_formats: # Get any plugboards for the content server plugboards = self.db.prefs.get('plugboards', {}) cpb = find_plugboard(plugboard_content_server_value, format.lower(), plugboards) if cpb: # Transform the metadata via the plugboard newmi = mi.deepcopy_metadata() newmi.template_to_attribute(mi, cpb) if update_metadata: # Write the updated file from calibre.ebooks.metadata.meta import set_metadata set_metadata(fmt, newmi, format.lower()) fmt.seek(0) fmt.seek(0, 2) cherrypy.response.headers['Content-Length'] = fmt.tell() fmt.seek(0) ua = cherrypy.request.headers.get('User-Agent', '').strip() have_kobo_browser = self.is_kobo_browser(ua) file_extension = "kepub.epub" if have_kobo_browser and format.lower() == "kepub" else format au = authors_to_string(newmi.authors if newmi.authors else [_('Unknown')]) title = newmi.title if newmi.title else _('Unknown') fname = u'%s - %s_%s.%s'%(title[:30], au[:30], id, file_extension.lower()) fname = ascii_filename(fname).replace('"', '_') cherrypy.response.headers['Content-Disposition'] = \ b'attachment; filename="%s"'%fname cherrypy.response.body = fmt cherrypy.response.timeout = 3600 return fmt
def data_as_text(self, book, col): if col == 0: return unicode(book.gui_rank + 1) if col == 1: t = book.title if book.title else _("Unknown") a = authors_to_string(book.authors) if book.authors else "" return "<b>%s</b><br><i>%s</i>" % (t, a) if col == 2: d = format_date(book.pubdate, "yyyy") if book.pubdate else _("Unknown") p = book.publisher if book.publisher else "" return "<b>%s</b><br><i>%s</i>" % (d, p)
def data_as_text(self, book, col): if col == 0: return unicode(book.gui_rank+1) if col == 1: t = book.title if book.title else _('Unknown') a = authors_to_string(book.authors) if book.authors else '' return '<b>%s</b><br><i>%s</i>' % (t, a) if col == 2: d = format_date(book.pubdate, 'yyyy') if book.pubdate else _('Unknown') p = book.publisher if book.publisher else '' return '<b>%s</b><br><i>%s</i>' % (d, p)
def get_title_authors_text(db, book_id): def authors_to_list(db, book_id): authors = db.authors(book_id, index_is_id=True) if authors: return [a.strip().replace('|',',') for a in authors.split(',')] return [] title = db.title(book_id, index_is_id=True) authors = authors_to_list(db, book_id) from calibre.ebooks.metadata import authors_to_string return '%s / %s'%(title, authors_to_string(authors))
def header(self): header = u'{\\rtf1{\\info{\\title %s}{\\author %s}}\\ansi\\ansicpg1252\\deff0\\deflang1033\n' % ( self.oeb_book.metadata.title[0].value, authors_to_string([x.value for x in self.oeb_book.metadata.creator])) return header + ( '{\\fonttbl{\\f0\\froman\\fprq2\\fcharset128 Times New Roman;}{\\f1\\froman\\fprq2\\fcharset128 Times New Roman;}{\\f2\\fswiss\\fprq2\\fcharset128 Arial;}{\\f3\\fnil\\fprq2\\fcharset128 Arial;}{\\f4\\fnil\\fprq2\\fcharset128 MS Mincho;}{\\f5\\fnil\\fprq2\\fcharset128 Tahoma;}{\\f6\\fnil\\fprq0\\fcharset128 Tahoma;}}\n' # noqa '{\\stylesheet{\\ql \\li0\\ri0\\nowidctlpar\\wrapdefault\\faauto\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\af25\\afs24\\alang1033 \\ltrch\\fcs0 \\fs24\\lang1033\\langfe255\\cgrid\\langnp1033\\langfenp255 \\snext0 Normal;}\n' # noqa '{\\s1\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel0\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\af0\\afs32\\alang1033 \\ltrch\\fcs0 \\b\\fs32\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink21 heading 1;}\n' # noqa '{\\s2\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel1\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\ai\\af0\\afs28\\alang1033 \\ltrch\\fcs0 \\b\\i\\fs28\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink22 heading 2;}\n' # noqa '{\\s3\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel2\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\af0\\afs28\\alang1033 \\ltrch\\fcs0 \\b\\fs28\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink23 heading 3;}\n' # noqa '{\\s4\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel3\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\ai\\af0\\afs23\\alang1033 \\ltrch\\fcs0\\b\\i\\fs23\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink24 heading 4;}\n' # noqa '{\\s5\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel4\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\af0\\afs23\\alang1033 \\ltrch\\fcs0 \\b\\fs23\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink25 heading 5;}\n' # noqa '{\\s6\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel5\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\af0\\afs21\\alang1033 \\ltrch\\fcs0 \\b\\fs21\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink26 heading 6;}}\n' # noqa )
def start(self, title=None, authors=None, identifiers={}): self.log.clear() self.log("Starting download") parts, simple_desc = [], "" if title: parts.append("title:" + title) simple_desc += _("Title: %s ") % title if authors: parts.append("authors:" + authors_to_string(authors)) simple_desc += _("Authors: %s ") % authors_to_string(authors) if identifiers: x = ", ".join("%s:%s" % (k, v) for k, v in identifiers.iteritems()) parts.append(x) if "isbn" in identifiers: simple_desc += " ISBN: %s" % identifiers["isbn"] self.query.setText(simple_desc) self.log(unicode(self.query.text())) self.worker = IdentifyWorker(self.log, self.abort, title, authors, identifiers, self.caches) self.worker.start() QTimer.singleShot(50, self.update)
def book_filename(rd, book_id, mi, fmt, as_encoded_unicode=False): au = authors_to_string(mi.authors or [_('Unknown')]) title = mi.title or _('Unknown') ext = (fmt or '').lower() if ext == 'kepub' and 'Kobo Touch' in rd.inheaders.get('User-Agent', ''): ext = 'kepub.epub' fname = '%s - %s_%s.%s' % (title[:30], au[:30], book_id, ext) if as_encoded_unicode: # See https://tools.ietf.org/html/rfc6266 fname = sanitize_file_name(fname).encode('utf-8') fname = unicode_type(quote(fname)) else: fname = ascii_filename(fname).replace('"', '_') return fname
def _info_section(self, metadata): text = 'TYPE=2\n' if metadata: if len(metadata.title) >= 1: text += 'TITLE=%s\n' % metadata.title[0].value if len(metadata.creator) >= 1: from calibre.ebooks.metadata import authors_to_string text += 'AUTHOR=%s\n' % authors_to_string([x.value for x in metadata.creator]) text += 'GENERATOR=%s - %s\n' % (__appname__, __version__) text += 'PARSE=1\n' text += 'OUTPUT=1\n' text += 'BODY=index.html\n' return text
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)
def ACQUISITION_ENTRY(book_id, updated, request_context): field_metadata = request_context.db.field_metadata mi = request_context.db.get_metadata(book_id) extra = [] if mi.rating > 0: rating = u''.join(repeat(u'\u2605', int(mi.rating/2.))) extra.append(_('RATING: %s<br />')%rating) if mi.tags: extra.append(_('TAGS: %s<br />')%xml(format_tag_string(mi.tags, None, no_tag_count=True))) if mi.series: extra.append(_('SERIES: %(series)s [%(sidx)s]<br />')% dict(series=xml(mi.series), sidx=fmt_sidx(float(mi.series_index)))) for key in field_metadata.ignorable_field_keys(): name, val = mi.format_field(key) if val: fm = field_metadata[key] datatype = fm['datatype'] if datatype == 'text' and fm['is_multiple']: extra.append('%s: %s<br />'% (xml(name), xml(format_tag_string(val, fm['is_multiple']['ui_to_list'], no_tag_count=True, joinval=fm['is_multiple']['list_to_ui'])))) elif datatype == 'comments' or (fm['datatype'] == 'composite' and fm['display'].get('contains_html', False)): extra.append('%s: %s<br />'%(xml(name), comments_to_html(unicode(val)))) else: extra.append('%s: %s<br />'%(xml(name), xml(unicode(val)))) if mi.comments: comments = comments_to_html(mi.comments) extra.append(comments) if extra: extra = html_to_lxml('\n'.join(extra)) ans = E.entry(TITLE(mi.title), E.author(E.name(authors_to_string(mi.authors))), ID('urn:uuid:' + mi.uuid), UPDATED(updated)) if len(extra): ans.append(E.content(extra, type='xhtml')) get = partial(request_context.ctx.url_for, '/get', book_id=book_id, library_id=request_context.library_id) if mi.formats: for fmt in mi.formats: fmt = fmt.lower() mt = guess_type('a.'+fmt)[0] if mt: ans.append(E.link(type=mt, href=get(what=fmt), rel="http://opds-spec.org/acquisition")) ans.append(E.link(type='image/jpeg', href=get(what='cover'), rel="http://opds-spec.org/cover")) ans.append(E.link(type='image/jpeg', href=get(what='thumb'), rel="http://opds-spec.org/thumbnail")) return ans
def do_test(self): from calibre.ebooks.metadata import authors_to_string from calibre.ebooks.metadata.meta import metadata_from_filename fname = unicode_type(self.filename.text()) ext = os.path.splitext(fname)[1][1:].lower() if ext not in BOOK_EXTENSIONS: return warning_dialog(self, _('Test file name invalid'), _('The file name <b>%s</b> does not appear to end with a' ' file extension. It must end with a file ' ' extension like .epub or .mobi')%fname, show=True) try: pat = self.pattern() except Exception as err: error_dialog(self, _('Invalid regular expression'), _('Invalid regular expression: %s')%err).exec_() return mi = metadata_from_filename(fname, pat) if mi.title: self.title.setText(mi.title) else: self.title.setText(_('No match')) if mi.authors: self.authors.setText(authors_to_string(mi.authors)) else: self.authors.setText(_('No match')) if mi.series: self.series.setText(mi.series) else: self.series.setText(_('No match')) if mi.series_index is not None: self.series_index.setText(str(mi.series_index)) else: self.series_index.setText(_('No match')) if mi.publisher: self.publisher.setText(mi.publisher) else: self.publisher.setText(_('No match')) if mi.pubdate: self.pubdate.setText(strftime('%Y-%m-%d', mi.pubdate)) else: self.pubdate.setText(_('No match')) self.isbn.setText(_('No match') if mi.isbn is None else str(mi.isbn)) self.comments.setText(mi.comments if mi.comments else _('No match'))
def start(self, title=None, authors=None, identifiers={}): self.log.clear() self.log('Starting download') parts, simple_desc = [], '' if title: parts.append('title:' + title) simple_desc += _('Title: %s ') % title if authors: parts.append('authors:' + authors_to_string(authors)) simple_desc += _('Authors: %s ') % authors_to_string(authors) if identifiers: x = ', '.join('%s:%s' % (k, v) for k, v in iteritems(identifiers)) parts.append(x) if 'isbn' in identifiers: simple_desc += 'ISBN: %s' % identifiers['isbn'] self.query.setText(simple_desc) self.log(unicode_type(self.query.text())) self.worker = IdentifyWorker(self.log, self.abort, title, authors, identifiers, self.caches) self.worker.start() QTimer.singleShot(50, self.update)
def header(self): header = '{\\rtf1{\\info{\\title %s}{\\author %s}}\\ansi\\ansicpg1252\\deff0\\deflang1033\n' % ( self.oeb_book.metadata.title[0].value, authors_to_string( [x.value for x in self.oeb_book.metadata.creator])) return header + ( '{\\fonttbl{\\f0\\froman\\fprq2\\fcharset128 Times New Roman;}{\\f1\\froman\\fprq2\\fcharset128 Times New Roman;}{\\f2\\fswiss\\fprq2\\fcharset128 Arial;}{\\f3\\fnil\\fprq2\\fcharset128 Arial;}{\\f4\\fnil\\fprq2\\fcharset128 MS Mincho;}{\\f5\\fnil\\fprq2\\fcharset128 Tahoma;}{\\f6\\fnil\\fprq0\\fcharset128 Tahoma;}}\n' # noqa '{\\stylesheet{\\ql \\li0\\ri0\\nowidctlpar\\wrapdefault\\faauto\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\af25\\afs24\\alang1033 \\ltrch\\fcs0 \\fs24\\lang1033\\langfe255\\cgrid\\langnp1033\\langfenp255 \\snext0 Normal;}\n' # noqa '{\\s1\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel0\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\af0\\afs32\\alang1033 \\ltrch\\fcs0 \\b\\fs32\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink21 heading 1;}\n' # noqa '{\\s2\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel1\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\ai\\af0\\afs28\\alang1033 \\ltrch\\fcs0 \\b\\i\\fs28\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink22 heading 2;}\n' # noqa '{\\s3\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel2\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\af0\\afs28\\alang1033 \\ltrch\\fcs0 \\b\\fs28\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink23 heading 3;}\n' # noqa '{\\s4\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel3\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\ai\\af0\\afs23\\alang1033 \\ltrch\\fcs0\\b\\i\\fs23\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink24 heading 4;}\n' # noqa '{\\s5\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel4\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\af0\\afs23\\alang1033 \\ltrch\\fcs0 \\b\\fs23\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink25 heading 5;}\n' # noqa '{\\s6\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel5\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\af0\\afs21\\alang1033 \\ltrch\\fcs0 \\b\\fs21\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink26 heading 6;}}\n' # noqa )
def book_filename(rd, book_id, mi, fmt, as_encoded_unicode=False): au = authors_to_string(mi.authors or [_('Unknown')]) title = mi.title or _('Unknown') ext = (fmt or '').lower() fname = '%s - %s_%s.%s' % (title[:30], au[:30], book_id, ext) if as_encoded_unicode: # See https://tools.ietf.org/html/rfc6266 fname = sanitize_file_name(fname).encode('utf-8') fname = unicode_type(quote(fname)) else: fname = ascii_filename(fname).replace('"', '_') if ext == 'kepub' and 'Kobo Touch' in rd.inheaders.get('User-Agent', ''): fname = fname.replace('!', '_') fname += '.epub' return fname
def _info_section(self, metadata): text = 'TYPE=2\n' if metadata: if len(metadata.title) >= 1: text += 'TITLE=%s\n' % metadata.title[0].value if len(metadata.creator) >= 1: from calibre.ebooks.metadata import authors_to_string text += 'AUTHOR=%s\n' % authors_to_string( [x.value for x in metadata.creator]) text += 'GENERATOR=%s - %s\n' % (__appname__, __version__) text += 'PARSE=1\n' text += 'OUTPUT=1\n' text += 'BODY=index.html\n' return text
def set_metadata(stream, mi): pheader = PdbHeaderReader(stream) # Only Dropbook produced 132 byte record0 files are supported if pheader.section_data(0) != 132: return sections = [ pheader.section_data(x) for x in range(0, pheader.section_count()) ] hr = HeaderRecord(sections[0]) if hr.compression not in (2, 10): return # Create a metadata record for the file if one does not already exist if not hr.has_metadata: sections += [b'', b'MeTaInFo\x00'] last_data = len(sections) - 1 for i in range(0, 132, 2): val, = struct.unpack('>H', sections[0][i:i + 2]) if val >= hr.last_data_offset: sections[0][i:i + 2] = struct.pack('>H', last_data) sections[0][24:26] = struct.pack('>H', 1) # Set has metadata sections[0][44:46] = struct.pack('>H', last_data - 1) # Set location of metadata sections[0][52:54] = struct.pack( '>H', last_data) # Ensure last data offset is updated # Merge the metadata into the file file_mi = get_metadata(stream, False) file_mi.smart_update(mi) sections[hr.metadata_offset] = ('{}\x00{}\x00{}\x00{}\x00{}\x00'.format( file_mi.title, authors_to_string(file_mi.authors), '', file_mi.publisher, file_mi.isbn)).encode('cp1252', 'replace') # Rebuild the PDB wrapper because the offsets have changed due to the # new metadata. pheader_builder = PdbHeaderBuilder(pheader.ident, pheader.title) stream.seek(0) stream.truncate(0) pheader_builder.build_header([len(x) for x in sections], stream) # Write the data back to the file for item in sections: stream.write(item)
def read_metadata(root, ver=None, return_extra_data=False): ans = Metadata(_('Unknown'), [_('Unknown')]) prefixes, refines = read_prefixes(root), read_refines(root) identifiers = read_identifiers(root, prefixes, refines) ids = {} for key, vals in iteritems(identifiers): if key == 'calibre': ans.application_id = vals[0] elif key == 'uuid': ans.uuid = vals[0] else: ids[key] = vals[0] ans.set_identifiers(ids) ans.title = read_title(root, prefixes, refines) or ans.title ans.title_sort = read_title_sort(root, prefixes, refines) or ans.title_sort ans.languages = read_languages(root, prefixes, refines) or ans.languages auts, aus = [], [] for a in read_authors(root, prefixes, refines): auts.append(a.name), aus.append(a.sort) ans.authors = auts or ans.authors ans.author_sort = authors_to_string(aus) or ans.author_sort bkp = read_book_producers(root, prefixes, refines) if bkp: if bkp[0]: ans.book_producer = bkp[0] pd = read_pubdate(root, prefixes, refines) if not is_date_undefined(pd): ans.pubdate = pd ts = read_timestamp(root, prefixes, refines) if not is_date_undefined(ts): ans.timestamp = ts lm = read_last_modified(root, prefixes, refines) if not is_date_undefined(lm): ans.last_modified = lm ans.comments = read_comments(root, prefixes, refines) or ans.comments ans.publisher = read_publisher(root, prefixes, refines) or ans.publisher ans.tags = read_tags(root, prefixes, refines) or ans.tags ans.rating = read_rating(root, prefixes, refines) or ans.rating s, si = read_series(root, prefixes, refines) if s: ans.series, ans.series_index = s, si ans.author_link_map = read_author_link_map(root, prefixes, refines) or ans.author_link_map ans.user_categories = read_user_categories(root, prefixes, refines) or ans.user_categories for name, fm in iteritems((read_user_metadata(root, prefixes, refines) or {})): ans.set_user_metadata(name, fm) if return_extra_data: ans = ans, ver, read_raster_cover(root, prefixes, refines), first_spine_item(root, prefixes, refines) return ans
def reverse(pdf_path, out_path, metadata=None): if metadata == None: title = _('Unknown') author = _('Unknown') else: title = metadata.title author = authors_to_string(metadata.authors) out_pdf = PdfFileWriter(title=title, author=author) pdf = PdfFileReader(open(os.path.abspath(pdf_path), 'rb')) for page in reversed(pdf.pages): out_pdf.addPage(page) with open(out_path, 'wb') as out_file: out_pdf.write(out_file)
def rotate(pdf_path, out_path, degrees, metadata=None): if metadata == None: title = _('Unknown') author = _('Unknown') else: title = metadata.title author = authors_to_string(metadata.authors) out_pdf = PdfFileWriter(title=title, author=author) pdf = PdfFileReader(open(os.path.abspath(pdf_path), 'rb')) for page in pdf.pages: out_pdf.addPage(page.rotateClockwise(int(degrees))) with open(out_path, 'wb') as out_file: out_pdf.write(out_file)
def process_duplicates(self, db, duplicates): ta = _('%(title)s by %(author)s [%(formats)s]') bf = QFont(self.dup_list.font()) bf.setBold(True) itf = QFont(self.dup_list.font()) itf.setItalic(True) for mi, cover, formats in duplicates: # formats is a list of file paths # Grab just the extension and display to the user # Based only off the file name, no file type tests are done. incoming_formats = ', '.join( os.path.splitext(path)[-1].replace('.', '').upper() for path in formats) item = QTreeWidgetItem([ ta % dict(title=mi.title, author=mi.format_field('authors')[1], formats=incoming_formats) ], 0) item.setCheckState(0, Qt.Checked) item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable) item.setData(0, Qt.FontRole, bf) item.setData(0, Qt.UserRole, (mi, cover, formats)) matching_books = db.books_with_same_title(mi) def add_child(text): c = QTreeWidgetItem([text], 1) c.setFlags(Qt.ItemIsEnabled) item.addChild(c) return c add_child(_('Already in calibre:')).setData(0, Qt.FontRole, itf) for book_id in matching_books: aut = [ a.replace('|', ',') for a in ( db.authors(book_id, index_is_id=True) or '').split(',') ] add_child( ta % dict(title=db.title(book_id, index_is_id=True), author=authors_to_string(aut), formats=db.formats( book_id, index_is_id=True, verify_formats=False))) add_child('') yield item
def search(query, max_results=10, timeout=60): url = 'http://www.baen.com/catalogsearch/result/?' + urlencode( {'q':query.lower(), 'dir':'desc', 'order':'relevance'}) br = browser() counter = max_results with closing(br.open_novisit(url, timeout=timeout)) as f: raw = f.read() root = html.fromstring(raw) for data in root.xpath('//div[@id="productMatches"]//table[@id="authorTable"]//tr[contains(@class, "IDCell")]'): if counter <= 0: break try: book_url = data.xpath('./td[1]/a/@href[1]')[0] except IndexError: continue try: title = data.xpath('./td[2]/a[1]/text()')[0].strip() except IndexError: continue try: cover_url = data.xpath('./td[1]//img[1]/@src')[0] except IndexError: cover_url = '' tails = [(b.tail or '').strip() for b in data.xpath('./td[2]/br')] authors = [x[2:].strip() for x in tails if x.startswith('by ')] author = authors_to_string(authors) price = ''.join(data.xpath('.//span[@class="variantprice"]/text()')) a, b, price = price.partition('$') price = b + price counter -= 1 s = SearchResult() s.cover_url = cover_url s.title = title.strip() s.author = author.strip() s.price = price s.detail_item = book_url.strip() s.drm = SearchResult.DRM_UNLOCKED s.formats = 'RB, MOBI, EPUB, LIT, LRF, RTF, HTML' yield s
def apply_downloaded_metadata(self, payload): good_ids, tdir, log_file, lm_map = payload if not good_ids: return modified = set() db = self.gui.current_db for i in good_ids: lm = db.metadata_last_modified(i, index_is_id=True) if lm is not None and lm_map[i] is not None and lm > lm_map[i]: title = db.title(i, index_is_id=True) authors = db.authors(i, index_is_id=True) if authors: authors = [x.replace('|', ',') for x in authors.split(',')] title += ' - ' + authors_to_string(authors) modified.add(title) if modified: from calibre.utils.icu import lower modified = sorted(modified, key=lower) if not question_dialog( self.gui, _('Some books changed'), '<p>' + _('The metadata for some books in your library has' ' changed since you started the download. If you' ' proceed, some of those changes may be overwritten. ' 'Click "Show details" to see the list of changed books. ' 'Do you want to proceed?'), det_msg='\n'.join(modified)): return id_map = {} for bid in good_ids: opf = os.path.join(tdir, '%d.mi' % bid) if not os.path.exists(opf): opf = None cov = os.path.join(tdir, '%d.cover' % bid) if not os.path.exists(cov): cov = None id_map[bid] = (opf, cov) self.apply_metadata_changes( id_map, callback=lambda x: self.cleanup_bulk_download(tdir))
def setup_ui(self): self.l = l = QVBoxLayout(self) self.splitter = s = QSplitter(self) s.setChildrenCollapsible(False) l.addWidget(s), l.addWidget(self.bb) self.bb.setStandardButtons(self.bb.Yes | self.bb.No) self.left = w = QWidget(self) s.addWidget(w) w.l = l = QVBoxLayout(w) l.setContentsMargins(0, 0, 0, 0) self.la = la = QLabel(self.msg) la.setWordWrap(True) l.addWidget(la) self.confirm = c = QCheckBox(_('Show this confirmation again'), self) c.setChecked(True) c.stateChanged.connect(self.toggle) l.addWidget(c) self.right = r = QTextBrowser(self) series = '' mi, fm = self.mi, field_metadata if mi.series: series = _('{num} of {series}').format( num=mi.format_series_index(), series='<i>%s</i>' % mi.series) r.setHtml(''' <h3 style="text-align:center">{mb}</h3> <p><b>{title}</b> - <i>{authors}</i><br></p> <table> <tr><td>{fm[timestamp][name]}:</td><td>{date}</td></tr> <tr><td>{fm[pubdate][name]}:</td><td>{published}</td></tr> <tr><td>{fm[formats][name]}:</td><td>{formats}</td></tr> <tr><td>{fm[series][name]}:</td><td>{series}</td></tr> </table> '''.format(mb=_('Target book'), title=mi.title, authors=authors_to_string(mi.authors), date=format_date(mi.timestamp, tweaks['gui_timestamp_display_format']), fm=fm, published=(format_date(mi.pubdate, tweaks['gui_pubdate_display_format']) if mi.pubdate else ''), formats=', '.join(mi.formats or ()), series=series)) s.addWidget(r)
def book_fmt(ctx, rd, library_id, db, book_id, fmt): mdata = db.format_metadata(book_id, fmt) if not mdata: raise NoSuchFormat() mtime = mdata['mtime'] update_metadata = fmt in update_metadata_in_fmts extra_etag_data = '' if update_metadata: mi = db.get_metadata(book_id) mtime = max(mtime, mi.last_modified) # Get any plugboards for the content server plugboards = db.pref('plugboards') if plugboards: cpb = find_plugboard(plugboard_content_server_value, fmt, plugboards) if cpb: # Transform the metadata via the plugboard newmi = mi.deepcopy_metadata() newmi.template_to_attribute(mi, cpb) mi = newmi extra_etag_data = repr(cpb) else: mi = db.get_proxy_metadata(book_id) def copy_func(dest): db.copy_format_to(book_id, fmt, dest) if update_metadata: set_metadata(dest, mi, fmt) dest.seek(0) au = authors_to_string(mi.authors or [_('Unknown')]) title = mi.title or _('Unknown') fname = '%s - %s_%s.%s' % (title[:30], au[:30], book_id, fmt) fname = ascii_filename(fname).replace('"', '_') rd.outheaders['Content-Disposition'] = 'attachment; filename="%s"' % fname return create_file_copy(ctx, rd, 'fmt', library_id, book_id, fmt, mtime, copy_func, extra_etag_data=extra_etag_data)
def update_doc_props(root, mi, namespace): def setm(name, text=None, ns='dc'): ans = root.makeelement('{%s}%s' % (namespace.namespaces[ns], name)) for child in tuple(root): if child.tag == ans.tag: root.remove(child) ans.text = text root.append(ans) return ans setm('title', mi.title) setm('creator', authors_to_string(mi.authors)) if mi.tags: setm('keywords', ', '.join(mi.tags), ns='cp') if mi.comments: setm('description', mi.comments) if mi.languages: l = canonicalize_lang(mi.languages[0]) setm('language', lang_as_iso639_1(l) or l)
def __init__(self, oeb_metadata=None): from calibre import force_unicode from calibre.ebooks.metadata import authors_to_string self.title = _(u'Unknown') self.author = _(u'Unknown') self.tags = u'' if oeb_metadata is not None: if len(oeb_metadata.title) >= 1: self.title = oeb_metadata.title[0].value if len(oeb_metadata.creator) >= 1: self.author = authors_to_string( [x.value for x in oeb_metadata.creator]) if oeb_metadata.subject: self.tags = u', '.join(map(unicode, oeb_metadata.subject)) self.title = force_unicode(self.title) self.author = force_unicode(self.author)
def __init__(self, mi=None): from calibre import force_unicode from calibre.ebooks.metadata import authors_to_string self.title = _(u'Unknown') self.author = _(u'Unknown') self.tags = u'' self.mi = mi if mi is not None: if mi.title: self.title = mi.title if mi.authors: self.author = authors_to_string(mi.authors) if mi.tags: self.tags = u', '.join(mi.tags) self.title = force_unicode(self.title) self.author = force_unicode(self.author)
def do_send_mail(self, book, mail_to, fmt, fpath): body = open(fpath).read() # read meta info author = authors_to_string( book['authors'] if book['authors'] else [_('Unknown')]) title = book['title'] if book['title'] else _("No Title") fname = u'%s - %s.%s' % (title, author, fmt) fname = ascii_filename(fname).replace('"', '_') # content type mt = guess_type('dummy.' + fmt)[0] if mt is None: mt = 'application/octet-stream' # send mail mail_from = '*****@*****.**' mail_subject = _('Book from Calibre: %(title)s') % vars() mail_body = _('We Send this book to your kindle.') status = msg = "" try: msg = create_mail(mail_from, mail_to, mail_subject, text=mail_body, attachment_data=body, attachment_type=mt, attachment_name=fname) sendmail(msg, from_=mail_from, to=[mail_to], timeout=30, username=tweaks['smtp_username'], password=tweaks['smtp_password']) status = "success" msg = _('Send to kindle success!! email: %(mail_to)s') % vars() except: import traceback cherrypy.log.error('Failed to generate cover:') cherrypy.log.error(traceback.format_exc()) status = "danger" msg = traceback.format_exc() messages.append({'status': status, 'msg': msg}) return
def set_metadata_(tdir, title, authors, bkp, tags, xmp_packet): podofo = get_podofo() os.chdir(tdir) p = podofo.PDFDoc() p.open('input.pdf') title = prep(title) touched = False if title and title != p.title: p.title = title touched = True author = prep(authors_to_string(authors)) if author and author != p.author: p.author = author touched = True bkp = prep(bkp) if bkp and bkp != p.creator: p.creator = bkp touched = True try: tags = prep(', '.join([x.strip() for x in tags if x.strip()])) if tags != p.keywords: p.keywords = tags touched = True except: pass try: current_xmp_packet = p.get_xmp_metadata() if current_xmp_packet: from calibre.ebooks.metadata.xmp import merge_xmp_packet xmp_packet = merge_xmp_packet(current_xmp_packet, xmp_packet) p.set_xmp_metadata(xmp_packet) touched = True except: pass if touched: p.save('output.pdf') return touched
def format_report(self): report = [] a = report.append def indent(text): text = force_unicode(text) return '\xa0\xa0\xa0\xa0' + '\n\xa0\xa0\xa0\xa0'.join( text.splitlines()) for book_id, errors in self.errors.iteritems(): types = {t for t, data in errors} title, authors = self.book_id_data( book_id).title, authors_to_string( self.book_id_data(book_id).authors[:1]) if report: a('\n' + ('_' * 70) + '\n') if 'critical' in types: a( _('Failed to save: {0} by {1} to disk, with error:'). format(title, authors)) for t, tb in errors: if t == 'critical': a(indent(tb)) else: errs = defaultdict(list) for t, data in errors: errs[t].append(data) for fmt, tb in errs['fmt']: a( _('Failed to save the {2} format of: {0} by {1} to disk, with error:' ).format(title, authors, fmt.upper())) a(indent(tb)), a('') for fmt, tb in errs['metadata']: if fmt: a( _('Failed to update the metadata in the {2} format of: {0} by {1}, with error:' ).format(title, authors, fmt.upper())) else: a( _('Failed to update the metadata in all formats of: {0} by {1}, with error:' ).format(title, authors)) a(indent(tb)), a('') return '\n'.join(report)
def get_format(self, id, format): format = format.upper() fm = self.db.format_metadata(id, format, allow_cache=False) if not fm: raise web.HTTPError(404, 'book: %d does not have format: %s'%(id, format)) mi = newmi = self.db.get_metadata(id, index_is_id=True) self.set_header( 'Last-Modified', self.last_modified(max(fm['mtime'], mi.last_modified)) ) fmt = self.db.format(id, format, index_is_id=True, as_file=True, mode='rb') if fmt is None: raise web.HTTPError(404, 'book: %d does not have format: %s'%(id, format)) mt = guess_type('dummy.'+format.lower())[0] if mt is None: mt = 'application/octet-stream' self.set_header( 'Content-Type', mt ) if format == 'EPUB': # Get the original metadata # Get any EPUB plugboards for the content server plugboards = self.db.prefs.get('plugboards', {}) cpb = find_plugboard(plugboard_content_server_value, 'epub', plugboards) if cpb: # Transform the metadata via the plugboard newmi = mi.deepcopy_metadata() newmi.template_to_attribute(mi, cpb) if format in ('MOBI', 'EPUB'): # Write the updated file set_metadata(fmt, newmi, format.lower()) fmt.seek(0) fmt.seek(0, 2) self.set_header( 'Content-Lenght', fmt.tell() ) fmt.seek(0) au = authors_to_string(newmi.authors if newmi.authors else [_('Unknown')]) title = newmi.title if newmi.title else _('Unknown') fname = u'%s - %s_%s.%s'%(title[:30], au[:30], id, format.lower()) fname = ascii_filename(fname).replace('"', '_') self.set_header( 'Content-Disposition', b'attachment; filename="%s"'%fname ) return fmt
def start(self, title=None, authors=None, identifiers={}): self.log.clear() self.log('Starting download') parts = [] if title: parts.append('title:'+title) if authors: parts.append('authors:'+authors_to_string(authors)) if identifiers: x = ', '.join('%s:%s'%(k, v) for k, v in identifiers.iteritems()) parts.append(x) self.query.setText(_('Query: ')+'; '.join(parts)) self.log(unicode(self.query.text())) self.worker = IdentifyWorker(self.log, self.abort, title, authors, identifiers, self.caches) self.worker.start() QTimer.singleShot(50, self.update)
def crop_pdf(pdf_path, opts, metadata=None): if metadata == None: title = _('Unknown') author = _('Unknown') else: title = metadata.title author = authors_to_string(metadata.authors) input_pdf = PdfFileReader(open(pdf_path, 'rb')) bounding_lines = [] if opts.bounding != None: try: bounding = open(opts.bounding , 'r') bounding_regex = re.compile('%%BoundingBox: (?P<bottom_x>\d+) (?P<bottom_y>\d+) (?P<top_x>\d+) (?P<top_y>\d+)') except: raise Exception('Error reading %s' % opts.bounding) lines = bounding.readlines() for line in lines: if line.startswith('%%BoundingBox:'): bounding_lines.append(line) if len(bounding_lines) != input_pdf.numPages: raise Exception('Error bounding file %s page count does not correspond to specified pdf' % opts.bounding) output_pdf = PdfFileWriter(title=title,author=author) blines = iter(bounding_lines) for page in input_pdf.pages: if bounding_lines != []: mo = bounding_regex.search(blines.next()) if mo == None: raise Exception('Error in bounding file %s' % opts.bounding) page.mediaBox.upperRight = (float(mo.group('top_x')), Decimal(mo.group('top_y'))) page.mediaBox.lowerLeft = (float(mo.group('bottom_x')), Decimal(mo.group('bottom_y'))) else: page.mediaBox.upperRight = (page.bleedBox.getUpperRight_x() - Decimal(opts.top_right_x), page.bleedBox.getUpperRight_y() - Decimal(opts.top_right_y)) page.mediaBox.lowerLeft = (page.bleedBox.getLowerLeft_x() + Decimal(opts.bottom_left_x), page.bleedBox.getLowerLeft_y() + Decimal(opts.bottom_left_y)) output_pdf.addPage(page) with open(opts.output, 'wb') as output_file: output_pdf.write(output_file)
def do_send_mail(self, book, mail_to, fmt, fpath): try: import local_settings settings.update(local_settings.settings) except Exception as e: logging.error("read local_settings fail") pass # read meta info author = authors_to_string( book['authors'] if book['authors'] else [_(u'佚名')]) title = book['title'] if book['title'] else _(u"无名书籍") fname = u'%s - %s.%s' % (title, author, fmt) fdata = open(fpath).read() mail_from = settings['smtp_username'] mail_subject = _('奇异书屋:推送给您一本书《%(title)s》') % vars() mail_body = _( u'为您奉上一本《%(title)s》, 欢迎常来访问奇异书屋!http://www.talebook.org' % vars()) status = msg = "" try: logging.info('send %(title)s to %(mail_to)s' % vars()) mail = self.create_mail(mail_from, mail_to, mail_subject, mail_body, fdata, fname) sendmail(mail, from_=mail_from, to=[mail_to], timeout=30, relay=settings['smtp_server'], username=settings['smtp_username'], password=settings['smtp_password']) status = "success" msg = _('[%(title)s] 已成功发送至Kindle邮箱 [%(mail_to)s] !!') % vars() logging.info(msg) except: import traceback logging.error('Failed to send to kindle:') logging.error(traceback.format_exc()) status = "danger" msg = traceback.format_exc() self.add_msg(status, msg) return
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_type(self.title))] ans += [(_('Author(s)'), (authors_to_string(self.authors) if self.authors else _('Unknown')))] ans += [(_('Publisher'), unicode_type(self.publisher))] ans += [(_('Producer'), unicode_type(self.book_producer))] ans += [(_('Comments'), unicode_type(self.comments))] ans += [('ISBN', unicode_type(self.isbn))] ans += [(_('Tags'), ', '.join([unicode_type(t) for t in self.tags]))] if self.series: ans += [(ngettext('Series', 'Series', 1), unicode_type(self.series) + ' #%s' % self.format_series_index())] ans += [(_('Languages'), ', '.join(self.languages))] if self.timestamp is not None: ans += [ (_('Timestamp'), unicode_type(isoformat(self.timestamp, as_utc=False, sep=' '))) ] if self.pubdate is not None: ans += [ (_('Published'), unicode_type(isoformat(self.pubdate, as_utc=False, sep=' '))) ] if self.rights is not None: ans += [(_('Rights'), unicode_type(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] = '<tr><td><b>%s</b></td><td>%s</td></tr>' % x return '<table>%s</table>' % '\n'.join(ans)
def get_title_author_series(mi, options=None): if not options: options = cfg.plugin_prefs[cfg.STORE_CURRENT] title = normalize(mi.title) authors = mi.authors if options.get(cfg.KEY_SWAP_AUTHOR, False): swapped_authors = [] for author in authors: swapped_authors.append(swap_author_names(author)) authors = swapped_authors author_string = normalize(authors_to_string(authors)) series = None if mi.series: series_text = options.get(cfg.KEY_SERIES_TEXT, '') if not series_text: series_text = cfg.DEFAULT_SERIES_TEXT from calibre.ebooks.metadata.book.formatter import SafeFormat series = SafeFormat().safe_format( series_text, mi, 'GC template error', mi) series_string = normalize(series) return (title, author_string, series_string)
def finalize_apply(self): db = self.gui.current_db db.commit() if self.apply_pd is not None: self.apply_pd.hide() if self.apply_failures: msg = [] for i, tb in self.apply_failures: title = db.title(i, index_is_id=True) authors = db.authors(i, index_is_id=True) if authors: authors = [x.replace('|', ',') for x in authors.split(',')] title += ' - ' + authors_to_string(authors) msg.append(title + '\n\n' + tb + '\n' + ('*' * 80)) error_dialog(self.gui, _('Some failures'), _('Failed to apply updated metadata for some books' ' in your library. Click "Show Details" to see ' 'details.'), det_msg='\n\n'.join(msg), show=True) if self.applied_ids: cr = self.gui.library_view.currentIndex().row() self.gui.library_view.model().refresh_ids(list(self.applied_ids), cr) if self.gui.cover_flow: self.gui.cover_flow.dataChanged() self.gui.tags_view.recount() self.apply_id_map = [] self.apply_pd = None try: if callable(self.apply_callback): self.apply_callback(list(self.applied_ids)) finally: self.apply_callback = None
def set_metadata_implementation(pdf_doc, title, authors, bkp, tags, xmp_packet): title = prep(title) touched = False if title and title != pdf_doc.title: pdf_doc.title = title touched = True author = prep(authors_to_string(authors)) if author and author != pdf_doc.author: pdf_doc.author = author touched = True bkp = prep(bkp) if bkp and bkp != pdf_doc.creator: pdf_doc.creator = bkp touched = True if bkp and bkp != pdf_doc.producer: pdf_doc.producer = bkp touched = True try: tags = prep(', '.join([x.strip() for x in tags if x.strip()])) if tags != pdf_doc.keywords: pdf_doc.keywords = tags touched = True except Exception: pass try: current_xmp_packet = pdf_doc.get_xmp_metadata() if current_xmp_packet: from calibre.ebooks.metadata.xmp import merge_xmp_packet xmp_packet = merge_xmp_packet(current_xmp_packet, xmp_packet) pdf_doc.set_xmp_metadata(xmp_packet) touched = True except Exception: pass return touched
def _check_proceed_with_extracted_isbns(self, payload): extracted_ids, _same_isbn_ids, _failed_ids = payload modified = set() db = self.gui.current_db for i, title, last_modified, isbn in extracted_ids: lm = db.metadata_last_modified(i, index_is_id=True) if lm > last_modified: title = db.title(i, index_is_id=True) authors = db.authors(i, index_is_id=True) if authors: authors = [x.replace('|', ',') for x in authors.split(',')] title += ' - ' + authors_to_string(authors) modified.add(title) if modified: from calibre.utils.icu import lower modified = sorted(modified, key=lower) if not question_dialog(self.gui, _('Some books changed'), '<p>'+ _('The metadata for some books in your library has' ' changed since you started the download. If you' ' proceed, some of those changes may be overwritten. ' 'Click "Show details" to see the list of changed books. ' 'Do you want to proceed?'), det_msg='\n'.join(modified)): return # At this point we want to re-use code in edit_metadata to go ahead and # apply the changes. So we will replace the Metadata objects with some # empty ones with only the isbn field set so only that field gets updated id_map = {} for i, title, last_modified, isbn in extracted_ids: mi = Metadata(_('Unknown')) mi.isbn = isbn id_map[i] = mi edit_metadata_action = self.gui.iactions['Edit Metadata'] edit_metadata_action.apply_metadata_changes(id_map, callback=self._mark_and_display_results)
def __init__(self, mi, parent=None): QTextBrowser.__init__(self, parent) series = '' fm = field_metadata if mi.series: series = _('{num} of {series}').format(num=mi.format_series_index(), series='<i>%s</i>' % mi.series) self.setHtml(''' <h3 style="text-align:center">{mb}</h3> <p><b>{title}</b> - <i>{authors}</i><br></p> <table> <tr><td>{fm[timestamp][name]}:</td><td>{date}</td></tr> <tr><td>{fm[pubdate][name]}:</td><td>{published}</td></tr> <tr><td>{fm[formats][name]}:</td><td>{formats}</td></tr> <tr><td>{fm[series][name]}:</td><td>{series}</td></tr> </table> '''.format( mb=_('Target book'), title=mi.title, authors=authors_to_string(mi.authors), date=format_date(mi.timestamp, tweaks['gui_timestamp_display_format']), fm=fm, published=(format_date(mi.pubdate, tweaks['gui_pubdate_display_format']) if mi.pubdate else ''), formats=', '.join(mi.formats or ()), series=series ))
def finalize_apply(self): db = self.gui.current_db db.commit() if self.apply_pd is not None: self.apply_pd.hide() if self.apply_failures: msg = [] for i, tb in self.apply_failures: title = db.title(i, index_is_id=True) authors = db.authors(i, index_is_id=True) if authors: authors = [x.replace('|', ',') for x in authors.split(',')] title += ' - ' + authors_to_string(authors) msg.append(title + '\n\n' + tb + '\n' + ('*' * 80)) error_dialog(self.gui, _('Some failures'), _('Failed to apply updated metadata for some books' ' in your library. Click "Show details" to see ' 'details.'), det_msg='\n\n'.join(msg), show=True) changed_books = len(self.applied_ids or ()) self.refresh_gui(self.applied_ids) self.apply_id_map = [] self.apply_pd = None try: if callable(self.apply_callback): self.apply_callback(list(self.applied_ids)) finally: self.apply_callback = None if changed_books: QApplication.alert(self.gui, 2000)