Store MathJax in the db

This commit is contained in:
Kovid Goyal 2016-05-02 14:32:30 +05:30
parent 7501a3b0fd
commit edb11dbd28
2 changed files with 136 additions and 7 deletions

View File

@ -13,6 +13,8 @@ def upgrade_schema(idb, old_version, new_version):
idb.createObjectStore('books', {'keyPath':'key'})
if not idb.objectStoreNames.contains('files'):
idb.createObjectStore('files')
if not idb.objectStoreNames.contains('mathjax'):
idb.createObjectStore('mathjax')
if not idb.objectStoreNames.contains('objects'):
idb.createObjectStore('objects', {'keyPath':'key'})
@ -27,7 +29,7 @@ def get_error_details(event):
desc = desc.errorCode
DB_NAME = 'calibre-books-db-testing' # TODO: Remove test suffix and change version back to 1
DB_VERSION = 1
DB_VERSION = 2
class DB:
@ -96,6 +98,11 @@ class DB:
})
)
def get_mathjax_info(self, proceed):
self.do_op(['objects'], 'mathjax-info', _('Failed to read from the objects database'), def(result):
proceed(result or {'key':'mathjax-info'})
)
def save_manifest(self, book, manifest, proceed):
book.manifest = manifest
book.metadata = manifest.metadata
@ -106,7 +113,7 @@ class DB:
self.do_op(['books'], book, _('Failed to write to the books database'), proceed, op='put')
def store_file(self, book, name, xhr, proceed, is_cover):
store_as_text = xhr.responseType is 'text'
store_as_text = xhr.responseType is 'text' or not xhr.responseType
fname = file_store_name(book, name)
needs_encoding = not store_as_text and not self.supports_blobs
book.stored_files[fname] = {'encoded':needs_encoding, 'mimetype':book.manifest.files[name].mimetype, 'store_as_text':store_as_text}
@ -148,10 +155,22 @@ class DB:
req.onerror = def(event):
proceed(_('Failed to store book data ({0}) with error: {1}').format(name, get_error_details(event)))
def store_mathjax_file(self, name, xhr, proceed):
data = xhr.response
if not self.supports_blobs:
data = base64encode(Uint8Array(data))
req = self.idb.transaction(['mathjax'], 'readwrite').objectStore('mathjax').put(data, name)
req.onsuccess = def(event): proceed()
req.onerror = def(event):
proceed(_('Failed to store mathjax file ({0}) with error: {1}').format(name, get_error_details(event)))
def finish_book(self, book, proceed):
book.is_complete = True
self.do_op(['books'], book, _('Failed to write to the books database'), proceed, op='put')
def finish_mathjax(self, mathjax_info, proceed):
self.do_op(['objects'], mathjax_info, _('Failed to write to the objects database'), proceed, op='put')
def update_last_read_time(self, book):
book.last_read = Date()
self.do_op(['books'], book, _('Failed to write to the books database'), op='put')

View File

@ -141,7 +141,7 @@ class ReadUI:
return
if end_type is not 'load':
return self.show_error(_('Failed to load book manifest'),
_('Could not open {title} as book manifest failed to load, click "Show Details" for more information.').format(title=self.current_metadata.title),
_('Could not open {title} as the book manifest failed to load, click "Show Details" for more information.').format(title=self.current_metadata.title),
xhr.error_html)
try:
manifest = JSON.parse(xhr.responseText)
@ -201,6 +201,11 @@ class ReadUI:
pbar.setAttribute('value', x + '')
progress.lastChild.textContent = _('Downloaded {0}, {1} left').format(human_readable(x), human_readable(total - x))
def show_failure():
det = ['<h4>{}</h4><div>{}</div><hr>'.format(fname, err_html) for fname, err_html in failed_files].join('')
self.show_error(_('Could not download book'), _(
'Failed to download some book data, click "Show details" for more information'), det)
def on_stored(err):
files_left.discard(this)
if err:
@ -208,10 +213,7 @@ class ReadUI:
if len(files_left):
return
if failed_files.length:
det = ['<h4>{}</h4><div>{}</div><hr>'.format(fname, err_html) for fname, err_html in failed_files].join('')
self.show_error(_('Could not download book'), _(
'Failed to download some book data, click "Show details" for more information'), det)
return
return show_failure()
self.db.finish_book(book, self.display_book.bind(self, book))
def on_complete(end_type, xhr, ev):
@ -226,6 +228,8 @@ class ReadUI:
else:
failed_files.append([this, xhr.error_html])
files_left.discard(this)
if not len(files_left):
show_failure()
def on_progress(loaded, ftotal):
nonlocal total, cover_total_updated, raster_cover_size
@ -252,6 +256,112 @@ class ReadUI:
if fname is not raster_cover_name:
start_download(fname, base_path + encodeURIComponent(fname))
def ensure_maths(self, proceed):
self.db.get_mathjax_info(def(mathjax_info):
if mathjax_info.version is MATHJAX_VERSION:
return proceed()
print('Upgrading MathJax, previous version:', mathjax_info.version)
self.get_mathjax_manifest(mathjax_info, proceed)
)
def get_mathjax_manifest(self, mathjax_info, proceed):
ajax('mathjax', def(end_type, xhr, event):
if end_type is 'abort':
return
if end_type is not 'load':
return self.show_error(_('Failed to load MathJax manifest'),
_('Could not open {title} as the MathJax manifest failed to load, click "Show Details" for more information.').format(title=self.current_metadata.title),
xhr.error_html)
try:
manifest = JSON.parse(xhr.responseText)
except Exception:
return self.show_error(_('Failed to load MathJax manifest'),
_('The MathJax manifest is not valid'), traceback.format_exc())
if manifest.etag is not MATHJAX_VERSION:
print('calibre upgraded: MATHJAX_VERSION={} manifest.etag={}'.format(MATHJAX_VERSION, manifest.etag))
return self.show_error(_('calibre upgraded!'), _(
'A newer version of calibre is available, please click the reload button in your browser.'))
mathjax_info.version = manifest.etag
mathjax_info.files = manifest.files
self.download_mathjax(mathjax_info, proceed)
).send()
def download_mathjax(self, mathjax_info, proceed):
files = mathjax_info.files
total = 0
progress_track = {}
files_left = set()
failed_files = []
for key in files:
total += files[key]
progress_track[key] = 0
files_left.add(key)
progress = document.getElementById(self.progress_id)
progress.firstChild.textContent = _(
'Downloading MathJax to render mathematics in this book...')
pbar = progress.firstChild.nextSibling
pbar.setAttribute('max', total + '')
for xhr in self.downloads_in_progress:
xhr.abort()
self.downloads_in_progress = []
def update_progress():
x = 0
for name in progress_track:
x += progress_track[name]
pbar.setAttribute('value', x + '')
progress.lastChild.textContent = _('Downloaded {0}, {1} left').format(human_readable(x), human_readable(total - x))
def on_progress(loaded, ftotal):
progress_track[this] = loaded
update_progress()
def show_failure():
det = ['<h4>{}</h4><div>{}</div><hr>'.format(fname, err_html) for fname, err_html in failed_files].join('')
self.show_error(_('Could not download MathJax'), _(
'Failed to download some MathJax data, click "Show details" for more information'), det)
def on_complete(end_type, xhr, ev):
self.downloads_in_progress.remove(xhr)
progress_track[this] = files[this]
update_progress()
if end_type is 'abort':
files_left.discard(this)
return
if end_type is 'load':
self.db.store_mathjax_file(this, xhr, on_stored.bind(this))
else:
failed_files.append([this, xhr.error_html])
files_left.discard(this)
if not len(files_left):
show_failure()
def on_stored(err):
files_left.discard(this)
if err:
failed_files.append([this, err])
if len(files_left):
return
if failed_files.length:
return show_failure()
self.db.finish_mathjax(mathjax_info, proceed)
def start_download(name):
path = 'mathjax/' + name
xhr = ajax(path, on_complete.bind(name), on_progress=on_progress.bind(name), progress_totals_needed=False)
xhr.responseType = 'blob' if self.db.supports_blobs else 'arraybuffer'
xhr.send()
self.downloads_in_progress.push(xhr)
for fname in files_left:
start_download(fname)
def display_book(self, book):
if book.manifest.has_maths:
self.ensure_maths(self.display_book_stage2.bind(self, book))
else:
self.display_book_stage2(book)
def display_book_stage2(self, book):
self.show_stack(self.display_id)
self.view.display_book(book)