From 47625a616d3f02387be242e671d8668bfe402a4a Mon Sep 17 00:00:00 2001 From: AsukaVuuyn <1346007099@qq.com> Date: Fri, 20 Feb 2026 21:45:49 +0800 Subject: [PATCH 1/4] fix: support native UTF-8 downloaded filenames for browsers --- cps/helper.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cps/helper.py b/cps/helper.py index 8cd7b4518..f85e6fa17 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -1091,8 +1091,13 @@ def get_download_link(book_id, book_format, client): file_name = book.title if len(book.authors) > 0: file_name = file_name + ' - ' + book.authors[0].name + original_name = file_name file_name = get_valid_filename(file_name, replace_whitespace=False, force_unidecode=True) - quoted_file_name = file_name if client == "kindle" else quote(file_name) + if client == "kindle": + quoted_file_name = file_name + else: + native_name = get_valid_filename(original_name, replace_whitespace=False, force_unidecode=False) + quoted_file_name = quote(native_name) headers = Headers() headers["Content-Type"] = mimetypes.types_map.get('.' + book_format, "application/octet-stream") headers["Content-Disposition"] = ('attachment; filename="{}.{}"; filename*=UTF-8\'\'{}.{}').format( From 2d4ca23d0c609fda516dd35cb428597f51d21049 Mon Sep 17 00:00:00 2001 From: jarynclouatre Date: Sun, 1 Mar 2026 18:19:40 -0600 Subject: [PATCH 2/4] Kobo: include PriorityTimestamp in PUT /state response Kobo: include PriorityTimestamp in PUT /state response --- cps/kobo.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cps/kobo.py b/cps/kobo.py index aa043503b..7aaa587e3 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -811,6 +811,8 @@ def HandleStateRequest(book_uuid): ub.session.merge(kobo_reading_state) ub.session_commit() + update_results_response["LastModified"] = convert_to_kobo_timestamp_string(kobo_reading_state.last_modified) + update_results_response["PriorityTimestamp"] = convert_to_kobo_timestamp_string(kobo_reading_state.priority_timestamp) return jsonify({ "RequestResult": "Success", "UpdateResults": [update_results_response], From 1134f54be496e1388401aaa088b6351d86572b9f Mon Sep 17 00:00:00 2001 From: jarynclouatre Date: Sun, 1 Mar 2026 20:44:05 -0600 Subject: [PATCH 3/4] fix: series list view sorts by name instead of sort field In series_list(), the SQLite query correctly orders results by Series.sort, but a subsequent Python sorted() call (needed to re-order after appending the "None" category entry) was using Series.name as the sort key instead of Series.sort. This caused series titles with leading articles (A, An, The) to sort strictly alphabetically by the article rather than by the meaningful word, e.g. "A Collins-Burke Mystery" appeared under "A" instead of "C". Fix by using Series.sort (with a fallback to Series.name if sort is NULL) as the key in the Python re-sort, consistent with the intent of the existing DB query. Fixes #3583 --- cps/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/web.py b/cps/web.py index 961e1df7f..99fb362a2 100644 --- a/cps/web.py +++ b/cps/web.py @@ -1026,7 +1026,7 @@ def series_list(): .count()) if no_series_count: entries.append([db.Category(_("None"), "-1"), no_series_count]) - entries = sorted(entries, key=lambda x: x[0].name.lower(), reverse=not order_no) + entries = sorted(entries, key=lambda x: (x[0].sort or x[0].name).lower(), reverse=not order_no) return render_title_template('list.html', entries=entries, folder='web.books_list', From 7c715f34dc841b131f9f8e1716d259e9f9b84f66 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Fri, 3 Apr 2026 09:01:01 +0200 Subject: [PATCH 4/4] Clean strings in comments before displaying --- cps/clean_html.py | 2 +- cps/jinjia.py | 7 +++++++ cps/templates/basic_detail.html | 2 +- cps/templates/detail.html | 2 +- cps/templates/listenmp3.html | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/cps/clean_html.py b/cps/clean_html.py index c687f1542..3436f43a5 100644 --- a/cps/clean_html.py +++ b/cps/clean_html.py @@ -35,7 +35,7 @@ def clean_string(unsafe_text, book_id=0): try: if bleach: allowed_tags = list(ALLOWED_TAGS) - allowed_tags.extend(["p", "span", "div", "pre", "br", "h1", "h2", "h3", "h4", "h5", "h6"]) + allowed_tags.extend(["p", "span", "div", "pre", "br", "h1", "h2", "h3", "h4", "h5", "h6", "img"]) safe_text = clean_html(unsafe_text, tags=set(allowed_tags)) else: safe_text = clean_html(unsafe_text) diff --git a/cps/jinjia.py b/cps/jinjia.py index cb51b2bc0..0b30f3b80 100644 --- a/cps/jinjia.py +++ b/cps/jinjia.py @@ -30,6 +30,7 @@ from uuid import uuid4 from flask import Blueprint, request, url_for, g from flask_babel import format_date from .cw_login import current_user +from .clean_html import clean_string as html_clean_string from . import constants, logger @@ -181,3 +182,9 @@ def contains_music(book_formats): if format.format.lower() in g.constants.EXTENSIONS_AUDIO: result = True return result + +@jinjia.app_template_filter('clean_string') +def clean_string(unsafe_text): + return html_clean_string(unsafe_text) + + diff --git a/cps/templates/basic_detail.html b/cps/templates/basic_detail.html index 1996497ba..dc8242536 100644 --- a/cps/templates/basic_detail.html +++ b/cps/templates/basic_detail.html @@ -74,7 +74,7 @@ {% if entry.comments|length > 0 and entry.comments[0].text|length > 0 %}

{{ _('Description:') }}

- {{ entry.comments[0].text|safe }} + {{ entry.comments[0].text|clean_string|safe }}
{% endif %} diff --git a/cps/templates/detail.html b/cps/templates/detail.html index 69ec460f7..164ac424d 100644 --- a/cps/templates/detail.html +++ b/cps/templates/detail.html @@ -284,7 +284,7 @@ {% if entry.comments|length > 0 and entry.comments[0].text|length > 0 %}

{{ _('Description:') }}

- {{ entry.comments[0].text|safe }} + {{ entry.comments[0].text|clean_string|safe }}
{% endif %} diff --git a/cps/templates/listenmp3.html b/cps/templates/listenmp3.html index 4ba6931e8..375a871b4 100644 --- a/cps/templates/listenmp3.html +++ b/cps/templates/listenmp3.html @@ -175,7 +175,7 @@ {% if entry.comments|length > 0 and entry.comments[0].text|length > 0%}

{{_('Description:')}}

- {{entry.comments[0].text|safe}} + {{entry.comments[0].text|clean_string|safe}}
{% endif %}