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(stream): from calibre.ebooks.metadata.archive import is_comic from calibre.ebooks.metadata.meta import get_metadata path = getattr(stream, 'name', False) if not path: pt = PersistentTemporaryFile('_rar-meta.rar') pt.write(stream.read()) pt.close() path = pt.name path = os.path.abspath(path) file_names = list(names(path)) if is_comic(file_names): return get_metadata(stream, 'cbr') for f in file_names: stream_type = os.path.splitext(f)[1].lower() if stream_type: stream_type = stream_type[1:] if stream_type in ('lit', 'opf', 'prc', 'mobi', 'fb2', 'epub', 'rb', 'imp', 'pdf', 'lrf', 'azw'): with TemporaryDirectory() as tdir: with CurrentDir(tdir): stream = extract_member(path, match=None, name=f, as_file=True)[1] return get_metadata(stream, stream_type) raise ValueError('No ebook found in RAR archive')
def get_metadata(stream, cpath=None): if not podofo: raise Unavailable(podofo_err) pt = PersistentTemporaryFile('_podofo.pdf') pt.write(stream.read()) pt.close() server = Server(pool_size=1) job = ParallelJob('read_pdf_metadata', 'Read pdf metadata', lambda x,y:x, args=[pt.name, cpath]) server.add_job(job) while not job.is_finished: time.sleep(0.1) job.update() job.update() server.close() if job.result is None: raise ValueError('Failed to read metadata: ' + job.details) title, authors, creator, tags, ok = job.result if not ok: print 'Failed to extract cover:' print job.details if title == '_': title = getattr(stream, 'name', _('Unknown')) title = os.path.splitext(title)[0] mi = MetaInformation(title, authors) if creator: mi.book_producer = creator if tags: mi.tags = tags if os.path.exists(pt.name): os.remove(pt.name) if ok: mi.cover = cpath return mi
def _download(self, cookie_file, url, filename, save_loc, add_to_lib): dfilename = '' if not url: raise Exception(_('No file specified to download.')) if not save_loc and not add_to_lib: # Nothing to do. return dfilename if not filename: filename = get_download_filename(url, cookie_file) filename, ext = os.path.splitext(filename) filename = filename[:60] + ext filename = ascii_filename(filename) br = browser() if cookie_file: cj = MozillaCookieJar() cj.load(cookie_file) br.set_cookiejar(cj) with closing(br.open(url)) as r: tf = PersistentTemporaryFile(suffix=filename) tf.write(r.read()) dfilename = tf.name return dfilename
def do_download(self, urn): self.lock.lock() try: account_info = self.recipe_model.get_account_info(urn) customize_info = self.recipe_model.get_customize_info(urn) recipe = self.recipe_model.recipe_from_urn(urn) un = pw = None if account_info is not None: un, pw = account_info add_title_tag, custom_tags, keep_issues = customize_info script = self.recipe_model.get_recipe(urn) pt = PersistentTemporaryFile('_builtin.recipe') pt.write(script) pt.close() arg = { 'username': un, 'password': pw, 'add_title_tag':add_title_tag, 'custom_tags':custom_tags, 'recipe':pt.name, 'title':recipe.get('title',''), 'urn':urn, 'keep_issues':keep_issues } self.download_queue.add(urn) self.start_recipe_fetch.emit(arg) finally: self.lock.unlock()
def convert(self, stream, options, file_ext, log, accelerators): from calibre.ebooks.txt.processor import convert_basic stdout = StringIO() ppdjvu = True # using djvutxt is MUCH faster, should make it an option if options.use_djvutxt and os.path.exists('/usr/bin/djvutxt'): from calibre.ptempfile import PersistentTemporaryFile try: fp = PersistentTemporaryFile(suffix='.djvu', prefix='djv_input') filename = fp._name fp.write(stream.read()) fp.close() cmd = ['djvutxt', filename] stdout.write(Popen(cmd, stdout=PIPE, close_fds=True).communicate()[0]) os.remove(filename) ppdjvu = False except: stream.seek(0) # retry with the pure python converter if ppdjvu: from calibre.ebooks.djvu.djvu import DJVUFile x = DJVUFile(stream) x.get_text(stdout) html = convert_basic(stdout.getvalue().replace(b"\n", b' ').replace( b'\037', b'\n\n')) # Run the HTMLized text through the html processing plugin. from calibre.customize.ui import plugin_for_input_format html_input = plugin_for_input_format('html') for opt in html_input.options: setattr(options, opt.option.name, opt.recommended_value) options.input_encoding = 'utf-8' base = os.getcwdu() if file_ext != 'txtz' and hasattr(stream, 'name'): base = os.path.dirname(stream.name) fname = os.path.join(base, 'index.html') c = 0 while os.path.exists(fname): c += 1 fname = 'index%d.html'%c htmlfile = open(fname, 'wb') with htmlfile: htmlfile.write(html.encode('utf-8')) odi = options.debug_pipeline options.debug_pipeline = None # Generate oeb from html conversion. with open(htmlfile.name, 'rb') as f: oeb = html_input.convert(f, options, 'html', log, {}) options.debug_pipeline = odi os.remove(htmlfile.name) # Set metadata from file. from calibre.customize.ui import get_file_type_metadata from calibre.ebooks.oeb.transforms.metadata import meta_info_to_oeb_metadata mi = get_file_type_metadata(stream, file_ext) meta_info_to_oeb_metadata(mi, oeb.metadata, log) return oeb
def create_cover_file(db, book_id): cover = db.cover(book_id, index_is_id=True) cf = None if cover: cf = PersistentTemporaryFile('.jpeg') cf.write(cover) cf.close() return cf
def _download_zip(self, plugin_zip_url): from calibre.ptempfile import PersistentTemporaryFile br = browser() br.set_handle_gzip(True) raw = br.open_novisit(plugin_zip_url).read() pt = PersistentTemporaryFile('.zip') pt.write(raw) pt.close() return pt.name
def render_inline_toc(self): self.rendered_inline_toc = True from calibre.ebooks.pdf.render.toc import toc_as_html raw = toc_as_html(self.toc, self.doc, self.opts) pt = PersistentTemporaryFile('_pdf_itoc.htm') pt.write(raw) pt.close() self.render_queue.append(pt.name) self.render_next()
def prince_convert(self): ''' Call the actual Prince command to convert to PDF ''' from os import makedirs from os.path import dirname, join, exists from calibre.ptempfile import PersistentTemporaryFile from calibre.constants import DEBUG # All files are relative to the OPF location opf_dir = dirname(self.opf) base_dir = dirname(self.pdf_file) base_dir = join(opf_dir, base_dir) try: makedirs(base_dir) except BaseException: if not exists(base_dir): raise # Create a temporary CSS file with the box contents custom_CSS = PersistentTemporaryFile() custom_CSS.write(unicode(self.css1.toPlainText())) custom_CSS.close() # Create a temporary file with the list of input files file_list = PersistentTemporaryFile() for item in self.oeb.spine: file_list.write(item.href + "\n") file_list.close() # Build the command line command = prefs['prince_exe'] args = ['-v'] if self.prince_file: args.append('-s') args.append(self.prince_file) args.append('-s') args.append(custom_CSS.name) args.append('-l') args.append(file_list.name) args.append('-o') args.append(self.pdf_file) # Hide the convert button and show a busy indicator self.convert.setEnabled(False) self.progress_bar = QProgressBar() self.progress_bar.setRange(0,0) self.progress_bar.setValue(0) self.l.addWidget(self.progress_bar) # Run the command and return the path to the PDF file if DEBUG: print(_('Converting book...')) process = QProcess(self) process.setWorkingDirectory(opf_dir) process.setProcessChannelMode(QProcess.MergedChannels); process.error.connect(self.error) process.finished.connect(self.end) self.process = process process.start(command, args)
def render_inline_toc(self): evaljs = self.view.page().mainFrame().evaluateJavaScript self.rendered_inline_toc = True from calibre.ebooks.pdf.render.toc import toc_as_html raw = toc_as_html(self.toc, self.doc, self.opts, evaljs) pt = PersistentTemporaryFile('_pdf_itoc.htm') pt.write(raw) pt.close() self.render_queue.append(pt.name) self.render_next()
def render_inline_toc(self): evaljs = self.view.page().mainFrame().evaluateJavaScript self.rendered_inline_toc = True from calibre.ebooks.pdf.render.toc import toc_as_html raw = toc_as_html(self.toc, self.doc, self.opts, evaljs) pt = PersistentTemporaryFile('_pdf_itoc.htm') pt.write(raw) pt.close() self.render_queue.append(pt.name) self.render_next()
def create_opf_file(db, book_id): mi = db.get_metadata(book_id, index_is_id=True) mi.application_id = uuid.uuid4() old_cover = mi.cover mi.cover = None raw = metadata_to_opf(mi) mi.cover = old_cover opf_file = PersistentTemporaryFile('.opf') opf_file.write(raw) opf_file.close() return mi, opf_file
def create_opf_file(db, book_id): mi = db.get_metadata(book_id, index_is_id=True) mi.application_id = uuid.uuid4() old_cover = mi.cover mi.cover = None raw = metadata_to_opf(mi) mi.cover = old_cover opf_file = PersistentTemporaryFile('.opf') opf_file.write(raw) opf_file.close() return mi, opf_file
def calibre_cover(title, author_string, series_string=None, output_format='jpg', title_size=46, author_size=36, logo_path=None): title = normalize(title) author_string = normalize(author_string) series_string = normalize(series_string) from calibre.utils.magick.draw import create_cover_page, TextLine import regex pat = regex.compile( ur'\p{Cf}+', flags=regex.VERSION1) # remove non-printing chars like the soft hyphen text = pat.sub(u'', title + author_string + (series_string or u'')) font_path = P('fonts/liberation/LiberationSerif-Bold.ttf') from calibre.utils.fonts.utils import get_font_for_text font = open(font_path, 'rb').read() c = get_font_for_text(text, font) cleanup = False if c is not None and c != font: from calibre.ptempfile import PersistentTemporaryFile pt = PersistentTemporaryFile('.ttf') pt.write(c) pt.close() font_path = pt.name cleanup = True lines = [ TextLine(pat.sub(u'', title), title_size, font_path=font_path), TextLine(pat.sub(u'', author_string), author_size, font_path=font_path) ] if series_string: lines.append( TextLine(pat.sub(u'', series_string), author_size, font_path=font_path)) if logo_path is None: logo_path = I('library.png') try: return create_cover_page(lines, logo_path, output_format='jpg', texture_opacity=0.3, texture_data=I('cover_texture.png', data=True)) finally: if cleanup: os.remove(font_path)
def __init__(self, text, font_size, bottom_margin=30, font_path=None): self.text, self.font_size, = text, font_size self.bottom_margin = bottom_margin if font_path is None: if not isinstance(text, unicode): text = force_unicode(text) from calibre.utils.fonts.utils import get_font_for_text fd = get_font_for_text(text) if fd is not None: from calibre.ptempfile import PersistentTemporaryFile pt = PersistentTemporaryFile('.ttf') pt.write(fd) pt.close() font_path = pt.name self.font_path = font_path
def __init__(self, text, font_size, bottom_margin=30, font_path=None): self.text, self.font_size, = text, font_size self.bottom_margin = bottom_margin if font_path is None: if not isinstance(text, unicode): text = force_unicode(text) from calibre.utils.fonts.utils import get_font_for_text fd = get_font_for_text(text) if fd is not None: from calibre.ptempfile import PersistentTemporaryFile pt = PersistentTemporaryFile('.ttf') pt.write(fd) pt.close() font_path = pt.name self.font_path = font_path
def calibre_cover(title, author_string, series_string=None, output_format='jpg', title_size=46, author_size=36, logo_path=None): from calibre.utils.config_base import tweaks title = normalize(title) author_string = normalize(author_string) series_string = normalize(series_string) from calibre.utils.magick.draw import create_cover_page, TextLine text = title + author_string + (series_string or u'') font_path = tweaks['generate_cover_title_font'] if font_path is None: font_path = P('fonts/liberation/LiberationSerif-Bold.ttf') from calibre.utils.fonts.utils import get_font_for_text font = open(font_path, 'rb').read() c = get_font_for_text(text, font) cleanup = False if c is not None and c != font: from calibre.ptempfile import PersistentTemporaryFile pt = PersistentTemporaryFile('.ttf') pt.write(c) pt.close() font_path = pt.name cleanup = True lines = [ TextLine(title, title_size, font_path=font_path), TextLine(author_string, author_size, font_path=font_path) ] if series_string: lines.append(TextLine(series_string, author_size, font_path=font_path)) if logo_path is None: logo_path = I('library.png') try: return create_cover_page(lines, logo_path, output_format='jpg', texture_opacity=0.3, texture_data=I('cover_texture.png', data=True)) finally: if cleanup: os.remove(font_path)
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 = self.get_metadata() self.cover_file = self.opf_file = None if self.db is not None: self.mi, self.opf_file = create_opf_file(self.db, self.book_id) 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 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 = self.get_metadata() self.cover_file = self.opf_file = None if self.db is not None: self.mi, self.opf_file = create_opf_file(self.db, self.book_id) 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_recipe(urls, title): fmt = cprefs['output_format'].lower() pt = PersistentTemporaryFile(suffix='_recipe_out.%s' % fmt.lower()) pt.close() recs = [] ps = load_defaults('page_setup') if 'output_profile' in ps: recs.append(('output_profile', ps['output_profile'], OptionRecommendation.HIGH)) lf = load_defaults('look_and_feel') if lf.get('base_font_size', 0.0) != 0.0: recs.append(('base_font_size', lf['base_font_size'], OptionRecommendation.HIGH)) recs.append(('keep_ligatures', lf.get('keep_ligatures', False), OptionRecommendation.HIGH)) lr = load_defaults('lrf_output') if lr.get('header', False): recs.append(('header', True, OptionRecommendation.HIGH)) recs.append(('header_format', '%t', OptionRecommendation.HIGH)) epub = load_defaults('epub_output') if epub.get('epub_flatten', False): recs.append(('epub_flatten', True, OptionRecommendation.HIGH)) recipe = get_resources('wikipedia.recipe') url_line = bytes(repr(urls)) recipe = re.sub( br'^(\s+urls = ).+?# REPLACE_ME_URLS', br'\1' + url_line, recipe, flags=re.M) if title.strip(): title_line = bytes(repr(title)) recipe = re.sub( br'^(\s+title\s+=\s+)DEFAULT_TITLE', br'\1' + title_line, recipe, flags=re.M) logo = get_resources('images/icon.png') lf = PersistentTemporaryFile('_wiki_logo.png') lf.write(logo) recipe = recipe.replace(b'LOGO = None', b'LOGO = %r' % lf.name) lf.close() rf = PersistentTemporaryFile(suffix='_wikipedia.recipe') rf.write(recipe) rf.close() args = [rf.name, pt.name, recs] return args, fmt.upper(), [pt, rf, lf]
def extract_content(self, output_dir): self.log.info('Extracting PDF...') pdf = PersistentTemporaryFile('.pdf') pdf.close() pdf = open(pdf, 'wb') for x in xrange(self.header.section_count()): pdf.write(self.header.section_data(x)) pdf.close() from calibre.customize.ui import plugin_for_input_format pdf_plugin = plugin_for_input_format('pdf') for opt in pdf_plugin.options: if not hasattr(self.options, opt.option.name): setattr(self.options, opt.option.name, opt.recommended_value) return pdf_plugin.convert(open(pdf, 'rb'), self.options, 'pdf', self.log, {})
def share(self): index = self.available_profiles.currentIndex() title, src = self._model.title(index), self._model.script(index) if not title or not src: error_dialog(self, _('No recipe selected'), _('No recipe selected')).exec_() return pt = PersistentTemporaryFile(suffix='.recipe') pt.write(src.encode('utf-8')) pt.close() body = _('The attached file: %(fname)s is a ' 'recipe to download %(title)s.')%dict( fname=os.path.basename(pt.name), title=title) subject = _('Recipe for ')+title url = QUrl('mailto:') url.addQueryItem('subject', subject) url.addQueryItem('body', body) url.addQueryItem('attachment', pt.name) open_url(url)
def share(self): index = self.available_profiles.currentIndex() title, src = self._model.title(index), self._model.script(index) if not title or not src: error_dialog(self, _("No recipe selected"), _("No recipe selected")).exec_() return pt = PersistentTemporaryFile(suffix=".recipe") pt.write(src.encode("utf-8")) pt.close() body = _("The attached file: %(fname)s is a " "recipe to download %(title)s.") % dict( fname=os.path.basename(pt.name), title=title ) subject = _("Recipe for ") + title url = QUrl("mailto:") url.addQueryItem("subject", subject) url.addQueryItem("body", body) url.addQueryItem("attachment", pt.name) open_url(url)
def calibre_cover( title, author_string, series_string=None, output_format="jpg", title_size=46, author_size=36, logo_path=None ): from calibre.utils.config_base import tweaks title = normalize(title) author_string = normalize(author_string) series_string = normalize(series_string) from calibre.utils.magick.draw import create_cover_page, TextLine text = title + author_string + (series_string or u"") font_path = tweaks["generate_cover_title_font"] if font_path is None: font_path = P("fonts/liberation/LiberationSerif-Bold.ttf") from calibre.utils.fonts.utils import get_font_for_text font = open(font_path, "rb").read() c = get_font_for_text(text, font) cleanup = False if c is not None and c != font: from calibre.ptempfile import PersistentTemporaryFile pt = PersistentTemporaryFile(".ttf") pt.write(c) pt.close() font_path = pt.name cleanup = True lines = [ TextLine(title, title_size, font_path=font_path), TextLine(author_string, author_size, font_path=font_path), ] if series_string: lines.append(TextLine(series_string, author_size, font_path=font_path)) if logo_path is None: logo_path = I("library.png") try: return create_cover_page( lines, logo_path, output_format="jpg", texture_opacity=0.3, texture_data=I("cover_texture.png", data=True) ) finally: if cleanup: os.remove(font_path)
def paste_image(self): c = QApplication.instance().clipboard() img = c.image() if img.isNull(): img = c.image(c.Selection) if img.isNull(): return error_dialog(self, _('No image'), _( 'There is no image on the clipboard'), show=True) d = ChooseName('image.jpg', self) if d.exec_() == d.Accepted and d.filename: fmt = d.filename.rpartition('.')[-1].lower() if fmt not in {'jpg', 'jpeg', 'png'}: return error_dialog(self, _('Invalid file extension'), _( 'The file name you choose must have a .jpg or .png extension'), show=True) t = PersistentTemporaryFile(prefix='editor-paste-image-', suffix='.' + fmt) t.write(pixmap_to_data(img, fmt)) t.close() self.chosen_image_is_external = (d.filename, t.name) self.accept()
def files_for_family(self, family, normalize=True): ''' Find all the variants in the font family `family`. Returns a dictionary of tuples. Each tuple is of the form (path to font file, Full font name). The keys of the dictionary depend on `normalize`. If `normalize` is `False`, they are a tuple (slant, weight) otherwise they are strings from the set `('normal', 'bold', 'italic', 'bi', 'light', 'li')` ''' if iswindows: from calibre.ptempfile import PersistentTemporaryFile fonts = self.backend.fonts_for_family(family, normalize=normalize) ans = {} for ft, val in fonts.iteritems(): ext, name, data = val pt = PersistentTemporaryFile('.'+ext) pt.write(data) pt.close() ans[ft] = (pt.name, name) return ans return self.backend.files_for_family(family, normalize=normalize)
def get_cookies(self): ''' Writes QNetworkCookies to Mozilla cookie .txt file. :return: The file path to the cookie file. ''' cf = PersistentTemporaryFile(suffix='.txt') cf.write('# Netscape HTTP Cookie File\n\n') for c in self.page().networkAccessManager().cookieJar().allCookies(): cookie = [] domain = unicode(c.domain()) cookie.append(domain) cookie.append('TRUE' if domain.startswith('.') else 'FALSE') cookie.append(unicode(c.path())) cookie.append('TRUE' if c.isSecure() else 'FALSE') cookie.append(unicode(c.expirationDate().toTime_t())) cookie.append(unicode(c.name())) cookie.append(unicode(c.value())) cf.write('\t'.join(cookie)) cf.write('\n') cf.close() return cf.name
def calibre_cover(title, author_string, series_string=None, output_format='jpg', title_size=46, author_size=36, logo_path=None): from calibre.utils.config_base import tweaks title = normalize(title) author_string = normalize(author_string) series_string = normalize(series_string) from calibre.utils.magick.draw import create_cover_page, TextLine import regex pat = regex.compile(ur'\p{Cf}+', flags=regex.VERSION1) # remove non-printing chars like the soft hyphen text = pat.sub(u'', title + author_string + (series_string or u'')) font_path = tweaks['generate_cover_title_font'] if font_path is None: font_path = P('fonts/liberation/LiberationSerif-Bold.ttf') from calibre.utils.fonts.utils import get_font_for_text font = open(font_path, 'rb').read() c = get_font_for_text(text, font) cleanup = False if c is not None and c != font: from calibre.ptempfile import PersistentTemporaryFile pt = PersistentTemporaryFile('.ttf') pt.write(c) pt.close() font_path = pt.name cleanup = True lines = [TextLine(pat.sub(u'', title), title_size, font_path=font_path), TextLine(pat.sub(u'', author_string), author_size, font_path=font_path)] if series_string: lines.append(TextLine(pat.sub(u'', series_string), author_size, font_path=font_path)) if logo_path is None: logo_path = I('library.png') try: return create_cover_page(lines, logo_path, output_format='jpg', texture_opacity=0.3, texture_data=I('cover_texture.png', data=True)) finally: if cleanup: os.remove(font_path)
def _download(self, url, filename, save_loc): dfilename = '' if not url: raise Exception(_('No file specified to download.')) if not save_loc: # Nothing to do. return dfilename if not filename: filename = get_download_filename(url) filename, ext = os.path.splitext(filename) filename = filename[:60] + ext filename = ascii_filename(filename) br = browser() with closing(br.open(url)) as r: tf = PersistentTemporaryFile(suffix=filename) tf.write(r.read()) dfilename = tf.name return dfilename
def _download(self, url, filename, save_loc): dfilename = '' if not url: raise Exception(_('No file specified to download.')) if not save_loc: # Nothing to do. return dfilename if not filename: filename = get_download_filename(url) filename, ext = os.path.splitext(filename) filename = filename[:60] + ext filename = ascii_filename(filename) br = browser() with closing(br.open(url)) as r: tf = PersistentTemporaryFile(suffix=filename) tf.write(r.read()) dfilename = tf.name return dfilename
def get_metadata(stream): from calibre.ebooks.metadata.archive import is_comic from calibre.ebooks.metadata.meta import get_metadata path = getattr(stream, "name", False) if not path: pt = PersistentTemporaryFile("_rar-meta.rar") pt.write(stream.read()) pt.close() path = pt.name path = os.path.abspath(path) file_names = list(names(path)) if is_comic(file_names): return get_metadata(stream, "cbr") for f in file_names: stream_type = os.path.splitext(f)[1].lower() if stream_type: stream_type = stream_type[1:] if stream_type in ( "lit", "opf", "prc", "mobi", "fb2", "epub", "rb", "imp", "pdf", "lrf", "azw", "azw1", "azw3", ): with TemporaryDirectory() as tdir: with CurrentDir(tdir): stream = extract_member(path, match=None, name=f, as_file=True)[1] return get_metadata(stream, stream_type) raise ValueError("No ebook found in RAR archive")
def get_metadata(stream, cpath=None): if not podofo: raise Unavailable(podofo_err) pt = PersistentTemporaryFile('_podofo.pdf') pt.write(stream.read()) pt.close() server = Server(pool_size=1) job = ParallelJob('read_pdf_metadata', 'Read pdf metadata', lambda x, y: x, args=[pt.name, cpath]) server.add_job(job) while not job.is_finished: time.sleep(0.1) job.update() job.update() server.close() if job.result is None: raise ValueError('Failed to read metadata: ' + job.details) title, authors, creator, tags, ok = job.result if not ok: print 'Failed to extract cover:' print job.details if title == '_': title = getattr(stream, 'name', _('Unknown')) title = os.path.splitext(title)[0] mi = MetaInformation(title, authors) if creator: mi.book_producer = creator if tags: mi.tags = tags if os.path.exists(pt.name): os.remove(pt.name) if ok: mi.cover = cpath return mi
except: pass if self.opts.use_existing_cover and not existing_cover: log.warning("no existing catalog cover found") if self.opts.use_existing_cover and existing_cover: recommendations.append( ('cover', cpath, OptionRecommendation.HIGH)) log.info("using existing catalog cover") else: log.info("replacing catalog cover") new_cover_path = PersistentTemporaryFile(suffix='.jpg') new_cover = calibre_cover( opts.catalog_title.replace('"', '\\"'), 'calibre') new_cover_path.write(new_cover) new_cover_path.close() recommendations.append( ('cover', new_cover_path.name, OptionRecommendation.HIGH)) # Run ebook-convert from calibre.ebooks.conversion.plumber import Plumber plumber = Plumber(os.path.join(catalog.catalog_path, opts.basename + '.opf'), path_to_output, log, report_progress=notification, abort_after_input_dump=False) plumber.merge_ui_recommendations(recommendations) plumber.run()
def convert_single_ebook( parent, db, book_ids, auto_conversion=False, # {{{ out_format=None, show_no_format_warning=True): changed = False jobs = [] bad = [] total = len(book_ids) if total == 0: return None, None, None for i, book_id in enumerate(book_ids): temp_files = [] try: d = SingleConfig(parent, db, book_id, None, out_format) if auto_conversion: d.accept() result = QDialog.Accepted else: result = d.exec_() if result == QDialog.Accepted: #if not convert_existing(parent, db, [book_id], d.output_format): # continue mi = db.get_metadata(book_id, True) in_file = PersistentTemporaryFile('.' + d.input_format) with in_file: input_fmt = db.original_fmt(book_id, d.input_format).lower() same_fmt = input_fmt == d.output_format.lower() db.copy_format_to(book_id, input_fmt, in_file, index_is_id=True) out_file = PersistentTemporaryFile('.' + d.output_format) out_file.write(d.output_format) out_file.close() temp_files = [in_file] try: dtitle = unicode(mi.title) except: dtitle = repr(mi.title) desc = _('Convert book %(num)d of %(total)d (%(title)s)') % \ {'num':i + 1, 'total':total, 'title':dtitle} recs = cPickle.loads(d.recommendations) if d.opf_file is not None: recs.append(('read_metadata_from_opf', d.opf_file.name, OptionRecommendation.HIGH)) temp_files.append(d.opf_file) if d.cover_file is not None: recs.append(('cover', d.cover_file.name, OptionRecommendation.HIGH)) temp_files.append(d.cover_file) args = [in_file.name, out_file.name, recs] temp_files.append(out_file) func = 'gui_convert_override' if same_fmt: func += ':same_fmt' jobs.append((func, args, desc, d.output_format.upper(), book_id, temp_files)) changed = True d.break_cycles() except NoSupportedInputFormats: bad.append(book_id) if bad and show_no_format_warning: res = [] for id in bad: title = db.title(id, True) res.append('%s' % title) msg = '%s' % '\n'.join(res) warning_dialog( parent, _('Could not convert some books'), _('Could not convert %(num)d of %(tot)d books, because no suitable source' ' format was found.') % dict(num=len(res), tot=total), msg).exec_() return jobs, changed, bad
def convert_single_ebook( parent, db, book_ids, auto_conversion=False, # {{{ out_format=None, show_no_format_warning=True): changed = False jobs = [] bad = [] total = len(book_ids) if total == 0: return None, None, None for i, book_id in enumerate(book_ids): temp_files = [] try: d = SingleConfig(parent, db, book_id, None, out_format) if auto_conversion: d.accept() result = QDialog.Accepted else: result = d.exec_() if result == QDialog.Accepted: # if not convert_existing(parent, db, [book_id], d.output_format): # continue mi = db.get_metadata(book_id, True) in_file = PersistentTemporaryFile('.' + d.input_format) with in_file: input_fmt = db.original_fmt(book_id, d.input_format).lower() same_fmt = input_fmt == d.output_format.lower() db.copy_format_to(book_id, input_fmt, in_file, index_is_id=True) out_file = PersistentTemporaryFile('.' + d.output_format) out_file.write(d.output_format) out_file.close() temp_files = [in_file] try: dtitle = unicode(mi.title) except: dtitle = repr(mi.title) desc = _('Convert book %(num)d of %(total)d (%(title)s)') % \ {'num':i + 1, 'total':total, 'title':dtitle} recs = cPickle.loads(d.recommendations) if d.opf_file is not None: recs.append(('read_metadata_from_opf', d.opf_file.name, OptionRecommendation.HIGH)) temp_files.append(d.opf_file) if d.cover_file is not None: recs.append(('cover', d.cover_file.name, OptionRecommendation.HIGH)) temp_files.append(d.cover_file) args = [in_file.name, out_file.name, recs] temp_files.append(out_file) func = 'gui_convert_override' parts = [] if not auto_conversion and d.manually_fine_tune_toc: parts.append('manually_fine_tune_toc') if same_fmt: parts.append('same_fmt') if parts: func += ':%s' % (';'.join(parts)) jobs.append((func, args, desc, d.output_format.upper(), book_id, temp_files)) changed = True d.break_cycles() except NoSupportedInputFormats as nsif: bad.append((book_id, nsif.available_formats)) if bad and show_no_format_warning: if len(bad) == 1 and not bad[0][1]: title = db.title(bad[0][0], True) warning_dialog( parent, _('Could not convert'), '<p>' + _('Could not convert <b>%s</b> as it has no e-book files. If you ' 'think it should have files, but calibre is not finding ' 'them, that is most likely because you moved the book\'s ' 'files around outside of calibre. You will need to find those files ' 'and re-add them to calibre.') % title, show=True) else: res = [] for id, available_formats in bad: title = db.title(id, True) if available_formats: msg = _('No supported formats (Available formats: %s)') % ( ', '.join(available_formats)) else: msg = _('This book has no actual e-book files') res.append('%s - %s' % (title, msg)) msg = '%s' % '\n'.join(res) warning_dialog( parent, _('Could not convert some books'), ngettext( 'Could not convert the book because no supported source format was found', 'Could not convert {num} of {tot} books, because no supported source formats were found.', len(res)).format(num=len(res), tot=total), msg).exec_() return jobs, changed, bad
def do_book(self): if self.i >= len(self.book_ids): return self.do_queue() book_id = self.book_ids[self.i] self.i += 1 temp_files = [] try: input_format = get_input_format_for_book(self.db, book_id, None)[0] input_fmt = self.db.original_fmt(book_id, input_format).lower() same_fmt = input_fmt == self.output_format.lower() mi, opf_file = create_opf_file(self.db, book_id) in_file = PersistentTemporaryFile('.' + input_format) with in_file: self.db.copy_format_to(book_id, input_fmt, in_file, index_is_id=True) out_file = PersistentTemporaryFile('.' + self.output_format) out_file.write(self.output_format) out_file.close() temp_files = [in_file] combined_recs = GuiRecommendations() default_recs = bulk_defaults_for_input_format(input_format) for key in default_recs: combined_recs[key] = default_recs[key] if self.use_saved_single_settings: specific_recs = load_specifics(self.db, book_id) for key in specific_recs: combined_recs[key] = specific_recs[key] for item in self.user_recs: combined_recs[item[0]] = item[1] save_specifics(self.db, book_id, combined_recs) lrecs = list(combined_recs.to_recommendations()) from calibre.customize.ui import plugin_for_output_format op = plugin_for_output_format(self.output_format) if op and op.recommendations: prec = {x[0] for x in op.recommendations} for i, r in enumerate(list(lrecs)): if r[0] in prec: lrecs[i] = (r[0], r[1], OptionRecommendation.HIGH) cover_file = create_cover_file(self.db, book_id) if opf_file is not None: lrecs.append(('read_metadata_from_opf', opf_file.name, OptionRecommendation.HIGH)) temp_files.append(opf_file) if cover_file is not None: lrecs.append( ('cover', cover_file.name, OptionRecommendation.HIGH)) temp_files.append(cover_file) for x in list(lrecs): if x[0] == 'debug_pipeline': lrecs.remove(x) try: dtitle = unicode(mi.title) except: dtitle = repr(mi.title) if len(dtitle) > 50: dtitle = dtitle[:50].rpartition(' ')[0] + '...' self.setLabelText(_('Queueing ') + dtitle) desc = _('Convert book %(num)d of %(tot)d (%(title)s)') % dict( num=self.i, tot=len(self.book_ids), title=dtitle) args = [in_file.name, out_file.name, lrecs] temp_files.append(out_file) func = 'gui_convert_override' if same_fmt: func += ':same_fmt' self.jobs.append((func, args, desc, self.output_format.upper(), book_id, temp_files)) self.changed = True self.setValue(self.i) except NoSupportedInputFormats: self.bad.append(book_id) QTimer.singleShot(0, self.do_book)
def modify_epub(container, filename, metadata=None, opts={}): print(str(opts)) # Search for the ePub cover found_cover = False opf = container.opf cover_meta_node = opf.xpath('./opf:metadata/opf:meta[@name="cover"]', namespaces=OPF_NAMESPACES) if len(cover_meta_node) > 0: cover_meta_node = cover_meta_node[0] cover_id = cover_meta_node.attrib[ "content"] if "content" in cover_meta_node.attrib else None if cover_id is not None: print( "KoboTouchExtended:common:modify_epub:Found cover image ID '{0}'" .format(cover_id)) cover_node = opf.xpath( './opf:manifest/opf:item[@id="{0}"]'.format(cover_id), namespaces=OPF_NAMESPACES) if len(cover_node) > 0: cover_node = cover_node[0] if "properties" not in cover_node.attrib or cover_node.attrib[ "properties"] != "cover-image": print( "KoboTouchExtended:common:modify_epub:Setting cover-image property" ) cover_node.set("properties", "cover-image") container.dirty(container.opf_name) found_cover = True # It's possible that the cover image can't be detected this way. Try looking for the cover image ID in the OPF manifest. if not found_cover: print( "KoboTouchExtended:common:modify_epub:Looking for cover image in OPF manifest" ) node_list = opf.xpath( './opf:manifest/opf:item[(@id="cover" or starts-with(@id, "cover")) and starts-with(@media-type, "image")]', namespaces=OPF_NAMESPACES) if len(node_list) > 0: node = node_list[0] if "properties" not in node.attrib or node.attrib[ "properties"] != 'cover-image': print( "KoboTouchExtended:common:modify_epub:Setting cover-image") node.set("properties", "cover-image") container.dirty(container.opf_name) found_cover = True # Because of the changes made to the markup here, cleanup needs to be done before any other content file processing container.forced_cleanup() if 'clean_markup' in opts and opts['clean_markup'] is True: container.clean_markup() # Hyphenate files? if 'hyphenate' in opts and opts['hyphenate'] is True: if ('replace_lang' not in opts or opts['replace_lang'] is not True) or (metadata is not None and metadata.language == NULL_VALUES['language']): print( "KoboTouchExtended:common:modify_epub:WARNING - Hyphenation is enabled but not overriding content file language. Hyphenation may use the wrong dictionary." ) hyphenation_css = PersistentTemporaryFile(suffix='_hyphenate', prefix='kepub_') hyphenation_css.write(get_resources('css/hyphenation.css')) hyphenation_css.close() css_path = os.path.basename( container.copy_file_to_container(hyphenation_css.name, name='kte-css/hyphenation.css')) container.add_content_file_reference("kte-css/{0}".format(css_path)) # Override content file language if 'replace_lang' in opts and opts['replace_lang'] is True and ( metadata is not None and metadata.language != NULL_VALUES["language"]): # First override for the OPF file lang_node = container.opf_xpath('//opf:metadata/dc:language') if len(lang_node) > 0: print( "KoboTouchExtended:common:modify_epub:Overriding OPF language") lang_node = lang_node[0] lang_node.text = metadata.language else: print("KoboTouchExtended:common:modify_epub:Setting OPF language") metadata_node = container.opf_xpath('//opf:metadata')[0] lang_node = metadata_node.makeelement("{%s}language" % OPF_NAMESPACES['dc']) lang_node.text = metadata.language container.insert_into_xml(metadata_node, lang_node) container.dirty(container.opf_name) # Now override for content files for name in container.get_html_names(): print( "KoboTouchExtended:common:modify_epub:Overriding content file language :: {0}" .format(name)) root = container.parsed(name) root.attrib["{%s}lang" % XML_NAMESPACE] = metadata.language root.attrib["lang"] = metadata.language # Now smarten punctuation if 'smarten_punctuation' in opts and opts['smarten_punctuation'] is True: container.smarten_punctuation() if 'extended_kepub_features' in opts and opts[ 'extended_kepub_features'] is True: if metadata is not None: print( "KoboTouchExtended:common:modify_epub:Adding extended Kobo features to {0} by {1}" .format(metadata.title, ' and '.join(metadata.authors))) # Add the Kobo span tags container.add_kobo_spans() skip_js = False # Check to see if there's already a kobo*.js in the ePub for name in container.name_path_map: if kobo_js_re.match(name): skip_js = True break if not skip_js: if os.path.isfile(reference_kepub): reference_container = EpubContainer(reference_kepub, default_log) for name in reference_container.name_path_map: if kobo_js_re.match(name): jsname = container.copy_file_to_container( os.path.join(reference_container.root, name), name='kobo.js') container.add_content_file_reference(jsname) break os.unlink(filename) container.commit(filename)
def prince_convert(self): ''' Call the actual Prince command to convert to PDF ''' from os import makedirs from os.path import dirname, join, exists from calibre.ptempfile import PersistentTemporaryFile from calibre.constants import DEBUG from shlex import split as shsplit # All files are relative to the OPF location opf_dir = dirname(self.opf) base_dir = dirname(self.pdf_file) base_dir = join(opf_dir, base_dir) try: makedirs(base_dir) except BaseException: if not exists(base_dir): raise # Create a temporary CSS file with the box contents custom_CSS = PersistentTemporaryFile(mode='w+') custom_CSS.write(unicode(self.css1.toPlainText())) custom_CSS.close() # Create a temporary file with the list of input files file_list = PersistentTemporaryFile(mode='w+') for item in self.oeb.spine: file_list.write(item.href + "\n") file_list.close() # Build the command line command = prefs['prince_exe'] args = ['-v'] if self.prince_file: args.append('-s') args.append(self.prince_file) args.append('-s') args.append(custom_CSS.name) args.append('-l') args.append(file_list.name) args.append('-o') args.append(self.pdf_file) # Additional command-line arguments args.extend(shsplit(self.args.text())) # Hide the convert button and show a busy indicator self.convert.setEnabled(False) self.progress_bar = QProgressBar() self.progress_bar.setRange(0, 0) self.progress_bar.setValue(0) self.l.addWidget(self.progress_bar) # Run the command and return the path to the PDF file if DEBUG: print(_('Converting book...')) process = QProcess(self) process.setWorkingDirectory(opf_dir) process.setProcessChannelMode(QProcess.MergedChannels) process.error.connect(self.error) process.finished.connect(self.end) self.process = process if DEBUG: from subprocess import list2cmdline line = list2cmdline([command] + args) print(_('Command line: %s') % line) process.start(command, args)
def run(self, path_to_output, opts, db, notification=DummyReporter()): from calibre.library.catalogs.epub_mobi_builder import CatalogBuilder from calibre.utils.logging import default_log as log from calibre.utils.config import JSONConfig # If preset specified from the cli, insert stored options from JSON file if hasattr(opts, 'preset') and opts.preset: available_presets = JSONConfig("catalog_presets") if opts.preset not in available_presets: if available_presets: print(_('Error: Preset "%s" not found.' % opts.preset)) print(_('Stored presets: %s' % ', '.join([p for p in sorted(available_presets.keys())]))) else: print(_('Error: No stored presets.')) return 1 # Copy the relevant preset values to the opts object for item in available_presets[opts.preset]: if item not in ['exclusion_rules_tw', 'format', 'prefix_rules_tw']: setattr(opts, item, available_presets[opts.preset][item]) # Provide an unconnected device opts.connected_device = { 'is_device_connected': False, 'kind': None, 'name': None, 'save_template': None, 'serial': None, 'storage': None, } # Convert prefix_rules and exclusion_rules from JSON lists to tuples prs = [] for rule in opts.prefix_rules: prs.append(tuple(rule)) opts.prefix_rules = tuple(prs) ers = [] for rule in opts.exclusion_rules: ers.append(tuple(rule)) opts.exclusion_rules = tuple(ers) opts.log = log opts.fmt = self.fmt = path_to_output.rpartition('.')[2] # Add local options opts.creator = '%s, %s %s, %s' % (strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y')) opts.creator_sort_as = '%s %s' % ('calibre', strftime('%Y-%m-%d')) opts.connected_kindle = False # Finalize output_profile op = opts.output_profile if op is None: op = 'default' if opts.connected_device['name'] and 'kindle' in opts.connected_device['name'].lower(): opts.connected_kindle = True if opts.connected_device['serial'] and \ opts.connected_device['serial'][:4] in ['B004', 'B005']: op = "kindle_dx" else: op = "kindle" opts.description_clip = 380 if op.endswith('dx') or 'kindle' not in op else 100 opts.author_clip = 100 if op.endswith('dx') or 'kindle' not in op else 60 opts.output_profile = op opts.basename = "Catalog" opts.cli_environment = not hasattr(opts, 'sync') # Hard-wired to always sort descriptions by author, with series after non-series opts.sort_descriptions_by_author = True build_log = [] build_log.append(u"%s('%s'): Generating %s %sin %s environment, locale: '%s'" % (self.name, current_library_name(), self.fmt, 'for %s ' % opts.output_profile if opts.output_profile else '', 'CLI' if opts.cli_environment else 'GUI', calibre_langcode_to_name(canonicalize_lang(get_lang()), localize=False)) ) # If exclude_genre is blank, assume user wants all tags as genres if opts.exclude_genre.strip() == '': # opts.exclude_genre = '\[^.\]' # build_log.append(" converting empty exclude_genre to '\[^.\]'") opts.exclude_genre = 'a^' build_log.append(" converting empty exclude_genre to 'a^'") if opts.connected_device['is_device_connected'] and \ opts.connected_device['kind'] == 'device': if opts.connected_device['serial']: build_log.append(u" connected_device: '%s' #%s%s " % (opts.connected_device['name'], opts.connected_device['serial'][0:4], 'x' * (len(opts.connected_device['serial']) - 4))) for storage in opts.connected_device['storage']: if storage: build_log.append(u" mount point: %s" % storage) else: build_log.append(u" connected_device: '%s'" % opts.connected_device['name']) try: for storage in opts.connected_device['storage']: if storage: build_log.append(u" mount point: %s" % storage) except: build_log.append(u" (no mount points)") else: build_log.append(u" connected_device: '%s'" % opts.connected_device['name']) opts_dict = vars(opts) if opts_dict['ids']: build_log.append(" book count: %d" % len(opts_dict['ids'])) sections_list = [] if opts.generate_authors: sections_list.append('Authors') if opts.generate_titles: sections_list.append('Titles') if opts.generate_series: sections_list.append('Series') if opts.generate_genres: sections_list.append('Genres') if opts.generate_recently_added: sections_list.append('Recently Added') if opts.generate_descriptions: sections_list.append('Descriptions') if not sections_list: if opts.cli_environment: opts.log.warn('*** No Section switches specified, enabling all Sections ***') opts.generate_authors = True opts.generate_titles = True opts.generate_series = True opts.generate_genres = True opts.generate_recently_added = True opts.generate_descriptions = True sections_list = ['Authors', 'Titles', 'Series', 'Genres', 'Recently Added', 'Descriptions'] else: opts.log.warn('\n*** No enabled Sections, terminating catalog generation ***') return ["No Included Sections", "No enabled Sections.\nCheck E-book options tab\n'Included sections'\n"] if opts.fmt == 'mobi' and sections_list == ['Descriptions']: warning = _("\n*** Adding 'By authors' section required for MOBI output ***") opts.log.warn(warning) sections_list.insert(0, 'Authors') opts.generate_authors = True opts.log(u" Sections: %s" % ', '.join(sections_list)) opts.section_list = sections_list # Limit thumb_width to 1.0" - 2.0" try: if float(opts.thumb_width) < float(self.THUMB_SMALLEST): log.warning("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width, self.THUMB_SMALLEST)) opts.thumb_width = self.THUMB_SMALLEST if float(opts.thumb_width) > float(self.THUMB_LARGEST): log.warning("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width, self.THUMB_LARGEST)) opts.thumb_width = self.THUMB_LARGEST opts.thumb_width = "%.2f" % float(opts.thumb_width) except: log.error("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width, self.THUMB_SMALLEST)) opts.thumb_width = "1.0" # eval prefix_rules if passed from command line if type(opts.prefix_rules) is not tuple: try: opts.prefix_rules = eval(opts.prefix_rules) except: log.error("malformed --prefix-rules: %s" % opts.prefix_rules) raise for rule in opts.prefix_rules: if len(rule) != 4: log.error("incorrect number of args for --prefix-rules: %s" % repr(rule)) # eval exclusion_rules if passed from command line if type(opts.exclusion_rules) is not tuple: try: opts.exclusion_rules = eval(opts.exclusion_rules) except: log.error("malformed --exclusion-rules: %s" % opts.exclusion_rules) raise for rule in opts.exclusion_rules: if len(rule) != 3: log.error("incorrect number of args for --exclusion-rules: %s" % repr(rule)) # Display opts keys = sorted(opts_dict.keys()) build_log.append(" opts:") for key in keys: if key in ['catalog_title', 'author_clip', 'connected_kindle', 'creator', 'cross_reference_authors', 'description_clip', 'exclude_book_marker', 'exclude_genre', 'exclude_tags', 'exclusion_rules', 'fmt', 'genre_source_field', 'header_note_source_field', 'merge_comments_rule', 'output_profile', 'prefix_rules', 'preset', 'read_book_marker', 'search_text', 'sort_by', 'sort_descriptions_by_author', 'sync', 'thumb_width', 'use_existing_cover', 'wishlist_tag']: build_log.append(" %s: %s" % (key, repr(opts_dict[key]))) if opts.verbose: log('\n'.join(line for line in build_log)) # Capture start_time opts.start_time = time.time() self.opts = opts if opts.verbose: log.info(" Begin catalog source generation (%s)" % str(datetime.timedelta(seconds=int(time.time() - opts.start_time)))) # Launch the Catalog builder catalog = CatalogBuilder(db, opts, self, report_progress=notification) try: catalog.build_sources() if opts.verbose: log.info(" Completed catalog source generation (%s)\n" % str(datetime.timedelta(seconds=int(time.time() - opts.start_time)))) except (AuthorSortMismatchException, EmptyCatalogException) as e: log.error(" *** Terminated catalog generation: %s ***" % e) except: log.error(" unhandled exception in catalog generator") raise else: recommendations = [] recommendations.append(('remove_fake_margins', False, OptionRecommendation.HIGH)) recommendations.append(('comments', '', OptionRecommendation.HIGH)) """ >>> Use to debug generated catalog code before pipeline conversion <<< """ GENERATE_DEBUG_EPUB = False if GENERATE_DEBUG_EPUB: catalog_debug_path = os.path.join(os.path.expanduser('~'), 'Desktop', 'Catalog debug') setattr(opts, 'debug_pipeline', os.path.expanduser(catalog_debug_path)) dp = getattr(opts, 'debug_pipeline', None) if dp is not None: recommendations.append(('debug_pipeline', dp, OptionRecommendation.HIGH)) if opts.output_profile and opts.output_profile.startswith("kindle"): recommendations.append(('output_profile', opts.output_profile, OptionRecommendation.HIGH)) recommendations.append(('book_producer', opts.output_profile, OptionRecommendation.HIGH)) if opts.fmt == 'mobi': recommendations.append(('no_inline_toc', True, OptionRecommendation.HIGH)) recommendations.append(('verbose', 2, OptionRecommendation.HIGH)) # Use existing cover or generate new cover cpath = None existing_cover = False try: search_text = 'title:"%s" author:%s' % ( opts.catalog_title.replace('"', '\\"'), 'calibre') matches = db.search(search_text, return_matches=True, sort_results=False) if matches: cpath = db.cover(matches[0], index_is_id=True, as_path=True) if cpath and os.path.exists(cpath): existing_cover = True except: pass if self.opts.use_existing_cover and not existing_cover: log.warning("no existing catalog cover found") if self.opts.use_existing_cover and existing_cover: recommendations.append(('cover', cpath, OptionRecommendation.HIGH)) log.info("using existing catalog cover") else: from calibre.ebooks.covers import calibre_cover2 log.info("replacing catalog cover") new_cover_path = PersistentTemporaryFile(suffix='.jpg') new_cover = calibre_cover2(opts.catalog_title, 'calibre') new_cover_path.write(new_cover) new_cover_path.close() recommendations.append(('cover', new_cover_path.name, OptionRecommendation.HIGH)) # Run ebook-convert from calibre.ebooks.conversion.plumber import Plumber plumber = Plumber(os.path.join(catalog.catalog_path, opts.basename + '.opf'), path_to_output, log, report_progress=notification, abort_after_input_dump=False) plumber.merge_ui_recommendations(recommendations) plumber.run() try: os.remove(cpath) except: pass if GENERATE_DEBUG_EPUB: from calibre.ebooks.epub import initialize_container from calibre.ebooks.tweak import zip_rebuilder from calibre.utils.zipfile import ZipFile input_path = os.path.join(catalog_debug_path, 'input') epub_shell = os.path.join(catalog_debug_path, 'epub_shell.zip') initialize_container(epub_shell, opf_name='content.opf') with ZipFile(epub_shell, 'r') as zf: zf.extractall(path=input_path) os.remove(epub_shell) zip_rebuilder(input_path, os.path.join(catalog_debug_path, 'input.epub')) if opts.verbose: log.info(" Catalog creation complete (%s)\n" % str(datetime.timedelta(seconds=int(time.time() - opts.start_time)))) # returns to gui2.actions.catalog:catalog_generated() return catalog.error
def convert_single_ebook(parent, db, book_ids, auto_conversion=False, # {{{ out_format=None, show_no_format_warning=True): changed = False jobs = [] bad = [] total = len(book_ids) if total == 0: return None, None, None for i, book_id in enumerate(book_ids): temp_files = [] try: d = SingleConfig(parent, db, book_id, None, out_format) if auto_conversion: d.accept() result = QDialog.Accepted else: result = d.exec_() if result == QDialog.Accepted: #if not convert_existing(parent, db, [book_id], d.output_format): # continue mi = db.get_metadata(book_id, True) in_file = PersistentTemporaryFile('.'+d.input_format) with in_file: input_fmt = db.original_fmt(book_id, d.input_format).lower() same_fmt = input_fmt == d.output_format.lower() db.copy_format_to(book_id, input_fmt, in_file, index_is_id=True) out_file = PersistentTemporaryFile('.' + d.output_format) out_file.write(d.output_format) out_file.close() temp_files = [in_file] try: dtitle = unicode(mi.title) except: dtitle = repr(mi.title) desc = _('Convert book %(num)d of %(total)d (%(title)s)') % \ {'num':i + 1, 'total':total, 'title':dtitle} recs = cPickle.loads(d.recommendations) if d.opf_file is not None: recs.append(('read_metadata_from_opf', d.opf_file.name, OptionRecommendation.HIGH)) temp_files.append(d.opf_file) if d.cover_file is not None: recs.append(('cover', d.cover_file.name, OptionRecommendation.HIGH)) temp_files.append(d.cover_file) args = [in_file.name, out_file.name, recs] temp_files.append(out_file) func = 'gui_convert_override' parts = [] if not auto_conversion and d.manually_fine_tune_toc: parts.append('manually_fine_tune_toc') if same_fmt: parts.append('same_fmt') if parts: func += ':%s'%(';'.join(parts)) jobs.append((func, args, desc, d.output_format.upper(), book_id, temp_files)) changed = True d.break_cycles() except NoSupportedInputFormats as nsif: bad.append((book_id, nsif.available_formats)) if bad and show_no_format_warning: if len(bad) == 1 and not bad[0][1]: title = db.title(bad[0][0], True) warning_dialog(parent, _('Could not convert'), '<p>'+ _('Could not convert <b>%s</b> as it has no ebook files. If you ' 'think it should have files, but calibre is not finding ' 'them, that is most likely because you moved the book\'s ' 'files around outside of calibre. You will need to find those files ' 'and re-add them to calibre.')%title, show=True) else: res = [] for id, available_formats in bad: title = db.title(id, True) if available_formats: msg = _('No supported formats (Available formats: %s)')%( ', '.join(available_formats)) else: msg = _('This book has no actual ebook files') res.append('%s - %s'%(title, msg)) msg = '%s' % '\n'.join(res) warning_dialog(parent, _('Could not convert some books'), _('Could not convert %(num)d of %(tot)d books, because no supported source' ' formats were found.') % dict(num=len(res), tot=total), msg).exec_() return jobs, changed, bad
def do_book(self): if self.i >= len(self.book_ids): return self.do_queue() book_id = self.book_ids[self.i] self.i += 1 temp_files = [] try: input_format = get_input_format_for_book(self.db, book_id, None)[0] input_fmt = self.db.original_fmt(book_id, input_format).lower() same_fmt = input_fmt == self.output_format.lower() mi, opf_file = create_opf_file(self.db, book_id) in_file = PersistentTemporaryFile('.'+input_format) with in_file: self.db.copy_format_to(book_id, input_fmt, in_file, index_is_id=True) out_file = PersistentTemporaryFile('.' + self.output_format) out_file.write(self.output_format) out_file.close() temp_files = [in_file] combined_recs = GuiRecommendations() default_recs = bulk_defaults_for_input_format(input_format) for key in default_recs: combined_recs[key] = default_recs[key] if self.use_saved_single_settings: specific_recs = load_specifics(self.db, book_id) for key in specific_recs: combined_recs[key] = specific_recs[key] for item in self.user_recs: combined_recs[item[0]] = item[1] save_specifics(self.db, book_id, combined_recs) lrecs = list(combined_recs.to_recommendations()) cover_file = create_cover_file(self.db, book_id) if opf_file is not None: lrecs.append(('read_metadata_from_opf', opf_file.name, OptionRecommendation.HIGH)) temp_files.append(opf_file) if cover_file is not None: lrecs.append(('cover', cover_file.name, OptionRecommendation.HIGH)) temp_files.append(cover_file) for x in list(lrecs): if x[0] == 'debug_pipeline': lrecs.remove(x) try: dtitle = unicode(mi.title) except: dtitle = repr(mi.title) if len(dtitle) > 50: dtitle = dtitle[:50].rpartition(' ')[0]+'...' self.setLabelText(_('Queueing ')+dtitle) desc = _('Convert book %(num)d of %(tot)d (%(title)s)') % dict( num=self.i, tot=len(self.book_ids), title=dtitle) args = [in_file.name, out_file.name, lrecs] temp_files.append(out_file) func = 'gui_convert_override' if same_fmt: func += ':same_fmt' self.jobs.append((func, args, desc, self.output_format.upper(), book_id, temp_files)) self.changed = True self.setValue(self.i) except NoSupportedInputFormats: self.bad.append(book_id) QTimer.singleShot(0, self.do_book)
def convert_single_ebook(parent, db, book_ids, auto_conversion=False, # {{{ out_format=None, show_no_format_warning=True): changed = False jobs = [] bad = [] total = len(book_ids) if total == 0: return None, None, None for i, book_id in enumerate(book_ids): temp_files = [] try: d = SingleConfig(parent, db, book_id, None, out_format) if auto_conversion: d.accept() result = QDialog.Accepted else: result = d.exec_() if result == QDialog.Accepted: #if not convert_existing(parent, db, [book_id], d.output_format): # continue mi = db.get_metadata(book_id, True) in_file = PersistentTemporaryFile('.'+d.input_format) with in_file: input_fmt = db.original_fmt(book_id, d.input_format).lower() same_fmt = input_fmt == d.output_format.lower() db.copy_format_to(book_id, input_fmt, in_file, index_is_id=True) out_file = PersistentTemporaryFile('.' + d.output_format) out_file.write(d.output_format) out_file.close() temp_files = [in_file] try: dtitle = unicode(mi.title) except: dtitle = repr(mi.title) desc = _('Convert book %(num)d of %(total)d (%(title)s)') % \ {'num':i + 1, 'total':total, 'title':dtitle} recs = cPickle.loads(d.recommendations) if d.opf_file is not None: recs.append(('read_metadata_from_opf', d.opf_file.name, OptionRecommendation.HIGH)) temp_files.append(d.opf_file) if d.cover_file is not None: recs.append(('cover', d.cover_file.name, OptionRecommendation.HIGH)) temp_files.append(d.cover_file) args = [in_file.name, out_file.name, recs] temp_files.append(out_file) func = 'gui_convert_override' if same_fmt: func += ':same_fmt' jobs.append((func, args, desc, d.output_format.upper(), book_id, temp_files)) changed = True d.break_cycles() except NoSupportedInputFormats: bad.append(book_id) if bad and show_no_format_warning: res = [] for id in bad: title = db.title(id, True) res.append('%s'%title) msg = '%s' % '\n'.join(res) warning_dialog(parent, _('Could not convert some books'), _('Could not convert %(num)d of %(tot)d books, because no suitable source' ' format was found.') % dict(num=len(res), tot=total), msg).exec_() return jobs, changed, bad
def modify_epub( container, # type: EpubContainer filename, # type: str metadata=None, # type: Optional[Metadata] opts={}, # type: Dict[str, Union[str, bool]] ): # type: (...) -> None """Modify the ePub file to make it KePub-compliant.""" _modify_start = time.time() # Search for the ePub cover # TODO: Refactor out cover detection logic so it can be directly used in # metadata/writer.py found_cover = False # type: bool opf = container.opf # type: _Element cover_meta_node_list = opf.xpath( './opf:metadata/opf:meta[@name="cover"]', namespaces=OPF_NAMESPACES) # List[_Element] if len(cover_meta_node_list) > 0: cover_meta_node = cover_meta_node_list[0] # type: _Element cover_id = cover_meta_node.attrib.get("content", None) log.debug("Found meta node with name=cover") if cover_id: log.info("Found cover image ID '{0}'".format(cover_id)) cover_node_list = opf.xpath( './opf:manifest/opf:item[@id="{0}"]'.format(cover_id), namespaces=OPF_NAMESPACES, ) # type: List[_Element] if len(cover_node_list) > 0: cover_node = cover_node_list[0] # type: _Element log.debug("Found an item node with cover ID") if cover_node.attrib.get("properties", "") != "cover-image": log.info("Setting cover-image property") cover_node.set("properties", "cover-image") container.dirty(container.opf_name) else: log.warning("Item node is already set as cover-image") found_cover = True # It's possible that the cover image can't be detected this way. Try # looking for the cover image ID in the OPF manifest. if not found_cover: log.debug("Looking for cover image in OPF manifest") node_list = opf.xpath( "./opf:manifest/opf:item[(translate(@id, " + "'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')" + '="cover" or starts-with(translate(@id, ' + "'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')" + ', "cover")) and starts-with(@media-type, "image")]', namespaces=OPF_NAMESPACES, ) # type: List[_Element] if len(node_list) > 0: log.info("Found {0:d} nodes, assuming the first is the " "right node".format(len(node_list))) node = node_list[0] # type: _Element if node.attrib.get("properties", "") != "cover-image": log.info("Setting cover-image property") node.set("properties", "cover-image") container.dirty(container.opf_name) else: log.warning("Item node is already set as cover-image") found_cover = True # Hyphenate files? if opts.get("no-hyphens", False): nohyphen_css = PersistentTemporaryFile( suffix="_nohyphen", prefix="kepub_") # type: PersistentTemporaryFile nohyphen_css.write(get_resources("css/no-hyphens.css")) # noqa: F821 nohyphen_css.close() css_path = os.path.basename( container.copy_file_to_container( nohyphen_css.name, name="kte-css/no-hyphens.css")) # type: str container.add_content_file_reference("kte-css/{0}".format(css_path)) os.unlink(nohyphen_css.name) elif opts.get("hyphenate", False) and opts.get("hyphen_min_chars", 6) > 0: if metadata and metadata.language == NULL_VALUES["language"]: log.warning( "Hyphenation is enabled but not overriding content file " "language. Hyphenation may use the wrong dictionary.") hyphen_css = PersistentTemporaryFile( suffix="_hyphenate", prefix="kepub_") # type: PersistentTemporaryFile css_template = get_resources( "css/hyphenation.css.tmpl").decode() # noqa: F821 hyphen_limit_lines = opts.get("hyphen_limit_lines", 2) if hyphen_limit_lines == 0: hyphen_limit_lines = "no-limit" hyphen_css.write( css_template.format( hyphen_min_chars=opts.get("hyphen_min_chars"), hyphen_min_chars_before=opts.get("hyphen_min_chars_before", 3), hyphen_min_chars_after=opts.get("hyphen_min_chars_after", 3), hyphen_limit_lines=hyphen_limit_lines, ).encode()) hyphen_css.close() css_path = os.path.basename( container.copy_file_to_container( hyphen_css.name, name="kte-css/hyphenation.css")) # type: str container.add_content_file_reference("kte-css/{0}".format(css_path)) os.unlink(hyphen_css.name) # Now smarten punctuation if opts.get("smarten_punctuation", False): container.smarten_punctuation() if opts.get("extended_kepub_features", True): if metadata is not None: log.info("Adding extended Kobo features to {0} by {1}".format( metadata.title, " and ".join(metadata.authors))) # Add the Kobo span and div tags container.convert() # Check to see if there's already a kobo*.js in the ePub skip_js = False # type: str for name in container.name_path_map: if KOBO_JS_RE.match(name): skip_js = True break if not skip_js: if os.path.isfile(REFERENCE_KEPUB): reference_container = EpubContainer(REFERENCE_KEPUB, log) for name in reference_container.name_path_map: if KOBO_JS_RE.match(name): jsname = container.copy_file_to_container( os.path.join(reference_container.root, name), name="kobo.js") container.add_content_file_reference(jsname) break # Add the Kobo style hacks stylehacks_css = PersistentTemporaryFile(suffix="_stylehacks", prefix="kepub_") stylehacks_css.write( get_resources("css/style-hacks.css")) # noqa: F821 stylehacks_css.close() css_path = os.path.basename( container.copy_file_to_container(stylehacks_css.name, name="kte-css/stylehacks.css")) container.add_content_file_reference("kte-css/{0}".format(css_path)) os.unlink(filename) container.commit(filename) _modify_time = time.time() - _modify_start log.info("modify_epub took {0:f} seconds".format(_modify_time))
if cpath and os.path.exists(cpath): existing_cover = True except: pass if self.opts.use_existing_cover and not existing_cover: log.warning("no existing catalog cover found") if self.opts.use_existing_cover and existing_cover: recommendations.append(('cover', cpath, OptionRecommendation.HIGH)) log.info("using existing catalog cover") else: log.info("replacing catalog cover") new_cover_path = PersistentTemporaryFile(suffix='.jpg') new_cover = calibre_cover(opts.catalog_title.replace('"', '\\"'), 'calibre') new_cover_path.write(new_cover) new_cover_path.close() recommendations.append(('cover', new_cover_path.name, OptionRecommendation.HIGH)) # Run ebook-convert from calibre.ebooks.conversion.plumber import Plumber plumber = Plumber(os.path.join(catalog.catalog_path, opts.basename + '.opf'), path_to_output, log, report_progress=notification, abort_after_input_dump=False) plumber.merge_ui_recommendations(recommendations) plumber.run() try: os.remove(cpath) except: pass