From ee3541d74ed372272a2008a50db39c386d40736c Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sat, 4 Jul 2020 13:35:08 +0200 Subject: [PATCH 1/9] Fix kobo links for reverse proxies with subdomains (and docker?) #1470 --- cps/kobo.py | 4 +--- cps/reverseproxy.py | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cps/kobo.py b/cps/kobo.py index 97d55db0..69708d0c 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -19,8 +19,6 @@ import base64 import datetime -import itertools -import json import sys import os import uuid @@ -267,7 +265,7 @@ 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 diff --git a/cps/reverseproxy.py b/cps/reverseproxy.py index 42b64050..3bcbd3b7 100644 --- a/cps/reverseproxy.py +++ b/cps/reverseproxy.py @@ -77,6 +77,7 @@ class ReverseProxied(object): servr = environ.get('HTTP_X_FORWARDED_HOST', '') if servr: environ['HTTP_HOST'] = servr + self.proxied = True return self.app(environ, start_response) @property From ee6f1405d4ef2f0af2330c19173b4b5a5a736cdf Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sat, 4 Jul 2020 21:22:19 +0200 Subject: [PATCH 2/9] Fix #1456 (Added route to robots.txt) --- cps/web.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cps/web.py b/cps/web.py index 4d59e61c..9af9858d 100644 --- a/cps/web.py +++ b/cps/web.py @@ -1256,6 +1256,9 @@ def render_archived_books(page, order): def get_cover(book_id): return get_book_cover(book_id) +@web.route("/robots.txt") +def get_robots(): + return send_from_directory(constants.STATIC_DIR, "robots.txt") @web.route("/show//", defaults={'anyname': 'None'}) @web.route("/show///") From 20c6f79a4472c0986bed0409c841bfae2b66d1dc Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sun, 5 Jul 2020 10:44:49 +0200 Subject: [PATCH 3/9] Changed behavior delete books with subfolders (additional warning message, but book is deleted) --- cps/helper.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cps/helper.py b/cps/helper.py index 48efc69b..20f3d4ad 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -295,15 +295,16 @@ def delete_book_file(book, calibrepath, book_format=None): return True, None else: if os.path.isdir(path): - if len(next(os.walk(path))[1]): - log.error("Deleting book %s failed, path has subfolders: %s", book.id, book.path) - return False , _("Deleting book %(id)s failed, path has subfolders: %(path)s", - id=book.id, - path=book.path) try: - for root, __, files in os.walk(path): + for root, folders, files in os.walk(path): for f in files: os.unlink(os.path.join(root, f)) + if len(folders): + 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) shutil.rmtree(path) except (IOError, OSError) as e: log.error("Deleting book %s failed: %s", book.id, e) From c1f5252b3ff7d2f7acf0b57028c346c2f7175896 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sun, 5 Jul 2020 13:40:33 +0200 Subject: [PATCH 4/9] Fix #1509 (OSError thrown during sync on Windows if one of the timestamps is outside range 1970 to 2038) --- cps/services/SyncToken.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cps/services/SyncToken.py b/cps/services/SyncToken.py index 1dd4f084..9790150c 100644 --- a/cps/services/SyncToken.py +++ b/cps/services/SyncToken.py @@ -45,7 +45,8 @@ def to_epoch_timestamp(datetime_object): def get_datetime_from_json(json_object, field_name): try: return datetime.utcfromtimestamp(json_object[field_name]) - except KeyError: + except (KeyError, OSError): + # OSError is thrown on Windows if timestamp is <1970 or >2038 return datetime.min From 76f914cbc2819e60bd42df1655fde8a17215c3d0 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sun, 5 Jul 2020 13:52:29 +0200 Subject: [PATCH 5/9] Fixed logging in SyncToken --- cps/services/SyncToken.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cps/services/SyncToken.py b/cps/services/SyncToken.py index 9790150c..2fe7fe95 100644 --- a/cps/services/SyncToken.py +++ b/cps/services/SyncToken.py @@ -27,7 +27,10 @@ except ImportError: from urllib.parse import unquote from flask import json -from .. import logger as log +from .. import logger + + +log = logger.create() def b64encode_json(json_data): From 12263ff02f931a0d33bf4073e6cbae50940346b1 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sun, 5 Jul 2020 13:55:59 +0200 Subject: [PATCH 6/9] Fix #1423 (OverflowError thrown during sync on some Linux Systems if one of the timestamps is outside range 1970 to 2038) --- cps/services/SyncToken.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/services/SyncToken.py b/cps/services/SyncToken.py index 2fe7fe95..f6db960b 100644 --- a/cps/services/SyncToken.py +++ b/cps/services/SyncToken.py @@ -48,7 +48,7 @@ def to_epoch_timestamp(datetime_object): def get_datetime_from_json(json_object, field_name): try: return datetime.utcfromtimestamp(json_object[field_name]) - except (KeyError, OSError): + except (KeyError, OSError, OverflowError): # OSError is thrown on Windows if timestamp is <1970 or >2038 return datetime.min From d1889a5e06ee690bb356a33e0fc8152d51b22b73 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sun, 5 Jul 2020 14:00:40 +0200 Subject: [PATCH 7/9] Fix #1502 (program info only visible in about section if user is admin) --- cps/templates/stats.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cps/templates/stats.html b/cps/templates/stats.html index 69712cc4..966abf2a 100644 --- a/cps/templates/stats.html +++ b/cps/templates/stats.html @@ -25,6 +25,7 @@ +{% if g.user.role_admin() %}

{{_('Linked Libraries')}}

@@ -44,4 +45,5 @@ {% endfor %}
+{% endif %} {% endblock %} From f80c67828bbd70ee482ce4178bfaffffb1f415ef Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sun, 5 Jul 2020 14:35:57 +0200 Subject: [PATCH 8/9] Fix #1500 (Custom ratings of increment 0.5 are allowed) --- cps/templates/book_edit.html | 4 ++-- cps/templates/detail.html | 2 +- cps/templates/search_form.html | 2 +- cps/web.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cps/templates/book_edit.html b/cps/templates/book_edit.html index 14bc590a..8bbe2460 100644 --- a/cps/templates/book_edit.html +++ b/cps/templates/book_edit.html @@ -159,9 +159,9 @@ {% endif %} {% if c.datatype == 'rating' %} - 0 %} - value="{{ '%d' % (book['custom_column_' ~ c.id][0].value / 2) }}" + value="{{ '%.1f' % (book['custom_column_' ~ c.id][0].value / 2) }}" {% endif %}> {% endif %} diff --git a/cps/templates/detail.html b/cps/templates/detail.html index a87695eb..d4b4252c 100644 --- a/cps/templates/detail.html +++ b/cps/templates/detail.html @@ -174,7 +174,7 @@ {{ c.name }}: {% for column in entry['custom_column_' ~ c.id] %} {% if c.datatype == 'rating' %} - {{ '%d' % (column.value / 2) }} + {{ '%d' % (column.value / 2) }}{% if ((column.value /2) % 1) != 0 %}{{ '.%d' % (((column.value /2) % 1)*10) }} {% endif %} {% else %} {% if c.datatype == 'bool' %} {% if column.value == true %} diff --git a/cps/templates/search_form.html b/cps/templates/search_form.html index 60cffa1d..fdc2990b 100644 --- a/cps/templates/search_form.html +++ b/cps/templates/search_form.html @@ -165,7 +165,7 @@ {% endif %} {% if c.datatype == 'rating' %} - + {% endif %} {% endfor %} diff --git a/cps/web.py b/cps/web.py index 9af9858d..7e39054d 100644 --- a/cps/web.py +++ b/cps/web.py @@ -1142,7 +1142,7 @@ def advanced_search(): db.cc_classes[c.id].value == custom_query)) elif c.datatype == 'rating': q = q.filter(getattr(db.Books, 'custom_column_' + str(c.id)).any( - db.cc_classes[c.id].value == int(custom_query) * 2)) + db.cc_classes[c.id].value == int(float(custom_query) * 2))) else: q = q.filter(getattr(db.Books, 'custom_column_' + str(c.id)).any( func.lower(db.cc_classes[c.id].value).ilike("%" + custom_query + "%"))) From 450411a732c86e392523cb020303a624f2e61e6b Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sun, 5 Jul 2020 20:54:36 +0200 Subject: [PATCH 9/9] #1344 (Support Multiple authors, but not showing up on Kobo reader) Fix for #1439 (reading progress was not stored, as user login was wrong) --- cps/kobo.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/cps/kobo.py b/cps/kobo.py index 69708d0c..70c4b45a 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -315,8 +315,15 @@ def get_description(book): # TODO handle multiple authors def get_author(book): if not book.authors: - return None - return book.authors[0].name + return {"Contributors": None} + if len(book.authors) > 1: + author_list = [] + autor_roles = [] + for author in book.authors: + autor_roles.append({"Name":author.name, "Role":"Author"}) + author_list.append(author.name) + return {"ContributorRoles": autor_roles, "Contributors":author_list} + return {"ContributorRoles": [{"Name":book.authors[0].name, "Role":"Author"}], "Contributors": book.authors[0].name} def get_publisher(book): @@ -355,7 +362,7 @@ def get_metadata(book): book_uuid = book.uuid metadata = { "Categories": ["00000000-0000-0000-0000-000000000001",], - "Contributors": get_author(book), + # "Contributors": get_author(book), "CoverImageId": book_uuid, "CrossRevisionId": book_uuid, "CurrentDisplayPrice": {"CurrencyCode": "USD", "TotalAmount": 0}, @@ -379,6 +386,7 @@ def get_metadata(book): "Title": book.title, "WorkId": book_uuid, } + metadata.update(get_author(book)) if get_series(book): if sys.version_info < (3, 0): @@ -397,7 +405,7 @@ def get_metadata(book): @kobo.route("/v1/library/tags", methods=["POST", "DELETE"]) -@login_required +@requires_kobo_auth # Creates a Shelf with the given items, and returns the shelf's uuid. def HandleTagCreate(): # catch delete requests, otherwise the are handeld in the book delete handler @@ -432,6 +440,7 @@ def HandleTagCreate(): @kobo.route("/v1/library/tags/", methods=["DELETE", "PUT"]) +@requires_kobo_auth def HandleTagUpdate(tag_id): shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.uuid == tag_id, ub.Shelf.user_id == current_user.id).one_or_none() @@ -486,7 +495,7 @@ def add_items_to_shelf(items, shelf): @kobo.route("/v1/library/tags//items", methods=["POST"]) -@login_required +@requires_kobo_auth def HandleTagAddItem(tag_id): items = None try: @@ -516,7 +525,7 @@ def HandleTagAddItem(tag_id): @kobo.route("/v1/library/tags//items/delete", methods=["POST"]) -@login_required +@requires_kobo_auth def HandleTagRemoveItem(tag_id): items = None try: @@ -625,7 +634,7 @@ def create_kobo_tag(shelf): @kobo.route("/v1/library//state", methods=["GET", "PUT"]) -@login_required +@requires_kobo_auth def HandleStateRequest(book_uuid): book = calibre_db.get_book_by_uuid(book_uuid) if not book or not book.data: @@ -799,7 +808,7 @@ def TopLevelEndpoint(): @kobo.route("/v1/library/", methods=["DELETE"]) -@login_required +@requires_kobo_auth def HandleBookDeletionRequest(book_uuid): log.info("Kobo book deletion request received for book %s" % book_uuid) book = calibre_db.get_book_by_uuid(book_uuid)