mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
CS: Dynamically generate covers for books lacking a cover
This commit is contained in:
parent
7c9cd55b19
commit
d67b951c59
@ -53,6 +53,18 @@ authors = re(authors, ' & ', '<br>');
|
|||||||
re(authors, '&&', '&')
|
re(authors, '&&', '&')
|
||||||
'''
|
'''
|
||||||
Prefs = namedtuple('Prefs', ' '.join(sorted(cprefs.defaults)))
|
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 {{{
|
# Draw text {{{
|
||||||
@ -255,7 +267,7 @@ def preserve_fields(obj, fields):
|
|||||||
def format_text(mi, prefs):
|
def format_text(mi, prefs):
|
||||||
with preserve_fields(mi, 'authors formatted_series_index'):
|
with preserve_fields(mi, 'authors formatted_series_index'):
|
||||||
mi.authors = [a for a in mi.authors if a != _('Unknown')]
|
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))
|
return tuple(format_fields(mi, prefs))
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -4,20 +4,19 @@
|
|||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import,
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
print_function)
|
print_function)
|
||||||
import re, hashlib, random, os
|
import re, hashlib, random
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from json import load as load_json_file, dumps as json_dumps
|
from json import load as load_json_file, dumps as json_dumps
|
||||||
|
|
||||||
from calibre import prepare_string_for_xml, as_unicode
|
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.customize.ui import available_input_formats
|
||||||
from calibre.db.view import sanitize_sort_field_name
|
from calibre.db.view import sanitize_sort_field_name
|
||||||
from calibre.srv.ajax import search_result
|
from calibre.srv.ajax import search_result
|
||||||
from calibre.srv.errors import HTTPNotFound, HTTPBadRequest
|
from calibre.srv.errors import HTTPNotFound, HTTPBadRequest
|
||||||
from calibre.srv.metadata import book_as_json, categories_as_json, icon_map
|
from calibre.srv.metadata import book_as_json, categories_as_json, icon_map
|
||||||
from calibre.srv.routes import endpoint, json
|
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.config import prefs, tweaks
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.utils.search_query_parser import ParseException
|
from calibre.utils.search_query_parser import ParseException
|
||||||
@ -82,20 +81,6 @@ def get_basic_query_data(ctx, rd):
|
|||||||
sorts, orders = ['timestamp'], ['desc']
|
sorts, orders = ['timestamp'], ['desc']
|
||||||
return library_id, db, sorts, orders
|
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
|
DEFAULT_NUMBER_OF_BOOKS = 50
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ from binascii import hexlify
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from future_builtins import map
|
from future_builtins import map
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from calibre import fit_image
|
from calibre import fit_image
|
||||||
from calibre.constants import config_dir, iswindows
|
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.library.save_to_disk import find_plugboard
|
||||||
from calibre.srv.errors import HTTPNotFound
|
from calibre.srv.errors import HTTPNotFound
|
||||||
from calibre.srv.routes import endpoint, json
|
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.config_base import tweaks
|
||||||
from calibre.utils.date import timestampfromdt
|
from calibre.utils.date import timestampfromdt
|
||||||
from calibre.utils.img import scale_image, image_from_data
|
from calibre.utils.img import scale_image, image_from_data
|
||||||
from calibre.utils.filenames import ascii_filename, atomic_rename
|
from calibre.utils.filenames import ascii_filename, atomic_rename
|
||||||
from calibre.utils.shared_file import share_open
|
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_value = 'content_server'
|
||||||
plugboard_content_server_formats = ['epub', 'mobi', 'azw3']
|
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'))
|
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)
|
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):
|
def cover(ctx, rd, library_id, db, book_id, width=None, height=None):
|
||||||
mtime = db.cover_last_modified(book_id)
|
mtime = db.cover_last_modified(book_id)
|
||||||
if mtime is None:
|
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'
|
prefix = 'cover'
|
||||||
if width is None and height is None:
|
if width is None and height is None:
|
||||||
def copy_func(dest):
|
def copy_func(dest):
|
||||||
|
@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import errno, socket, select, os
|
import errno, socket, select, os, re
|
||||||
from Cookie import SimpleCookie
|
from Cookie import SimpleCookie
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
from urlparse import parse_qs
|
from urlparse import parse_qs
|
||||||
@ -18,7 +18,7 @@ from urllib import quote as urlquote
|
|||||||
from binascii import hexlify, unhexlify
|
from binascii import hexlify, unhexlify
|
||||||
|
|
||||||
from calibre import prints
|
from calibre import prints
|
||||||
from calibre.constants import iswindows
|
from calibre.constants import iswindows, config_dir
|
||||||
from calibre.srv.errors import HTTPNotFound
|
from calibre.srv.errors import HTTPNotFound
|
||||||
from calibre.utils.config_base import tweaks
|
from calibre.utils.config_base import tweaks
|
||||||
from calibre.utils.localization import get_translator
|
from calibre.utils.localization import get_translator
|
||||||
@ -505,3 +505,18 @@ class Offsets(object):
|
|||||||
self.last_offset = last_allowed_index - delta
|
self.last_offset = last_allowed_index - delta
|
||||||
if self.last_offset < 0:
|
if self.last_offset < 0:
|
||||||
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user