diff --git a/cps/comic.py b/cps/comic.py index 7e3c7d47..d9f1796c 100644 --- a/cps/comic.py +++ b/cps/comic.py @@ -18,21 +18,21 @@ from __future__ import division, print_function, unicode_literals import os -import io from . import logger, isoLanguages from .constants import BookMeta -try: - from PIL import Image as PILImage - use_PIL = True -except ImportError as e: - use_PIL = False - log = logger.create() +try: + from wand.image import Image + use_IM = True +except (ImportError, RuntimeError) as e: + use_IM = False + + try: from comicapi.comicarchive import ComicArchive, MetaDataStyle use_comic_meta = True @@ -52,20 +52,26 @@ except (ImportError, LookupError) as e: use_rarfile = False use_comic_meta = False +COVER_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.webp', '.bmp'] + + 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 - if extension in ('.png', '.webp'): - imgc = PILImage.open(io.BytesIO(img)) - im = imgc.convert('RGB') - tmp_bytesio = io.BytesIO() - im.save(tmp_bytesio, format='JPEG') - img = tmp_bytesio.getvalue() + cover_ext = COVER_EXTENSIONS + cover_ext.remove('.jpeg') + cover_ext.remove('.jpg') + if extension in cover_ext: + with Image(filename=tmp_file_name) as imgc: + imgc.format = 'jpeg' + imgc.transform_colorspace('rgb') + imgc.save(tmp_cover_name) + return tmp_cover_name if not img: return None - tmp_cover_name = os.path.join(os.path.dirname(tmp_file_name), 'cover.jpg') with open(tmp_cover_name, 'wb') as f: f.write(img) return tmp_cover_name @@ -80,7 +86,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExecutable): ext = os.path.splitext(name) if len(ext) > 1: extension = ext[1].lower() - if extension in ('.jpg', '.jpeg', '.png', '.webp'): + if extension in COVER_EXTENSIONS: cover_data = archive.getPage(index) break else: @@ -90,7 +96,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExecutable): ext = os.path.splitext(name) if len(ext) > 1: extension = ext[1].lower() - if extension in ('.jpg', '.jpeg', '.png', '.webp'): + if extension in COVER_EXTENSIONS: cover_data = cf.read(name) break elif original_file_extension.upper() == '.CBT': @@ -99,7 +105,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExecutable): ext = os.path.splitext(name) if len(ext) > 1: extension = ext[1].lower() - if extension in ('.jpg', '.jpeg', '.png', '.webp'): + if extension in COVER_EXTENSIONS: cover_data = cf.extractfile(name).read() break 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) if len(ext) > 1: extension = ext[1].lower() - if extension in ('.jpg', '.jpeg', '.png', '.webp'): + if extension in COVER_EXTENSIONS: cover_data = cf.read(name) break except Exception as e: diff --git a/cps/helper.py b/cps/helper.py index f0d556d9..fdc51d6b 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -50,13 +50,6 @@ try: except ImportError: 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 .tasks.convert import TaskConvert 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 .tasks.mail import TaskEmail - 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 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): WorkerThread.add(user_name, TaskEmail(_(u'Calibre-Web test e-mail'), None, None, 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 # 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): - text = "Hello %s!\r\n" % user_name + txt = "Hello %s!\r\n" % user_name if not resend: - text += "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" - text += "User name: %s\r\n" % user_name - text += "Password: %s\r\n" % default_password - text += "Don't forget to change your password after first login.\r\n" - text += "Sincerely\r\n\r\n" - text += "Your Calibre-Web team" + txt += "Your new account at Calibre-Web has been created. Thanks for joining us!\r\n" + txt += "Please log in to your account using the following informations:\r\n" + txt += "User name: %s\r\n" % user_name + txt += "Password: %s\r\n" % default_password + txt += "Don't forget to change your password after first login.\r\n" + txt += "Sincerely\r\n\r\n" + txt += "Your Calibre-Web team" WorkerThread.add(None, TaskEmail( subject=_(u'Get Started with Calibre-Web'), filepath=None, @@ -131,7 +130,7 @@ def send_registration_mail(e_mail, user_name, default_password, resend=False): settings=config.get_mail_settings(), recipient=e_mail, taskMessage=_(u"Registration e-mail for user: %(name)s", name=user_name), - text=text + text=txt )) return @@ -177,7 +176,7 @@ def check_send_to_kindle(entry): 'convert': 0, 'text': _('Send %(format)s to Kindle', format='Pdf')}) 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', 'convert':1, '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: log.info(u'Cover Download Error %s', ex) return False, _("Error Downloading Cover") - except UnidentifiedImageError as ex: - log.info(u'File Format Error %s', ex) - return False, _("Cover Format Error") +# except UnidentifiedImageError as ex: +# log.info(u'File Format Error %s', ex) +# return False, _("Cover Format Error") def save_cover_from_filestorage(filepath, saved_filename, img): - if hasattr(img, '_content'): - f = open(os.path.join(filepath, saved_filename), "wb") - f.write(img._content) - f.close() + if hasattr(img,"metadata"): + img.save(filename=os.path.join(filepath, saved_filename)) + img.close() else: # check if file path exists, otherwise create it, copy file to calibre path and delete temp file 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): content_type = img.headers.get('content-type') - if use_PIL: - if content_type not in ('image/jpeg', 'image/png', 'image/webp'): - log.error("Only jpg/jpeg/png/webp files are supported as coverfile") - return False, _("Only jpg/jpeg/png/webp files are supported as coverfile") + if use_IM: + if content_type not in ('image/jpeg', 'image/png', 'image/webp', 'image/bmp'): + log.error("Only jpg/jpeg/png/webp/bmp 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 - if content_type in ('image/png', 'image/webp'): + if content_type != 'image/jpg': if hasattr(img, 'stream'): - imgc = PILImage.open(img.stream) + imgc = Image(blob=img.stream) else: - imgc = PILImage.open(io.BytesIO(img.content)) - im = imgc.convert('RGB') - tmp_bytesio = io.BytesIO() - im.save(tmp_bytesio, format='JPEG') - img._content = tmp_bytesio.getvalue() + imgc = Image(blob=io.BytesIO(img.content)) + imgc.format = 'jpeg' + imgc.transform_colorspace("rgb") + img = imgc else: if content_type not in 'image/jpeg': log.error("Only jpg/jpeg files are supported as coverfile") diff --git a/cps/uploader.py b/cps/uploader.py index 2cb982b9..4612791b 100644 --- a/cps/uploader.py +++ b/cps/uploader.py @@ -66,14 +66,6 @@ except ImportError as e: log.debug('Cannot import fb2, extracting fb2 metadata will not work: %s', e) 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): meta = None @@ -179,10 +171,6 @@ def get_versions(): XVersion = 'v'+'.'.join(map(str, lxmlversion)) else: XVersion = u'not installed' - if use_PIL: - PILVersion = 'v' + PILversion - else: - PILVersion = u'not installed' if comic.use_comic_meta: ComicVersion = comic.comic_version or u'installed' else: @@ -191,7 +179,7 @@ def get_versions(): 'PyPdf': PVersion, 'lxml':XVersion, 'Wand': WVersion, - 'Pillow': PILVersion, + # 'Pillow': PILVersion, 'Comic_API': ComicVersion} diff --git a/optional-requirements.txt b/optional-requirements.txt index 0d670875..60dc726f 100644 --- a/optional-requirements.txt +++ b/optional-requirements.txt @@ -26,7 +26,6 @@ SQLAlchemy-Utils>=0.33.5,<0.37.0 # extracting metadata lxml>=3.8.0,<4.6.0 -Pillow>=4.0.0,<7.2.0 rarfile>=2.7 # other