%PDF- %PDF-
| Direktori : /lib/calibre/calibre/gui2/ |
| Current File : //lib/calibre/calibre/gui2/tools.py |
#!/usr/bin/env python3
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
'''
Logic for setting up conversion jobs
'''
import os
from qt.core import QDialog, QProgressDialog, QTimer
from calibre.ptempfile import PersistentTemporaryFile
from calibre.gui2 import warning_dialog, question_dialog
from calibre.gui2.convert.single import Config as SingleConfig
from calibre.gui2.convert.bulk import BulkConfig
from calibre.gui2.convert.metadata import create_opf_file, create_cover_file
from calibre.customize.conversion import OptionRecommendation
from calibre.utils.config import prefs
from calibre.ebooks.conversion.config import (
GuiRecommendations, load_defaults, load_specifics, save_specifics,
get_input_format_for_book, NoSupportedInputFormats)
from calibre.gui2.convert import bulk_defaults_for_input_format
from polyglot.builtins import as_bytes
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.DialogCode.Accepted
else:
result = d.exec()
if result == QDialog.DialogCode.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(as_bytes(d.output_format))
out_file.close()
temp_files = [in_file]
try:
dtitle = str(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 = 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'),
(
_('Could not convert the book because no supported source format was found')
if len(res) == 1 else
_('Could not convert {num} of {tot} books, because no supported source formats were found.')
).format(num=len(res), tot=total),
msg).exec()
return jobs, changed, bad
# }}}
# Bulk convert {{{
def convert_bulk_ebook(parent, queue, db, book_ids, out_format=None, args=[]):
total = len(book_ids)
if total == 0:
return None, None, None
has_saved_settings = db.has_conversion_options(book_ids)
d = BulkConfig(parent, db, out_format,
has_saved_settings=has_saved_settings, book_ids=book_ids)
if d.exec() != QDialog.DialogCode.Accepted:
return None
output_format = d.output_format
user_recs = d.recommendations
book_ids = convert_existing(parent, db, book_ids, output_format)
use_saved_single_settings = d.opt_individual_saved_settings.isChecked()
return QueueBulk(parent, book_ids, output_format, queue, db, user_recs,
args, use_saved_single_settings=use_saved_single_settings)
class QueueBulk(QProgressDialog):
def __init__(self, parent, book_ids, output_format, queue, db, user_recs,
args, use_saved_single_settings=True):
QProgressDialog.__init__(self, '',
None, 0, len(book_ids), parent)
self.setWindowTitle(_('Queueing books for bulk conversion'))
self.book_ids, self.output_format, self.queue, self.db, self.args, self.user_recs = \
book_ids, output_format, queue, db, args, user_recs
self.parent = parent
self.use_saved_single_settings = use_saved_single_settings
self.i, self.bad, self.jobs, self.changed = 0, [], [], False
QTimer.singleShot(0, self.do_book)
self.exec()
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(as_bytes(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 = str(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 do_queue(self):
self.hide()
if self.bad != []:
res = []
for id in self.bad:
title = self.db.title(id, True)
res.append('%s'%title)
msg = '%s' % '\n'.join(res)
warning_dialog(self.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=len(self.book_ids)),
msg).exec()
self.parent = None
self.jobs.reverse()
self.queue(self.jobs, self.changed, self.bad, *self.args)
# }}}
def fetch_scheduled_recipe(arg): # {{{
fmt = prefs['output_format'].lower()
# Never use AZW3 for periodicals...
if fmt == 'azw3':
fmt = 'mobi'
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))
for edge in ('left', 'top', 'bottom', 'right'):
edge = 'margin_' + edge
if edge in ps:
recs.append((edge, ps[edge], 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))
if fmt == 'pdf':
pdf = load_defaults('pdf_output')
from calibre.customize.ui import plugin_for_output_format
p = plugin_for_output_format('pdf')
for opt in p.options:
recs.append((opt.option.name, pdf.get(opt.option.name, opt.recommended_value), OptionRecommendation.HIGH))
args = [arg['urn'], pt.name, recs]
if arg['username'] is not None:
recs.append(('username', arg['username'], OptionRecommendation.HIGH))
if arg['password'] is not None:
recs.append(('password', arg['password'], OptionRecommendation.HIGH))
return 'gui_convert_recipe', args, _('Fetch news from %s')%arg['title'], fmt.upper(), [pt]
# }}}
def generate_catalog(parent, dbspec, ids, device_manager, db): # {{{
from calibre.gui2.dialogs.catalog import Catalog
# Build the Catalog dialog in gui2.dialogs.catalog
d = Catalog(parent, dbspec, ids, db)
if d.exec() != QDialog.DialogCode.Accepted:
return None
# Create the output file
out = PersistentTemporaryFile(suffix='_catalog_out.'+d.catalog_format.lower())
# Profile the connected device
# Parallel initialization in calibre.db.cli.cmd_catalog
connected_device = {
'is_device_connected': device_manager.is_device_present,
'kind': device_manager.connected_device_kind,
'name': None,
'save_template': None,
'serial': None,
'storage': None
}
if device_manager.is_device_present:
device = device_manager.device
connected_device['name'] = device.get_gui_name()
try:
storage = []
if device._main_prefix:
storage.append(os.path.join(device._main_prefix, device.EBOOK_DIR_MAIN))
if device._card_a_prefix:
storage.append(os.path.join(device._card_a_prefix, device.EBOOK_DIR_CARD_A))
if device._card_b_prefix:
storage.append(os.path.join(device._card_b_prefix, device.EBOOK_DIR_CARD_B))
connected_device['storage'] = storage
connected_device['serial'] = device.detected_device.serial if \
hasattr(device.detected_device,'serial') else None
connected_device['save_template'] = device.save_template()
except:
pass
# These args are passed inline to gui2.convert.gui_conversion:gui_catalog
args = [
d.catalog_format,
d.catalog_title,
dbspec,
ids,
out.name,
d.catalog_sync,
d.fmt_options,
connected_device
]
out.close()
# This returns to gui2.actions.catalog:generate_catalog()
# Which then calls gui2.convert.gui_conversion:gui_catalog() with the args inline
return 'gui_catalog', args, _('Generate catalog'), out.name, d.catalog_sync, \
d.catalog_title
# }}}
def convert_existing(parent, db, book_ids, output_format): # {{{
already_converted_ids = []
already_converted_titles = []
for book_id in book_ids:
if db.has_format(book_id, output_format, index_is_id=True):
already_converted_ids.append(book_id)
already_converted_titles.append(db.get_metadata(book_id, True).title)
if already_converted_ids:
if not question_dialog(parent, _('Convert existing'),
_('The following books have already been converted to the %s format. '
'Do you wish to reconvert them?') % output_format.upper(),
det_msg='\n'.join(already_converted_titles), skip_dialog_name='confirm_bulk_reconvert'):
book_ids = [x for x in book_ids if x not in already_converted_ids]
return book_ids
# }}}