In-server conversion basically works, phew!

Now to create the UI for conversion settings.
This commit is contained in:
Kovid Goyal 2018-06-30 08:18:27 +05:30
parent 256c509238
commit 72fbe75bec
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 34 additions and 22 deletions

View File

@ -20,7 +20,7 @@ from calibre.customize.ui import run_plugins_on_import, run_plugins_on_postimpor
from calibre.db import SPOOL_SIZE, _get_next_series_num_for_list from calibre.db import SPOOL_SIZE, _get_next_series_num_for_list
from calibre.db.categories import get_categories from calibre.db.categories import get_categories
from calibre.db.locking import create_locks, DowngradeLockError, SafeReadLock from calibre.db.locking import create_locks, DowngradeLockError, SafeReadLock
from calibre.db.errors import NoSuchFormat from calibre.db.errors import NoSuchFormat, NoSuchBook
from calibre.db.fields import create_field, IDENTITY, InvalidLinkTable from calibre.db.fields import create_field, IDENTITY, InvalidLinkTable
from calibre.db.search import Search from calibre.db.search import Search
from calibre.db.tables import VirtualTable from calibre.db.tables import VirtualTable
@ -1340,10 +1340,9 @@ class Cache(object):
user_mi = mi.get_all_user_metadata(make_copy=False) user_mi = mi.get_all_user_metadata(make_copy=False)
fm = self.field_metadata fm = self.field_metadata
for key in user_mi.iterkeys(): for key in user_mi.iterkeys():
if (key in fm and if (key in fm and user_mi[key]['datatype'] == fm[key]['datatype'] and (
user_mi[key]['datatype'] == fm[key]['datatype'] and user_mi[key]['datatype'] != 'text' or (
(user_mi[key]['datatype'] != 'text' or user_mi[key]['is_multiple'] == fm[key]['is_multiple']))):
user_mi[key]['is_multiple'] == fm[key]['is_multiple'])):
val = mi.get(key, None) val = mi.get(key, None)
if force_changes or val is not None: if force_changes or val is not None:
protected_set_field(key, val) protected_set_field(key, val)
@ -1397,6 +1396,8 @@ class Cache(object):
fmt = check_ebook_format(stream_or_path, fmt) fmt = check_ebook_format(stream_or_path, fmt)
with self.write_lock: with self.write_lock:
if not self._has_id(book_id):
raise NoSuchBook(book_id)
fmt = (fmt or '').upper() fmt = (fmt or '').upper()
self.format_metadata_cache[book_id].pop(fmt, None) self.format_metadata_cache[book_id].pop(fmt, None)
try: try:

View File

@ -11,3 +11,9 @@ __docformat__ = 'restructuredtext en'
class NoSuchFormat(ValueError): class NoSuchFormat(ValueError):
pass pass
class NoSuchBook(KeyError):
def __init__(self, book_id):
KeyError.__init__(self, 'No book with id: {} in database'.format(book_id))
self.book_id = book_id

View File

@ -9,6 +9,7 @@ import shutil
import tempfile import tempfile
from threading import Lock from threading import Lock
from calibre.db.errors import NoSuchBook
from calibre.srv.changes import formats_added from calibre.srv.changes import formats_added
from calibre.srv.errors import BookNotFound, HTTPNotFound from calibre.srv.errors import BookNotFound, HTTPNotFound
from calibre.srv.routes import endpoint, json from calibre.srv.routes import endpoint, json
@ -25,7 +26,7 @@ class JobStatus(object):
def __init__(self, job_id, book_id, tdir, library_id, pathtoebook, conversion_data): def __init__(self, job_id, book_id, tdir, library_id, pathtoebook, conversion_data):
self.job_id = job_id self.job_id = job_id
self.log = '' self.log = self.traceback = ''
self.book_id = book_id self.book_id = book_id
self.output_path = os.path.join( self.output_path = os.path.join(
tdir, 'output.' + conversion_data['output_fmt'].lower()) tdir, 'output.' + conversion_data['output_fmt'].lower())
@ -34,9 +35,11 @@ class JobStatus(object):
self.conversion_data = conversion_data self.conversion_data = conversion_data
self.running = self.ok = True self.running = self.ok = True
self.last_check_at = monotonic() self.last_check_at = monotonic()
self.was_aborted = False
def cleanup(self): def cleanup(self):
safe_delete_tree(self.tdir) safe_delete_tree(self.tdir)
self.log = self.traceback = ''
@property @property
def current_status(self): def current_status(self):
@ -106,7 +109,7 @@ def convert_book(path_to_ebook, opf_path, cover_path, output_fmt, recs):
os.chdir(os.path.dirname(path_to_ebook)) os.chdir(os.path.dirname(path_to_ebook))
status_file = share_open('status', 'wb') status_file = share_open('status', 'wb')
def notification(percent, msg): def notification(percent, msg=''):
status_file.write('{}:{}|||\n'.format(percent, msg).encode('utf-8')) status_file.write('{}:{}|||\n'.format(percent, msg).encode('utf-8'))
status_file.flush() status_file.flush()
@ -129,11 +132,11 @@ def queue_job(ctx, rd, library_id, db, fmt, book_id, conversion_data):
fd, pathtocover = tempfile.mkstemp(prefix='', suffix=('.jpg'), dir=tdir) fd, pathtocover = tempfile.mkstemp(prefix='', suffix=('.jpg'), dir=tdir)
with os.fdopen(fd, 'wb') as f: with os.fdopen(fd, 'wb') as f:
cover_copied = db.copy_cover_to(book_id, f) cover_copied = db.copy_cover_to(book_id, f)
cover_path = f.name if cover_copied else None cover_path = pathtocover if cover_copied else None
mi = db.get_metadata(book_id) mi = db.get_metadata(book_id)
mi.application_id = mi.uuid mi.application_id = mi.uuid
raw = metadata_to_opf(mi) raw = metadata_to_opf(mi)
fd, pathtocover = tempfile.mkstemp(prefix='', suffix=('.opf'), dir=tdir) fd, opf_path = tempfile.mkstemp(prefix='', suffix=('.opf'), dir=tdir)
with os.fdopen(fd, 'wb') as metadata_file: with os.fdopen(fd, 'wb') as metadata_file:
metadata_file.write(raw) metadata_file.write(raw)
@ -146,7 +149,7 @@ def queue_job(ctx, rd, library_id, db, fmt, book_id, conversion_data):
job_id = ctx.start_job( job_id = ctx.start_job(
'Convert book %s (%s)' % (book_id, fmt), 'calibre.srv.convert', 'Convert book %s (%s)' % (book_id, fmt), 'calibre.srv.convert',
'convert_book', args=( 'convert_book', args=(
pathtoebook, metadata_file.name, cover_path, conversion_data['output_fmt'], recs), pathtoebook, opf_path, cover_path, conversion_data['output_fmt'], recs),
job_done_callback=job_done job_done_callback=job_done
) )
expire_old_jobs() expire_old_jobs()
@ -181,19 +184,20 @@ def conversion_status(ctx, rd, job_id):
del conversion_jobs[job_id] del conversion_jobs[job_id]
try: try:
ans = {'running': False, 'ok': job_status.ok, 'was_aborted': job_status.was_aborted, ans = {'running': False, 'ok': job_status.ok, 'was_aborted':
'traceback': job_status.traceback, 'log': job_status.log} job_status.was_aborted, 'traceback': job_status.traceback,
'log': job_status.log}
if job_status.ok: if job_status.ok:
db, library_id = get_library_data(ctx, rd)[:2] db, library_id = get_library_data(ctx, rd)[:2]
if library_id != job_status.library_id: if library_id != job_status.library_id:
raise HTTPNotFound('job library_id does not match') raise HTTPNotFound('job library_id does not match')
with db.safe_read_lock: fmt = job_status.output_path.rpartition('.')[-1]
if not db.has_id(job_status.book_id): try:
raise HTTPNotFound(
'book_id {} not found in library'.format(job_status.book_id))
fmt = job_status.output_path.rpartition('.')[-1]
db.add_format(job_status.book_id, fmt, job_status.output_path) db.add_format(job_status.book_id, fmt, job_status.output_path)
formats_added({job_status.book_id: (fmt,)}) except NoSuchBook:
raise HTTPNotFound(
'book_id {} not found in library'.format(job_status.book_id))
formats_added({job_status.book_id: (fmt,)})
ans['size'] = os.path.getsize(job_status.output_path) ans['size'] = os.path.getsize(job_status.output_path)
ans['fmt'] = fmt ans['fmt'] = fmt
return ans return ans

View File

@ -66,7 +66,7 @@ def report_conversion_ajax_failure(xhr):
nonlocal current_state nonlocal current_state
current_state = 'configuring' current_state = 'configuring'
apply_state_to_markup() apply_state_to_markup()
error_dialog(_('Failed to start conversion'), _( error_dialog(_('Failed to convert'), _(
'Could not convert {}. Click "Show details" for more' 'Could not convert {}. Click "Show details" for more'
' information').format(conversion_data.title), ' information').format(conversion_data.title),
xhr.error_html xhr.error_html
@ -104,14 +104,15 @@ def on_conversion_status(end_type, xhr, ev):
if response.running: if response.running:
c = container_for_current_state() c = container_for_current_state()
c.querySelector('progress').value = response.percent c.querySelector('progress').value = response.percent
c.querySelector('.progress-msg').textContent = response.msg if response.msg:
c.querySelector('.progress-msg').textContent = response.msg
check_for_conversion_status() check_for_conversion_status()
else: else:
if response.ok: if response.ok:
current_state = 'converted'
apply_state_to_markup()
conversion_data.fmt = response.fmt conversion_data.fmt = response.fmt
conversion_data.size = response.size conversion_data.size = response.size
current_state = 'converted'
apply_state_to_markup()
else: else:
show_failure(response) show_failure(response)
else: else: