1
0
mirror of https://github.com/janeczku/calibre-web synced 2024-12-26 01:50:31 +00:00

Remove Pillow as dependency

This commit is contained in:
Ozzieisaacs 2020-12-09 14:18:39 +01:00
parent d957b2d20f
commit 352b4a0b73
4 changed files with 60 additions and 70 deletions

View File

@ -18,21 +18,21 @@
from __future__ import division, print_function, unicode_literals from __future__ import division, print_function, unicode_literals
import os import os
import io
from . import logger, isoLanguages from . import logger, isoLanguages
from .constants import BookMeta from .constants import BookMeta
try:
from PIL import Image as PILImage
use_PIL = True
except ImportError as e:
use_PIL = False
log = logger.create() log = logger.create()
try:
from wand.image import Image
use_IM = True
except (ImportError, RuntimeError) as e:
use_IM = False
try: try:
from comicapi.comicarchive import ComicArchive, MetaDataStyle from comicapi.comicarchive import ComicArchive, MetaDataStyle
use_comic_meta = True use_comic_meta = True
@ -52,20 +52,26 @@ except (ImportError, LookupError) as e:
use_rarfile = False use_rarfile = False
use_comic_meta = False use_comic_meta = False
COVER_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.webp', '.bmp']
def _cover_processing(tmp_file_name, img, extension): def _cover_processing(tmp_file_name, img, extension):
if use_PIL: tmp_cover_name = os.path.join(os.path.dirname(tmp_file_name), 'cover.jpg')
if use_IM:
# convert to jpg because calibre only supports jpg # convert to jpg because calibre only supports jpg
if extension in ('.png', '.webp'): cover_ext = COVER_EXTENSIONS
imgc = PILImage.open(io.BytesIO(img)) cover_ext.remove('.jpeg')
im = imgc.convert('RGB') cover_ext.remove('.jpg')
tmp_bytesio = io.BytesIO() if extension in cover_ext:
im.save(tmp_bytesio, format='JPEG') with Image(filename=tmp_file_name) as imgc:
img = tmp_bytesio.getvalue() imgc.format = 'jpeg'
imgc.transform_colorspace('rgb')
imgc.save(tmp_cover_name)
return tmp_cover_name
if not img: if not img:
return None return None
tmp_cover_name = os.path.join(os.path.dirname(tmp_file_name), 'cover.jpg')
with open(tmp_cover_name, 'wb') as f: with open(tmp_cover_name, 'wb') as f:
f.write(img) f.write(img)
return tmp_cover_name return tmp_cover_name
@ -80,7 +86,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExecutable):
ext = os.path.splitext(name) ext = os.path.splitext(name)
if len(ext) > 1: if len(ext) > 1:
extension = ext[1].lower() extension = ext[1].lower()
if extension in ('.jpg', '.jpeg', '.png', '.webp'): if extension in COVER_EXTENSIONS:
cover_data = archive.getPage(index) cover_data = archive.getPage(index)
break break
else: else:
@ -90,7 +96,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExecutable):
ext = os.path.splitext(name) ext = os.path.splitext(name)
if len(ext) > 1: if len(ext) > 1:
extension = ext[1].lower() extension = ext[1].lower()
if extension in ('.jpg', '.jpeg', '.png', '.webp'): if extension in COVER_EXTENSIONS:
cover_data = cf.read(name) cover_data = cf.read(name)
break break
elif original_file_extension.upper() == '.CBT': elif original_file_extension.upper() == '.CBT':
@ -99,7 +105,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExecutable):
ext = os.path.splitext(name) ext = os.path.splitext(name)
if len(ext) > 1: if len(ext) > 1:
extension = ext[1].lower() extension = ext[1].lower()
if extension in ('.jpg', '.jpeg', '.png', '.webp'): if extension in COVER_EXTENSIONS:
cover_data = cf.extractfile(name).read() cover_data = cf.extractfile(name).read()
break break
elif original_file_extension.upper() == '.CBR' and use_rarfile: elif original_file_extension.upper() == '.CBR' and use_rarfile:
@ -110,7 +116,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExecutable):
ext = os.path.splitext(name) ext = os.path.splitext(name)
if len(ext) > 1: if len(ext) > 1:
extension = ext[1].lower() extension = ext[1].lower()
if extension in ('.jpg', '.jpeg', '.png', '.webp'): if extension in COVER_EXTENSIONS:
cover_data = cf.read(name) cover_data = cf.read(name)
break break
except Exception as e: except Exception as e:

View File

@ -50,13 +50,6 @@ try:
except ImportError: except ImportError:
use_unidecode = False use_unidecode = False
try:
from PIL import Image as PILImage
from PIL import UnidentifiedImageError
use_PIL = True
except ImportError:
use_PIL = False
from . import calibre_db from . import calibre_db
from .tasks.convert import TaskConvert from .tasks.convert import TaskConvert
from . import logger, config, get_locale, db, ub from . import logger, config, get_locale, db, ub
@ -66,9 +59,15 @@ from .subproc_wrapper import process_wait
from .services.worker import WorkerThread, STAT_WAITING, STAT_FAIL, STAT_STARTED, STAT_FINISH_SUCCESS from .services.worker import WorkerThread, STAT_WAITING, STAT_FAIL, STAT_STARTED, STAT_FINISH_SUCCESS
from .tasks.mail import TaskEmail from .tasks.mail import TaskEmail
log = logger.create() log = logger.create()
try:
from wand.image import Image
use_IM = True
except (ImportError, RuntimeError) as e:
log.debug('Cannot import Image, generating covers from non jpg files will not work: %s', e)
use_IM = False
# Convert existing book entry to new format # Convert existing book entry to new format
def convert_book_format(book_id, calibrepath, old_book_format, new_book_format, user_id, kindle_mail=None): def convert_book_format(book_id, calibrepath, old_book_format, new_book_format, user_id, kindle_mail=None):
@ -109,21 +108,21 @@ def convert_book_format(book_id, calibrepath, old_book_format, new_book_format,
def send_test_mail(kindle_mail, user_name): def send_test_mail(kindle_mail, user_name):
WorkerThread.add(user_name, TaskEmail(_(u'Calibre-Web test e-mail'), None, None, WorkerThread.add(user_name, TaskEmail(_(u'Calibre-Web test e-mail'), None, None,
config.get_mail_settings(), kindle_mail, _(u"Test e-mail"), config.get_mail_settings(), kindle_mail, _(u"Test e-mail"),
_(u'This e-mail has been sent via Calibre-Web.'))) _(u'This e-mail has been sent via Calibre-Web.')))
return return
# Send registration email or password reset email, depending on parameter resend (False means welcome email) # Send registration email or password reset email, depending on parameter resend (False means welcome email)
def send_registration_mail(e_mail, user_name, default_password, resend=False): def send_registration_mail(e_mail, user_name, default_password, resend=False):
text = "Hello %s!\r\n" % user_name txt = "Hello %s!\r\n" % user_name
if not resend: if not resend:
text += "Your new account at Calibre-Web has been created. Thanks for joining us!\r\n" txt += "Your new account at Calibre-Web has been created. Thanks for joining us!\r\n"
text += "Please log in to your account using the following informations:\r\n" txt += "Please log in to your account using the following informations:\r\n"
text += "User name: %s\r\n" % user_name txt += "User name: %s\r\n" % user_name
text += "Password: %s\r\n" % default_password txt += "Password: %s\r\n" % default_password
text += "Don't forget to change your password after first login.\r\n" txt += "Don't forget to change your password after first login.\r\n"
text += "Sincerely\r\n\r\n" txt += "Sincerely\r\n\r\n"
text += "Your Calibre-Web team" txt += "Your Calibre-Web team"
WorkerThread.add(None, TaskEmail( WorkerThread.add(None, TaskEmail(
subject=_(u'Get Started with Calibre-Web'), subject=_(u'Get Started with Calibre-Web'),
filepath=None, filepath=None,
@ -131,7 +130,7 @@ def send_registration_mail(e_mail, user_name, default_password, resend=False):
settings=config.get_mail_settings(), settings=config.get_mail_settings(),
recipient=e_mail, recipient=e_mail,
taskMessage=_(u"Registration e-mail for user: %(name)s", name=user_name), taskMessage=_(u"Registration e-mail for user: %(name)s", name=user_name),
text=text text=txt
)) ))
return return
@ -177,7 +176,7 @@ def check_send_to_kindle(entry):
'convert': 0, 'convert': 0,
'text': _('Send %(format)s to Kindle', format='Pdf')}) 'text': _('Send %(format)s to Kindle', format='Pdf')})
if config.config_converterpath: if config.config_converterpath:
if 'EPUB' in formats and not 'MOBI' in formats: if 'EPUB' in formats and 'MOBI' not in formats:
bookformats.append({'format': 'Mobi', bookformats.append({'format': 'Mobi',
'convert':1, 'convert':1,
'text': _('Convert %(orig)s to %(format)s and send to Kindle', 'text': _('Convert %(orig)s to %(format)s and send to Kindle',
@ -586,16 +585,15 @@ def save_cover_from_url(url, book_path):
requests.exceptions.Timeout) as ex: requests.exceptions.Timeout) as ex:
log.info(u'Cover Download Error %s', ex) log.info(u'Cover Download Error %s', ex)
return False, _("Error Downloading Cover") return False, _("Error Downloading Cover")
except UnidentifiedImageError as ex: # except UnidentifiedImageError as ex:
log.info(u'File Format Error %s', ex) # log.info(u'File Format Error %s', ex)
return False, _("Cover Format Error") # return False, _("Cover Format Error")
def save_cover_from_filestorage(filepath, saved_filename, img): def save_cover_from_filestorage(filepath, saved_filename, img):
if hasattr(img, '_content'): if hasattr(img,"metadata"):
f = open(os.path.join(filepath, saved_filename), "wb") img.save(filename=os.path.join(filepath, saved_filename))
f.write(img._content) img.close()
f.close()
else: else:
# check if file path exists, otherwise create it, copy file to calibre path and delete temp file # check if file path exists, otherwise create it, copy file to calibre path and delete temp file
if not os.path.exists(filepath): if not os.path.exists(filepath):
@ -616,20 +614,19 @@ def save_cover_from_filestorage(filepath, saved_filename, img):
def save_cover(img, book_path): def save_cover(img, book_path):
content_type = img.headers.get('content-type') content_type = img.headers.get('content-type')
if use_PIL: if use_IM:
if content_type not in ('image/jpeg', 'image/png', 'image/webp'): if content_type not in ('image/jpeg', 'image/png', 'image/webp', 'image/bmp'):
log.error("Only jpg/jpeg/png/webp files are supported as coverfile") log.error("Only jpg/jpeg/png/webp/bmp files are supported as coverfile")
return False, _("Only jpg/jpeg/png/webp files are supported as coverfile") return False, _("Only jpg/jpeg/png/webp/bmp files are supported as coverfile")
# convert to jpg because calibre only supports jpg # convert to jpg because calibre only supports jpg
if content_type in ('image/png', 'image/webp'): if content_type != 'image/jpg':
if hasattr(img, 'stream'): if hasattr(img, 'stream'):
imgc = PILImage.open(img.stream) imgc = Image(blob=img.stream)
else: else:
imgc = PILImage.open(io.BytesIO(img.content)) imgc = Image(blob=io.BytesIO(img.content))
im = imgc.convert('RGB') imgc.format = 'jpeg'
tmp_bytesio = io.BytesIO() imgc.transform_colorspace("rgb")
im.save(tmp_bytesio, format='JPEG') img = imgc
img._content = tmp_bytesio.getvalue()
else: else:
if content_type not in 'image/jpeg': if content_type not in 'image/jpeg':
log.error("Only jpg/jpeg files are supported as coverfile") log.error("Only jpg/jpeg files are supported as coverfile")

View File

@ -66,14 +66,6 @@ except ImportError as e:
log.debug('Cannot import fb2, extracting fb2 metadata will not work: %s', e) log.debug('Cannot import fb2, extracting fb2 metadata will not work: %s', e)
use_fb2_meta = False use_fb2_meta = False
try:
from PIL import Image as PILImage
from PIL import __version__ as PILversion
use_PIL = True
except ImportError as e:
log.debug('Cannot import Pillow, using png and webp images as cover will not work: %s', e)
use_PIL = False
def process(tmp_file_path, original_file_name, original_file_extension, rarExecutable): def process(tmp_file_path, original_file_name, original_file_extension, rarExecutable):
meta = None meta = None
@ -179,10 +171,6 @@ def get_versions():
XVersion = 'v'+'.'.join(map(str, lxmlversion)) XVersion = 'v'+'.'.join(map(str, lxmlversion))
else: else:
XVersion = u'not installed' XVersion = u'not installed'
if use_PIL:
PILVersion = 'v' + PILversion
else:
PILVersion = u'not installed'
if comic.use_comic_meta: if comic.use_comic_meta:
ComicVersion = comic.comic_version or u'installed' ComicVersion = comic.comic_version or u'installed'
else: else:
@ -191,7 +179,7 @@ def get_versions():
'PyPdf': PVersion, 'PyPdf': PVersion,
'lxml':XVersion, 'lxml':XVersion,
'Wand': WVersion, 'Wand': WVersion,
'Pillow': PILVersion, # 'Pillow': PILVersion,
'Comic_API': ComicVersion} 'Comic_API': ComicVersion}

View File

@ -26,7 +26,6 @@ SQLAlchemy-Utils>=0.33.5,<0.37.0
# extracting metadata # extracting metadata
lxml>=3.8.0,<4.6.0 lxml>=3.8.0,<4.6.0
Pillow>=4.0.0,<7.2.0
rarfile>=2.7 rarfile>=2.7
# other # other