CS: Dynamically generate covers for books lacking a cover

This commit is contained in:
Kovid Goyal 2016-02-21 10:13:01 +05:30
parent 7c9cd55b19
commit d67b951c59
4 changed files with 71 additions and 22 deletions

View File

@ -53,6 +53,18 @@ authors = re(authors, ' &amp; ', '<br>');
re(authors, '&amp;&amp;', '&amp;')
'''
Prefs = namedtuple('Prefs', ' '.join(sorted(cprefs.defaults)))
_use_roman = None
def get_use_roman():
global _use_roman
if _use_roman is None:
return config['use_roman_numerals_for_series_number']
return _use_roman
def set_use_roman(val):
global _use_roman
_use_roman = bool(val)
# }}}
# Draw text {{{
@ -255,7 +267,7 @@ def preserve_fields(obj, fields):
def format_text(mi, prefs):
with preserve_fields(mi, 'authors formatted_series_index'):
mi.authors = [a for a in mi.authors if a != _('Unknown')]
mi.formatted_series_index = fmt_sidx(mi.series_index or 0, use_roman=config['use_roman_numerals_for_series_number'])
mi.formatted_series_index = fmt_sidx(mi.series_index or 0, use_roman=get_use_roman())
return tuple(format_fields(mi, prefs))
# }}}

View File

@ -4,20 +4,19 @@
from __future__ import (unicode_literals, division, absolute_import,
print_function)
import re, hashlib, random, os
import re, hashlib, random
from functools import partial
from threading import Lock
from json import load as load_json_file, dumps as json_dumps
from calibre import prepare_string_for_xml, as_unicode
from calibre.constants import config_dir
from calibre.customize.ui import available_input_formats
from calibre.db.view import sanitize_sort_field_name
from calibre.srv.ajax import search_result
from calibre.srv.errors import HTTPNotFound, HTTPBadRequest
from calibre.srv.metadata import book_as_json, categories_as_json, icon_map
from calibre.srv.routes import endpoint, json
from calibre.srv.utils import get_library_data
from calibre.srv.utils import get_library_data, get_use_roman
from calibre.utils.config import prefs, tweaks
from calibre.utils.icu import sort_key
from calibre.utils.search_query_parser import ParseException
@ -82,20 +81,6 @@ def get_basic_query_data(ctx, rd):
sorts, orders = ['timestamp'], ['desc']
return library_id, db, sorts, orders
_use_roman = None
def get_use_roman():
global _use_roman
if _use_roman is None:
try:
with lopen(os.path.join(config_dir, 'gui.py'), 'rb') as f:
raw = f.read()
except EnvironmentError:
_use_roman = False
else:
m = re.search(br'use_roman_numerals_for_series_number\s*=\s*(True|False)', raw)
_use_roman = m is not None and m.group(1) == b'True'
return _use_roman
DEFAULT_NUMBER_OF_BOOKS = 50

View File

@ -11,6 +11,7 @@ from binascii import hexlify
from io import BytesIO
from threading import Lock
from future_builtins import map
from functools import partial
from calibre import fit_image
from calibre.constants import config_dir, iswindows
@ -21,12 +22,13 @@ from calibre.ebooks.metadata.opf2 import metadata_to_opf
from calibre.library.save_to_disk import find_plugboard
from calibre.srv.errors import HTTPNotFound
from calibre.srv.routes import endpoint, json
from calibre.srv.utils import http_date, get_db
from calibre.srv.utils import http_date, get_db, get_use_roman
from calibre.utils.config_base import tweaks
from calibre.utils.date import timestampfromdt
from calibre.utils.img import scale_image, image_from_data
from calibre.utils.filenames import ascii_filename, atomic_rename
from calibre.utils.shared_file import share_open
from calibre.utils.ipc.simple_worker import fork_job, WorkerError
plugboard_content_server_value = 'content_server'
plugboard_content_server_formats = ['epub', 'mobi', 'azw3']
@ -94,10 +96,45 @@ def create_file_copy(ctx, rd, prefix, library_id, book_id, ext, mtime, copy_func
rd.outheaders['Tempfile'] = hexlify(fname.encode('utf-8'))
return rd.filesystem_file_with_custom_etag(ans, prefix, library_id, book_id, mtime, extra_etag_data)
def generate_cover_worker(width, height, opf, file_name, use_roman):
# We have to generate the cover in a worker as it depends on Qt and needs
# QApplication
from calibre.ebooks.covers import cprefs, override_prefs, scale_cover, generate_cover, set_use_roman
from calibre.ebooks.metadata.opf2 import OPF
set_use_roman(use_roman)
mi = OPF(BytesIO(opf), try_to_guess_cover=False, populate_spine=False).to_book_metadata()
if height is None:
prefs = cprefs
else:
ratio = height / float(cprefs['cover_height'])
prefs = override_prefs(cprefs)
scale_cover(prefs, ratio)
cdata = generate_cover(mi, prefs=prefs)
with share_open(file_name, 'w+b') as f:
f.write(cdata)
def write_generated_cover(db, book_id, width, height, destf):
from calibre.ebooks.metadata.opf2 import metadata_to_opf
mi = metadata_to_opf(db.get_metadata(book_id))
try:
fork_job('calibre.srv.content', 'generate_cover_worker', args=(width, height, mi, destf.name, get_use_roman()), no_output=True)
except WorkerError as err:
raise Exception(err.orig_tb)
def generated_cover(ctx, rd, library_id, db, book_id, width=None, height=None):
prefix = 'generated-cover'
if height is not None:
prefix += '-%sx%s' % (width, height)
mtime = timestampfromdt(db.field_for('last_modified', book_id))
return create_file_copy(ctx, rd, prefix, library_id, book_id, 'jpg', mtime, partial(write_generated_cover, db, book_id, width, height))
def cover(ctx, rd, library_id, db, book_id, width=None, height=None):
mtime = db.cover_last_modified(book_id)
if mtime is None:
raise HTTPNotFound('No cover for book: %r' % book_id)
return generated_cover(ctx, rd, library_id, db, book_id, width, height)
prefix = 'cover'
if width is None and height is None:
def copy_func(dest):

View File

@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import,
__license__ = 'GPL v3'
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
import errno, socket, select, os
import errno, socket, select, os, re
from Cookie import SimpleCookie
from contextlib import closing
from urlparse import parse_qs
@ -18,7 +18,7 @@ from urllib import quote as urlquote
from binascii import hexlify, unhexlify
from calibre import prints
from calibre.constants import iswindows
from calibre.constants import iswindows, config_dir
from calibre.srv.errors import HTTPNotFound
from calibre.utils.config_base import tweaks
from calibre.utils.localization import get_translator
@ -505,3 +505,18 @@ class Offsets(object):
self.last_offset = last_allowed_index - delta
if self.last_offset < 0:
self.last_offset = 0
_use_roman = None
def get_use_roman():
global _use_roman
if _use_roman is None:
try:
with lopen(os.path.join(config_dir, 'gui.py'), 'rb') as f:
raw = f.read()
except EnvironmentError:
_use_roman = False
else:
m = re.search(br'use_roman_numerals_for_series_number\s*=\s*(True|False)', raw)
_use_roman = m is not None and m.group(1) == b'True'
return _use_roman