More work on in-server conversion

This commit is contained in:
Kovid Goyal 2018-06-27 09:39:03 +05:30
parent 04f82a64e0
commit 4dd9a58262
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 107 additions and 25 deletions

View File

@ -320,3 +320,25 @@ OPTIONS = {
}, },
} }
OPTIONS['output']['txtz'] = OPTIONS['output']['txt'] OPTIONS['output']['txtz'] = OPTIONS['output']['txt']
def options_for_input_fmt(fmt):
from calibre.customize.ui import plugin_for_input_format
fmt = fmt.lower()
plugin = plugin_for_input_format(fmt)
if plugin is None:
return None, ()
full_name = plugin.name.lower().replace(' ', '_')
name = full_name.rpartition('_')[0]
return full_name, OPTIONS['input'].get(name, ())
def options_for_output_fmt(fmt):
from calibre.customize.ui import plugin_for_output_format
fmt = fmt.lower()
plugin = plugin_for_output_format(fmt)
if plugin is None:
return None, ()
full_name = plugin.name.lower().replace(' ', '_')
name = full_name.rpartition('_')[0]
return full_name, OPTIONS['output'].get(name, ())

View File

@ -137,23 +137,35 @@ def conversion_status(ctx, rd, job_id):
def get_conversion_options(input_fmt, output_fmt, book_id, db): def get_conversion_options(input_fmt, output_fmt, book_id, db):
from calibre.ebooks.conversion.plumber import create_dummy_plumber from calibre.ebooks.conversion.plumber import create_dummy_plumber
from calibre.ebooks.conversion.config import load_specifics, load_defaults, OPTIONS from calibre.ebooks.conversion.config import (
load_specifics, load_defaults, OPTIONS, options_for_input_fmt, options_for_output_fmt)
from calibre.customize.conversion import OptionRecommendation from calibre.customize.conversion import OptionRecommendation
plumber = create_dummy_plumber(input_fmt, output_fmt) plumber = create_dummy_plumber(input_fmt, output_fmt)
specifics = load_specifics(db, book_id) specifics = load_specifics(db, book_id)
ans = {'options': {}, 'disabled': set()} ans = {'options': {}, 'disabled': set()}
for group_name, option_names in OPTIONS['pipe'].iteritems():
if group_name == 'debug': def merge_group(group_name, option_names):
continue if not group_name or group_name == 'debug':
return
defs = load_defaults(group_name) defs = load_defaults(group_name)
defs.merge_recommendations(plumber.get_option, OptionRecommendation.LOW, option_names) defs.merge_recommendations(plumber.get_option_by_name, OptionRecommendation.LOW, option_names)
specifics.merge_recommendations(plumber.get_option, OptionRecommendation.HIGH, option_names, only_existing=True) specifics.merge_recommendations(plumber.get_option_by_name, OptionRecommendation.HIGH, option_names, only_existing=True)
for k in defs: for k in defs:
if k in specifics: if k in specifics:
defs[k] = specifics[k] defs[k] = specifics[k]
defs = defs.as_dict() defs = defs.as_dict()
ans['options'].update(defs['options']) ans['options'].update(defs['options'])
ans['disabled'] |= defs['disabled'] ans['disabled'] |= set(defs['disabled'])
for group_name, option_names in OPTIONS['pipe'].iteritems():
merge_group(group_name, option_names)
group_name, option_names = options_for_input_fmt(input_fmt)
merge_group(group_name, option_names)
group_name, option_names = options_for_output_fmt(output_fmt)
merge_group(group_name, option_names)
ans['disabled'] = tuple(ans['disabled'])
return ans return ans

View File

@ -11,11 +11,14 @@ from book_list.library_data import load_status, url_books_query
from book_list.router import back, report_a_load_failure from book_list.router import back, report_a_load_failure
from book_list.top_bar import create_top_bar, set_title from book_list.top_bar import create_top_bar, set_title
from book_list.ui import set_panel_handler from book_list.ui import set_panel_handler
from dom import add_extra_css, build_rule, clear, ensure_id from dom import add_extra_css, build_rule
from utils import conditional_timeout, parse_url_params from utils import conditional_timeout, parse_url_params
from widgets import create_button from widgets import create_button
CLASS_NAME = 'convert-book-panel' CLASS_NAME = 'convert-book-panel'
current_state = 'initializing'
overall_container_id = None
initializers = {}
add_extra_css(def(): add_extra_css(def():
sel = '.' + CLASS_NAME + ' ' sel = '.' + CLASS_NAME + ' '
@ -28,12 +31,18 @@ conversion_data = None
conversion_data_load_status = {'loading':True, 'ok':False, 'error_html':None, 'current_fetch':None} conversion_data_load_status = {'loading':True, 'ok':False, 'error_html':None, 'current_fetch':None}
def container_for_current_state():
ans = document.getElementById(overall_container_id)
if ans:
return ans.querySelector(f'[data-state="{current_state}"]')
def on_conversion_started(end_type, xhr, ev): def on_conversion_started(end_type, xhr, ev):
pass pass
def get_conversion_options(container): def get_conversion_options(container):
return conversion_data.conversion_options return conversion_data.conversion_options.options
def start_conversion(): def start_conversion():
@ -48,17 +57,13 @@ def start_conversion():
ajax_send(f'/conversion/start/{conversion_data.book_id}', data, on_conversion_started, query=query) ajax_send(f'/conversion/start/{conversion_data.book_id}', data, on_conversion_started, query=query)
def create_convert_book(container): def create_configuring_markup():
conversion_data.container_id = ensure_id(container) ans = E.div(class_='top')
set_title(container.parentNode, _('Convert: {}').format(conversion_data.title))
container.appendChild(E.div(class_='top'))
def generate_choice(name): def generate_choice(name):
formats = conversion_data[name]
ans = E.select(name=name) ans = E.select(name=name)
for fmt in formats:
ans.appendChild(E.option(fmt, value=fmt))
return ans return ans
tcell = 'display: table-cell; padding-top: 1em; padding-left: 1em' tcell = 'display: table-cell; padding-top: 1em; padding-left: 1em'
start_conv = E.div( start_conv = E.div(
@ -79,7 +84,16 @@ def create_convert_book(container):
create_button(_('Start conversion'), action=start_conversion) create_button(_('Start conversion'), action=start_conversion)
) )
) )
container.lastChild.appendChild(start_conv) ans.appendChild(start_conv)
def initialize(container):
for name in 'input_formats', 'output_formats':
sel = container.querySelector(f'select[name="{name}"]')
formats = conversion_data[name]
for fmt in formats:
sel.appendChild(E.option(fmt, value=fmt))
return ans, initialize
# Initialization {{{ # Initialization {{{
@ -121,28 +135,62 @@ def on_close(container_id):
def check_for_data_loaded(): def check_for_data_loaded():
nonlocal current_state
container = this container = this
if load_status.loading or conversion_data_load_status.loading: if load_status.loading or conversion_data_load_status.loading:
conditional_timeout(container.id, 5, check_for_data_loaded) conditional_timeout(container.id, 5, check_for_data_loaded)
return return
container = container.lastChild container = container.lastChild
clear(container)
if not load_status.ok: if not load_status.ok:
report_load_failure(container) current_state = 'load-failure'
return report_load_failure(container_for_current_state())
if not conversion_data_load_status.ok: elif not conversion_data_load_status.ok:
current_state = 'load-failure'
report_a_load_failure( report_a_load_failure(
container, _('Failed to load conversion data from calibre, with error:'), container_for_current_state(),
_('Failed to load conversion data from calibre, with error:'),
conversion_data_load_status.error_html) conversion_data_load_status.error_html)
return else:
create_convert_book(container) set_title(container.parentNode, _('Convert: {}').format(conversion_data.title))
current_state = 'configuring'
apply_state_to_markup()
def create_markup(container):
container.appendChild(E.div(
data_state='initializing', style='margin: 1ex 1em',
E.div(_('Loading conversion data, please wait...'))
))
container.appendChild(E.div(
data_state='load-failure', style='margin: 1ex 1em',
))
ccm, init = create_configuring_markup()
ccm.dataset.state = 'configuring'
container.appendChild(ccm)
initializers[ccm.dataset.state] = init
def apply_state_to_markup():
container = document.getElementById(overall_container_id)
if container:
for node in container.lastChild.childNodes:
if node.dataset.state is current_state:
node.style.display = 'block'
if initializers[current_state]:
initializers[current_state](node)
else:
node.style.display = 'none'
def init(container_id): def init(container_id):
nonlocal overall_container_id
container = document.getElementById(container_id) container = document.getElementById(container_id)
overall_container_id = container_id
create_top_bar(container, title=_('Convert book'), action=on_close.bind(None, container_id), icon='close') create_top_bar(container, title=_('Convert book'), action=on_close.bind(None, container_id), icon='close')
container.appendChild(E.div(class_=CLASS_NAME)) container.appendChild(E.div(class_=CLASS_NAME))
container.lastChild.appendChild(E.div(_('Loading conversion data, please wait...'), style='margin: 1ex 1em')) create_markup(container.lastChild)
apply_state_to_markup()
conditional_timeout(container_id, 5, check_for_data_loaded) conditional_timeout(container_id, 5, check_for_data_loaded)
q = parse_url_params() q = parse_url_params()
fetch_conversion_data(q.book_id) fetch_conversion_data(q.book_id)