From e0483882136e139304faa7dcce61cd08f82e3b15 Mon Sep 17 00:00:00 2001 From: Jef LeCompte Date: Fri, 3 Jul 2020 12:19:11 -0400 Subject: [PATCH 1/5] feat(api): include external port option Signed-off-by: Jef LeCompte --- cps/admin.py | 1 + cps/config_sql.py | 1 + cps/kobo.py | 14 ++++++-------- cps/templates/admin.html | 4 ++++ cps/templates/config_edit.html | 4 ++++ 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/cps/admin.py b/cps/admin.py index 4fe027a2..cc4aacfb 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -623,6 +623,7 @@ def _configuration_update_helper(): gdriveError = _configuration_gdrive_helper(to_save) reboot_required |= _config_int(to_save, "config_port") + reboot_required |= _config_int(to_save, "config_external_port") reboot_required |= _config_string(to_save, "config_keyfile") if config.config_keyfile and not os.path.isfile(config.config_keyfile): diff --git a/cps/config_sql.py b/cps/config_sql.py index 1135516d..9c3a766b 100644 --- a/cps/config_sql.py +++ b/cps/config_sql.py @@ -57,6 +57,7 @@ class _Settings(_Base): config_calibre_dir = Column(String) config_port = Column(Integer, default=constants.DEFAULT_PORT) + config_external_port = Column(Integer, default=constants.DEFAULT_PORT) config_certfile = Column(String) config_keyfile = Column(String) diff --git a/cps/kobo.py b/cps/kobo.py index 97d55db0..573d9be3 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -267,14 +267,15 @@ def HandleMetadataRequest(book_uuid): def get_download_url_for_book(book, book_format): if not current_app.wsgi_app.is_proxied: - if ':' in request.host and not request.host.endswith(']') : + if ':' in request.host and not request.host.endswith(']'): host = "".join(request.host.split(':')[:-1]) else: host = request.host + return "{url_scheme}://{url_base}:{url_port}/download/{book_id}/{book_format}".format( url_scheme=request.scheme, url_base=host, - url_port=config.config_port, + url_port=config.config_external_port, book_id=book.id, book_format=book_format.lower() ) @@ -925,7 +926,7 @@ def HandleInitRequest(): calibre_web_url = "{url_scheme}://{url_base}:{url_port}".format( url_scheme=request.scheme, url_base=host, - url_port=config.config_port + url_port=config.config_external_port ) kobo_resources["image_host"] = calibre_web_url kobo_resources["image_url_quality_template"] = unquote(calibre_web_url + @@ -935,16 +936,14 @@ def HandleInitRequest(): width="{width}", height="{height}", Quality='{Quality}', - isGreyscale='isGreyscale' - )) + isGreyscale='isGreyscale')) kobo_resources["image_url_template"] = unquote(calibre_web_url + url_for("kobo.HandleCoverImageRequest", auth_token=kobo_auth.get_auth_token(), book_uuid="{ImageId}", width="{width}", height="{height}", - isGreyscale='false' - )) + isGreyscale='false')) else: kobo_resources["image_host"] = url_for("web.index", _external=True).strip("/") kobo_resources["image_url_quality_template"] = unquote(url_for("kobo.HandleCoverImageRequest", @@ -963,7 +962,6 @@ def HandleInitRequest(): isGreyscale='false', _external=True)) - response = make_response(jsonify({"Resources": kobo_resources})) response.headers["x-kobo-apitoken"] = "e30=" diff --git a/cps/templates/admin.html b/cps/templates/admin.html index a21fae48..d22c699b 100644 --- a/cps/templates/admin.html +++ b/cps/templates/admin.html @@ -88,6 +88,10 @@
{{_('Port')}}
{{config.config_port}}
+
+
{{_('External Port')}}
+
{{config.config_external_port}}
+
diff --git a/cps/templates/config_edit.html b/cps/templates/config_edit.html index 77a60c1b..251545d8 100644 --- a/cps/templates/config_edit.html +++ b/cps/templates/config_edit.html @@ -90,6 +90,10 @@
+
+ + +
From 969105b205b6ca382c0e6a2d7e030769f3cc338a Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Sun, 9 Aug 2020 01:08:00 -0400 Subject: [PATCH 2/5] Trim whitespace from filename This avoids an OSError when the book's metadata has whitespace at the end of it. --- cps/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/helper.py b/cps/helper.py index 681719a9..3e9f47c3 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -254,7 +254,7 @@ def get_valid_filename(value, replace_whitespace=True): value = re.sub(r'[\*\+:\\\"/<>\?]+', u'_', value, flags=re.U) # pipe has to be replaced with comma value = re.sub(r'[\|]+', u',', value, flags=re.U) - value = value[:128] + value = value[:128].strip() if not value: raise ValueError("Filename cannot be empty") if sys.version_info.major == 3: From 4ee5dcaff30fd0c3780ecb545088d6ac6ef44f7b Mon Sep 17 00:00:00 2001 From: Brandon Ingli Date: Mon, 17 Aug 2020 11:29:10 -0500 Subject: [PATCH 3/5] Elevate Log Messages in web.py Elevates login failure log messages and register failure log messages from INFO to WARNING, "Unknown Error" to ERROR, and fixes misspelling of "address" in web.py log messages. Misspelling remains in variable names to avoid breaking changes. --- cps/web.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cps/web.py b/cps/web.py index 7e39054d..512ef319 100644 --- a/cps/web.py +++ b/cps/web.py @@ -1362,7 +1362,7 @@ def register(): return render_title_template('register.html', title=_(u"register"), page="register") else: flash(_(u"Your e-mail is not allowed to register"), category="error") - log.info('Registering failed for user "%s" e-mail adress: %s', to_save['nickname'], to_save["email"]) + log.warning('Registering failed for user "%s" e-mail address: %s', to_save['nickname'], to_save["email"]) return render_title_template('register.html', title=_(u"register"), page="register") flash(_(u"Confirmation e-mail was send to your e-mail account."), category="success") return redirect(url_for('web.login')) @@ -1410,7 +1410,7 @@ def login(): flash(_(u"Could not login: %(message)s", message=error), category="error") else: ipAdress = request.headers.get('X-Forwarded-For', request.remote_addr) - log.info('LDAP Login failed for user "%s" IP-adress: %s', form['username'], ipAdress) + log.warning('LDAP Login failed for user "%s" IP-address: %s', form['username'], ipAdress) flash(_(u"Wrong Username or Password"), category="error") else: ipAdress = request.headers.get('X-Forwarded-For', request.remote_addr) @@ -1419,13 +1419,13 @@ def login(): ret, __ = reset_password(user.id) if ret == 1: flash(_(u"New Password was send to your email address"), category="info") - log.info('Password reset for user "%s" IP-adress: %s', form['username'], ipAdress) + log.info('Password reset for user "%s" IP-address: %s', form['username'], ipAdress) else: - log.info(u"An unknown error occurred. Please try again later") + log.error(u"An unknown error occurred. Please try again later") flash(_(u"An unknown error occurred. Please try again later."), category="error") else: flash(_(u"Please enter valid username to reset password"), category="error") - log.info('Username missing for password reset IP-adress: %s', ipAdress) + log.warning('Username missing for password reset IP-address: %s', ipAdress) else: if user and check_password_hash(str(user.password), form['password']) and user.nickname != "Guest": login_user(user, remember=bool(form.get('remember_me'))) @@ -1434,7 +1434,7 @@ def login(): config.config_is_initial = False return redirect_back(url_for("web.index")) else: - log.info('Login failed for user "%s" IP-adress: %s', form['username'], ipAdress) + log.warning('Login failed for user "%s" IP-address: %s', form['username'], ipAdress) flash(_(u"Wrong Username or Password"), category="error") next_url = request.args.get('next', default=url_for("web.index"), type=str) From a3ae97a5a31b8a1dde29a64b886dce55771fd829 Mon Sep 17 00:00:00 2001 From: OzzieIsaacs Date: Sat, 22 Aug 2020 15:37:40 +0200 Subject: [PATCH 4/5] Fix #1574 (author name not shown in change book order in shelfs) --- cps/templates/shelf_order.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/templates/shelf_order.html b/cps/templates/shelf_order.html index 84fb0208..005f663c 100644 --- a/cps/templates/shelf_order.html +++ b/cps/templates/shelf_order.html @@ -17,7 +17,7 @@ {{entry['series_index']}} - {{entry['series'][0].name}} {% endif %}
- {% for author in entry['authors'] %} + {% for author in entry['author'] %} {{author.name.replace('|',',')}} {% if not loop.last %} & From 45ff9394f229aa9795f930d91870efed84dd6e6a Mon Sep 17 00:00:00 2001 From: OzzieIsaacs Date: Sun, 23 Aug 2020 09:44:42 +0200 Subject: [PATCH 5/5] Fix comma separated author names during for upload --- cps/epub.py | 5 +++-- cps/helper.py | 56 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/cps/epub.py b/cps/epub.py index f863db61..bdba0607 100644 --- a/cps/epub.py +++ b/cps/epub.py @@ -22,6 +22,7 @@ import zipfile from lxml import etree from . import isoLanguages +from .helper import split_authors from .constants import BookMeta @@ -64,9 +65,9 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension): tmp = p.xpath('dc:%s/text()' % s, namespaces=ns) if len(tmp) > 0: if s == 'creator': - epub_metadata[s] = ' & '.join(p.xpath('dc:%s/text()' % s, namespaces=ns)) + epub_metadata[s] = ' & '.join(split_authors(p.xpath('dc:%s/text()' % s, namespaces=ns))) elif s == 'subject': - epub_metadata[s] = ', '.join(p.xpath('dc:%s/text()' % s, namespaces=ns)) + epub_metadata[s] = ', '.join(p.xpath('dc:%s/text()' % s, namespaces=ns)) else: epub_metadata[s] = p.xpath('dc:%s/text()' % s, namespaces=ns)[0] else: diff --git a/cps/helper.py b/cps/helper.py index 681719a9..a931f970 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -21,7 +21,6 @@ from __future__ import division, print_function, unicode_literals import sys import os import io -import json import mimetypes import re import shutil @@ -36,7 +35,7 @@ from babel.units import format_unit from flask import send_from_directory, make_response, redirect, abort from flask_babel import gettext as _ from flask_login import current_user -from sqlalchemy.sql.expression import true, false, and_, or_, text, func +from sqlalchemy.sql.expression import true, false, and_, text, func from werkzeug.datastructures import Headers from werkzeug.security import generate_password_hash from . import calibre_db @@ -59,10 +58,9 @@ try: except ImportError: use_PIL = False -from . import logger, config, get_locale, db, ub, isoLanguages, worker +from . import logger, config, get_locale, db, ub, worker from . import gdriveutils as gd from .constants import STATIC_DIR as _STATIC_DIR -from .pagination import Pagination from .subproc_wrapper import process_wait from .worker import STAT_WAITING, STAT_FAIL, STAT_STARTED, STAT_FINISH_SUCCESS from .worker import TASK_EMAIL, TASK_CONVERT, TASK_UPLOAD, TASK_CONVERT_ANY @@ -100,10 +98,10 @@ def convert_book_format(book_id, calibrepath, old_book_format, new_book_format, # text = _(u"%(format)s: %(book)s", format=new_book_format, book=book.title) else: settings = dict() - text = (u"%s -> %s: %s" % (old_book_format, new_book_format, book.title)) + txt = (u"%s -> %s: %s" % (old_book_format, new_book_format, book.title)) settings['old_book_format'] = old_book_format settings['new_book_format'] = new_book_format - worker.add_convert(file_path, book.id, user_id, text, settings, kindle_mail) + worker.add_convert(file_path, book.id, user_id, txt, settings, kindle_mail) return None else: error_message = _(u"%(format)s not found: %(fn)s", @@ -263,6 +261,22 @@ def get_valid_filename(value, replace_whitespace=True): return value.decode('utf-8') +def split_authors(values): + authors_list = [] + for value in values: + authors = re.split('[&;]', value) + for author in authors: + commas = author.count(',') + if commas == 1: + author_split = author.split(',') + authors_list.append(author_split[1] + ' ' + author_split[0]) + elif commas > 1: + authors_list.append(author.split(',')) + else: + authors_list.append(author) + return authors_list + + def get_sorted_author(value): try: if ',' not in value: @@ -303,8 +317,8 @@ def delete_book_file(book, calibrepath, book_format=None): log.warning("Deleting book {} failed, path {} has subfolders: {}".format(book.id, book.path, folders)) return True, _("Deleting bookfolder for book %(id)s failed, path has subfolders: %(path)s", - id=book.id, - path=book.path) + id=book.id, + path=book.path) shutil.rmtree(path) except (IOError, OSError) as e: log.error("Deleting book %s failed: %s", book.id, e) @@ -319,8 +333,8 @@ def delete_book_file(book, calibrepath, book_format=None): else: log.error("Deleting book %s failed, book path not valid: %s", book.id, book.path) return True, _("Deleting book %(id)s, book path not valid: %(path)s", - id=book.id, - path=book.path) + id=book.id, + path=book.path) def update_dir_structure_file(book_id, calibrepath, first_author): @@ -366,6 +380,7 @@ def update_dir_structure_file(book_id, calibrepath, first_author): src=path, dest=new_author_path, error=str(ex)) # Rename all files from old names to new names if authordir != new_authordir or titledir != new_titledir: + new_name = "" try: new_name = get_valid_filename(localbook.title) + ' - ' + get_valid_filename(new_authordir) path_name = os.path.join(calibrepath, new_authordir, os.path.basename(path)) @@ -474,14 +489,14 @@ def generate_random_password(): return "".join(s[c % len(s)] for c in os.urandom(passlen)) -def uniq(input): - output = [] - for x in input: - if x not in output: - output.append(x) - return output +def uniq(inpt): + output = [] + for x in inpt: + if x not in output: + output.append(x) + return output -################################## External interface +# ################################# External interface ################################# def update_dir_stucture(book_id, calibrepath, first_author=None): @@ -558,7 +573,6 @@ def save_cover_from_url(url, book_path): 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") @@ -617,7 +631,6 @@ def save_cover(img, book_path): return save_cover_from_filestorage(os.path.join(config.config_calibre_dir, book_path), "cover.jpg", img) - def do_download_file(book, book_format, client, data, headers): if config.config_use_google_drive: startTime = time.time() @@ -718,7 +731,7 @@ def render_task_status(tasklist): task['runtime'] = format_runtime(task['formRuntime']) # localize the task status - if isinstance( task['stat'], int): + if isinstance(task['stat'], int): if task['stat'] == STAT_WAITING: task['status'] = _(u'Waiting') elif task['stat'] == STAT_FAIL: @@ -731,7 +744,7 @@ def render_task_status(tasklist): task['status'] = _(u'Unknown Status') # localize the task type - if isinstance( task['taskType'], int): + if isinstance(task['taskType'], int): if task['taskType'] == TASK_EMAIL: task['taskMessage'] = _(u'E-mail: ') + task['taskMess'] elif task['taskType'] == TASK_CONVERT: @@ -787,6 +800,7 @@ def get_cc_columns(filter_config_custom_read=False): return cc + def get_download_link(book_id, book_format, client): book_format = book_format.split(".")[0] book = calibre_db.get_filtered_book(book_id)