More work on server book conversion

This commit is contained in:
Kovid Goyal 2018-06-21 11:47:37 +05:30
parent 27c79db06a
commit 3695c35229
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 190 additions and 21 deletions

View File

@ -45,6 +45,20 @@ def load_defaults(name):
return r
def load_all_defaults():
ans = {}
for x in os.listdir(config_dir):
if x.endswith('.py'):
path = os.path.join(config_dir, x)
with ExclusiveFile(path) as f:
raw = f.read()
r = GuiRecommendations()
if raw:
r.deserialize(raw)
ans[os.path.splitext(x)[0]] = r.copy()
return ans
def save_specifics(db, book_id, recs):
raw = recs.serialize()
db.set_conversion_options(book_id, 'PIPE', raw)
@ -146,14 +160,11 @@ def get_preferred_input_format_for_book(db, book_id):
def sort_formats_by_preference(formats, prefs):
uprefs = [x.upper() for x in prefs]
uprefs = {x.upper():i for i, x in enumerate(prefs)}
def key(x):
try:
return uprefs.index(x.upper())
except ValueError:
pass
return len(prefs)
return uprefs.get(x.upper(), len(prefs))
return sorted(formats, key=key)
@ -188,5 +199,16 @@ def get_output_formats(preferred_output_format):
fmts.append(pfo)
else:
fmts = list(sorted(all_formats,
key=lambda x:{'EPUB':'!A', 'MOBI':'!B'}.get(x.upper(), x)))
key=lambda x:{'EPUB':'!A', 'AZW3':'!B', 'MOBI':'!C'}.get(x.upper(), x)))
return fmts
def get_sorted_output_formats():
preferred_output_format = prefs['output_format'].upper()
fmts = get_output_formats(preferred_output_format)
try:
fmts.remove(preferred_output_format)
except Exception:
pass
fmts.insert(0, preferred_output_format)
return fmts

View File

@ -0,0 +1,44 @@
#!/usr/bin/env python2
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
from __future__ import absolute_import, division, print_function, unicode_literals
from calibre.srv.errors import BookNotFound
from calibre.srv.routes import endpoint, json
from calibre.srv.utils import get_library_data
def conversion_defaults():
from calibre.ebooks.conversion.config import load_all_defaults
ans = getattr(conversion_defaults, 'ans', None)
if ans is None:
ans = conversion_defaults.ans = load_all_defaults()
return ans
@endpoint('/conversion/book-data/{book_id}', postprocess=json, types={'book_id': int})
def conversion_data(ctx, rd, book_id):
from calibre.ebooks.conversion.config import (
NoSupportedInputFormats, get_input_format_for_book,
get_sorted_output_formats, load_specifics)
db = get_library_data(ctx, rd)[0]
if not ctx.has_id(rd, db, book_id):
raise BookNotFound(book_id, db)
try:
input_format, input_formats = get_input_format_for_book(db, book_id)
except NoSupportedInputFormats:
input_formats = []
else:
if input_format in input_formats:
input_formats.remove(input_format)
input_formats.insert(0, input_format)
ans = {
'input_formats': [x.upper() for x in input_formats],
'output_formats': get_sorted_output_formats(),
'conversion_defaults': conversion_defaults(),
'conversion_specifics': load_specifics(db, book_id),
'title': db.field_for('title', book_id),
'authors': db.field_for('authors', book_id),
}
return ans

View File

@ -179,7 +179,7 @@ class Context(object):
return old[1]
SRV_MODULES = ('ajax', 'books', 'cdb', 'code', 'content', 'legacy', 'opds', 'users_api')
SRV_MODULES = ('ajax', 'books', 'cdb', 'code', 'content', 'legacy', 'opds', 'users_api', 'convert')
class Handler(object):

View File

@ -14,7 +14,7 @@ from book_list.library_data import (
book_metadata, cover_url, current_library_id, current_virtual_library,
download_url, library_data, load_status, set_book_metadata
)
from book_list.router import back, home, open_book
from book_list.router import back, home, open_book, report_a_load_failure
from book_list.theme import get_color, get_font_size
from book_list.top_bar import add_button, clear_buttons, create_top_bar, set_title
from book_list.ui import query_as_href, set_panel_handler, show_panel
@ -546,17 +546,10 @@ def create_book_details(container):
def report_load_failure(container):
err = E.div()
safe_set_inner_html(err, load_status.error_html)
container.appendChild(E.div(
style='margin: 1ex 1em',
E.div(_('Failed to load books from calibre library, with error:')),
err,
E.div(
style='margin-top: 1em; border-top: solid 1px currentColor; padding-top: 1ex;',
E.a(onclick=def(): home(replace=True);, href='javascript: void(0)', style='color: blue', _('Go back to the home page')))
),
)
report_a_load_failure(
container, _('Failed to load books from calibre library, with error:'),
load_status.error_html)
def check_for_books_loaded():

View File

@ -0,0 +1,89 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import bound_methods, hash_literals
from elementmaker import E
from gettext import gettext as _
from ajax import ajax
from book_list.book_details import report_load_failure
from book_list.library_data import load_status, url_books_query
from book_list.router import back, report_a_load_failure
from book_list.top_bar import create_top_bar
from book_list.ui import set_panel_handler
from dom import add_extra_css, build_rule, clear
from utils import conditional_timeout, parse_url_params
CLASS_NAME = 'convert-book-panel'
add_extra_css(def():
sel = '.' + CLASS_NAME + ' '
style = build_rule(sel, placeholder='TODO: add this')
return style
)
conversion_data = None
conversion_data_load_status = {'loading':True, 'ok':False, 'error_html':None, 'current_fetch': None}
def on_data_loaded(end_type, xhr, ev):
nonlocal conversion_data
conversion_data_load_status.current_fetch = None
def bad_load(msg):
conversion_data_load_status.ok = False
conversion_data_load_status.loading = False
conversion_data_load_status.error_html = msg or xhr.error_html
if end_type is 'load':
conversion_data = JSON.parse(xhr.responseText)
elif end_type is 'abort':
pass
else:
bad_load()
def fetch_conversion_data(book_id):
if conversion_data_load_status.current_fetch:
conversion_data_load_status.current_fetch.abort()
query = url_books_query()
conversion_data_load_status.loading = True
conversion_data_load_status.ok = False
conversion_data_load_status.error_html = None
conversion_data_load_status.current_fetch = ajax(f'conversion/books-data/{book_id}', on_data_loaded, query=query)
conversion_data_load_status.current_fetch.send()
def on_close(container_id):
back()
def check_for_data_loaded():
container = this
if load_status.loading or conversion_data_load_status.loading:
conditional_timeout(container.id, 5, check_for_data_loaded)
return
container = container.lastChild
clear(container)
if not load_status.ok:
report_load_failure(container)
return
if not conversion_data_load_status.ok:
report_a_load_failure(
container, _('Failed to load conversion data from calibre, with error:'),
conversion_data_load_status.error_html)
# create_convert_book(container)
def init(container_id):
container = document.getElementById(container_id)
create_top_bar(container, title=_('Convert book'), action=on_close.bind(None, container_id), icon='close')
container.appendChild(E.div(class_=CLASS_NAME))
container.lastChild.appendChild(E.div(_('Loading conversion data, please wait...'), style='margin: 1ex 1em'))
conditional_timeout(container_id, 5, check_for_data_loaded)
q = parse_url_params()
fetch_conversion_data(q.book_id)
set_panel_handler('convert_book', init)

View File

@ -2,11 +2,17 @@
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import bound_methods, hash_literals
from elementmaker import E
from gettext import gettext as _
from book_list.constants import book_list_container_id, read_book_container_id
from book_list.globals import get_current_query
from book_list.library_data import current_library_id
from modals import close_all_modals
from utils import encode_query_with_path, parse_url_params, request_full_screen
from utils import (
encode_query_with_path, parse_url_params, request_full_screen,
safe_set_inner_html
)
mode_handlers = {}
default_mode_handler = None
@ -89,5 +95,20 @@ def back():
q = {}
push_state(q, replace=True)
def home(replace=False):
push_state({})
def report_a_load_failure(container, msg, error_html):
err = E.div()
safe_set_inner_html(err, error_html)
container.appendChild(E.div(
style='margin: 1ex 1em',
E.div(msg),
err,
E.div(
style='margin-top: 1em; border-top: solid 1px currentColor; padding-top: 1ex;',
E.a(onclick=def(): home(replace=True);, href='javascript: void(0)', style='color: blue', _('Go back to the home page')))
),
)