%PDF- %PDF-
| Direktori : /usr/lib/calibre/calibre/gui2/actions/ |
| Current File : //usr/lib/calibre/calibre/gui2/actions/convert.py |
#!/usr/bin/env python3
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os
from functools import partial
from qt.core import QModelIndex, QTimer
from calibre.gui2 import error_dialog, Dispatcher, gprefs
from calibre.gui2.tools import convert_single_ebook, convert_bulk_ebook
from calibre.utils.config import prefs, tweaks
from calibre.gui2.actions import InterfaceAction
from calibre.customize.ui import plugin_for_input_format
class ConvertAction(InterfaceAction):
name = 'Convert Books'
action_spec = (_('Convert books'), 'convert.png', _('Convert books between different e-book formats'), _('C'))
dont_add_to = frozenset(('context-menu-device',))
action_type = 'current'
action_add_menu = True
accepts_drops = True
def accept_enter_event(self, event, mime_data):
if mime_data.hasFormat("application/calibre+from_library"):
return True
return False
def accept_drag_move_event(self, event, mime_data):
if mime_data.hasFormat("application/calibre+from_library"):
return True
return False
def drop_event(self, event, mime_data):
mime = 'application/calibre+from_library'
if mime_data.hasFormat(mime):
self.dropped_ids = tuple(map(int, mime_data.data(mime).data().split()))
QTimer.singleShot(1, self.do_drop)
return True
return False
def do_drop(self):
book_ids = self.dropped_ids
del self.dropped_ids
self.do_convert(book_ids)
def genesis(self):
m = self.convert_menu = self.qaction.menu()
cm = partial(self.create_menu_action, self.convert_menu)
cm('convert-individual', _('Convert individually'),
icon=self.qaction.icon(), triggered=partial(self.convert_ebook,
False, bulk=False))
cm('convert-bulk', _('Bulk convert'),
triggered=partial(self.convert_ebook, False, bulk=True))
m.addSeparator()
cm('create-catalog',
_('Create a catalog of the books in your calibre library'),
icon='catalog.png', shortcut=False,
triggered=self.gui.iactions['Generate Catalog'].generate_catalog)
self.qaction.triggered.connect(self.convert_ebook)
self.conversion_jobs = {}
def location_selected(self, loc):
enabled = loc == 'library'
self.qaction.setEnabled(enabled)
self.menuless_qaction.setEnabled(enabled)
for action in list(self.convert_menu.actions()):
action.setEnabled(enabled)
def auto_convert(self, book_ids, on_card, format):
previous = self.gui.library_view.currentIndex()
rows = [x.row() for x in
self.gui.library_view.selectionModel().selectedRows()]
jobs, changed, bad = convert_single_ebook(self.gui, self.gui.library_view.model().db, book_ids, True, format)
if jobs == []:
return
self.queue_convert_jobs(jobs, changed, bad, rows, previous,
self.book_auto_converted, extra_job_args=[on_card])
def auto_convert_auto_add(self, book_ids):
previous = self.gui.library_view.currentIndex()
db = self.gui.current_db
needed = set()
of = prefs['output_format'].lower()
for book_id in book_ids:
fmts = db.formats(book_id, index_is_id=True)
fmts = {x.lower() for x in fmts.split(',')} if fmts else set()
if gprefs['auto_convert_same_fmt'] or of not in fmts:
needed.add(book_id)
if needed:
jobs, changed, bad = convert_single_ebook(self.gui,
self.gui.library_view.model().db, needed, True, of,
show_no_format_warning=False)
if not jobs:
return
self.queue_convert_jobs(jobs, changed, bad, list(needed), previous,
self.book_converted, rows_are_ids=True)
def auto_convert_mail(self, to, fmts, delete_from_library, book_ids, format, subject):
previous = self.gui.library_view.currentIndex()
rows = [x.row() for x in
self.gui.library_view.selectionModel().selectedRows()]
jobs, changed, bad = convert_single_ebook(self.gui, self.gui.library_view.model().db, book_ids, True, format)
if jobs == []:
return
self.queue_convert_jobs(jobs, changed, bad, rows, previous,
self.book_auto_converted_mail,
extra_job_args=[delete_from_library, to, fmts, subject])
def auto_convert_multiple_mail(self, book_ids, data, ofmt, delete_from_library):
previous = self.gui.library_view.currentIndex()
rows = [x.row() for x in self.gui.library_view.selectionModel().selectedRows()]
jobs, changed, bad = convert_single_ebook(self.gui, self.gui.library_view.model().db, book_ids, True, ofmt)
if jobs == []:
return
self.queue_convert_jobs(jobs, changed, bad, rows, previous,
self.book_auto_converted_multiple_mail,
extra_job_args=[delete_from_library, data])
def auto_convert_news(self, book_ids, format):
previous = self.gui.library_view.currentIndex()
rows = [x.row() for x in
self.gui.library_view.selectionModel().selectedRows()]
jobs, changed, bad = convert_single_ebook(self.gui, self.gui.library_view.model().db, book_ids, True, format)
if jobs == []:
return
self.queue_convert_jobs(jobs, changed, bad, rows, previous,
self.book_auto_converted_news)
def auto_convert_catalogs(self, book_ids, format):
previous = self.gui.library_view.currentIndex()
rows = [x.row() for x in
self.gui.library_view.selectionModel().selectedRows()]
jobs, changed, bad = convert_single_ebook(self.gui, self.gui.library_view.model().db, book_ids, True, format)
if jobs == []:
return
self.queue_convert_jobs(jobs, changed, bad, rows, previous,
self.book_auto_converted_catalogs)
def get_books_for_conversion(self):
rows = [r.row() for r in
self.gui.library_view.selectionModel().selectedRows()]
if not rows or len(rows) == 0:
d = error_dialog(self.gui, _('Cannot convert'),
_('No books selected'))
d.exec()
return None
return [self.gui.library_view.model().db.id(r) for r in rows]
def convert_ebook(self, checked, bulk=None):
book_ids = self.get_books_for_conversion()
if book_ids is None:
return
self.do_convert(book_ids, bulk=bulk)
def convert_ebooks_to_format(self, book_ids, to_fmt):
from calibre.customize.ui import available_output_formats
to_fmt = to_fmt.upper()
if to_fmt.lower() not in available_output_formats():
return error_dialog(self.gui, _('Cannot convert'), _(
'Conversion to the {} format is not supported').format(to_fmt), show=True)
self.do_convert(book_ids, output_fmt=to_fmt, auto_conversion=True)
def do_convert(self, book_ids, bulk=None, auto_conversion=False, output_fmt=None):
previous = self.gui.library_view.currentIndex()
rows = [x.row() for x in
self.gui.library_view.selectionModel().selectedRows()]
num = 0
if bulk or (bulk is None and len(book_ids) > 1):
self.__bulk_queue = convert_bulk_ebook(self.gui, self.queue_convert_jobs,
self.gui.library_view.model().db, book_ids,
out_format=output_fmt or prefs['output_format'], args=(rows, previous,
self.book_converted))
if self.__bulk_queue is None:
return
num = len(self.__bulk_queue.book_ids)
else:
jobs, changed, bad = convert_single_ebook(self.gui,
self.gui.library_view.model().db, book_ids, out_format=output_fmt or prefs['output_format'], auto_conversion=auto_conversion)
self.queue_convert_jobs(jobs, changed, bad, rows, previous,
self.book_converted)
num = len(jobs)
if num > 0:
self.gui.jobs_pointer.start()
self.gui.status_bar.show_message(ngettext(
'Starting conversion of the book', 'Starting conversion of {} books', num).format(num), 2000)
def queue_convert_jobs(self, jobs, changed, bad, rows, previous,
converted_func, extra_job_args=[], rows_are_ids=False):
for func, args, desc, fmt, id, temp_files in jobs:
func, _, parts = func.partition(':')
parts = {x for x in parts.split(';')}
input_file = args[0]
input_fmt = os.path.splitext(input_file)[1]
core_usage = 1
if input_fmt:
input_fmt = input_fmt[1:]
plugin = plugin_for_input_format(input_fmt)
if plugin is not None:
core_usage = plugin.core_usage
if id not in bad:
job = self.gui.job_manager.run_job(Dispatcher(converted_func),
func, args=args, description=desc,
core_usage=core_usage)
job.conversion_of_same_fmt = 'same_fmt' in parts
job.manually_fine_tune_toc = 'manually_fine_tune_toc' in parts
args = [temp_files, fmt, id]+extra_job_args
self.conversion_jobs[job] = tuple(args)
if changed:
m = self.gui.library_view.model()
if rows_are_ids:
m.refresh_ids(rows)
else:
m.refresh_rows(rows)
current = self.gui.library_view.currentIndex()
self.gui.library_view.model().current_changed(current, previous)
def book_auto_converted(self, job):
temp_files, fmt, book_id, on_card = self.conversion_jobs[job]
self.book_converted(job)
self.gui.sync_to_device(on_card, False, specific_format=fmt, send_ids=[book_id], do_auto_convert=False)
def book_auto_converted_mail(self, job):
temp_files, fmt, book_id, delete_from_library, to, fmts, subject = self.conversion_jobs[job]
self.book_converted(job)
self.gui.send_by_mail(to, fmts, delete_from_library, subject=subject,
specific_format=fmt, send_ids=[book_id], do_auto_convert=False)
def book_auto_converted_multiple_mail(self, job):
temp_files, fmt, book_id, delete_from_library, data = self.conversion_jobs[job]
self.book_converted(job)
for to, subject in data:
self.gui.send_by_mail(to, (fmt,), delete_from_library, subject=subject,
specific_format=fmt, send_ids=[book_id], do_auto_convert=False)
def book_auto_converted_news(self, job):
temp_files, fmt, book_id = self.conversion_jobs[job]
self.book_converted(job)
self.gui.sync_news(send_ids=[book_id], do_auto_convert=False)
def book_auto_converted_catalogs(self, job):
temp_files, fmt, book_id = self.conversion_jobs[job]
self.book_converted(job)
self.gui.sync_catalogs(send_ids=[book_id], do_auto_convert=False)
def book_converted(self, job):
temp_files, fmt, book_id = self.conversion_jobs.pop(job)[:3]
try:
if job.failed:
self.gui.job_exception(job)
return
db = self.gui.current_db
if not db.new_api.has_id(book_id):
return error_dialog(self.gui, _('Book deleted'), _(
'The book you were trying to convert has been deleted from the calibre library.'), show=True)
same_fmt = getattr(job, 'conversion_of_same_fmt', False)
manually_fine_tune_toc = getattr(job, 'manually_fine_tune_toc', False)
fmtf = temp_files[-1].name
if os.stat(fmtf).st_size < 1:
raise Exception(_('Empty output file, '
'probably the conversion process crashed'))
if same_fmt and tweaks['save_original_format']:
db.save_original_format(book_id, fmt, notify=False)
with open(temp_files[-1].name, 'rb') as data:
db.add_format(book_id, fmt, data, index_is_id=True)
self.gui.book_converted.emit(book_id, fmt)
self.gui.status_bar.show_message(job.description + ' ' +
_('completed'), 2000)
finally:
for f in temp_files:
try:
if os.path.exists(f.name):
os.remove(f.name)
except:
pass
self.gui.tags_view.recount()
if self.gui.current_view() is self.gui.library_view:
lv = self.gui.library_view
lv.model().refresh_ids((book_id,))
current = lv.currentIndex()
if current.isValid():
lv.model().current_changed(current, QModelIndex())
if manually_fine_tune_toc:
self.gui.iactions['Edit ToC'].do_one(book_id, fmt.upper())