The `serve_book` function uses `get_book()` which performs no access filtering:
it simply fetches by ID. Compare with `read_book` at web.py:1562 which
correctly uses `get_filtered_book()`. The `common_filters()` function enforces
per-user tag restrictions, language restrictions, and hidden-book rules.
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
request_username() is used as flask-limiter's key_func for the OPDS
blueprint. The limiter evaluates key_func in a before_request handler,
before the route's auth decorator runs. When no Authorization header is
present, request.authorization is None, causing an AttributeError and
a 500 response instead of the expected 401.
Guard against None so unauthenticated requests fall back to an empty
string key, allowing the auth decorator to handle the 401 correctly.
Fixes#3592
Disclaimer: AI assisted—humans supervised.
The HandleInitRequest() function rewrites image URLs to point to the local
Calibre-Web instance, but library_sync was left pointing to storeapi.kobo.com.
This caused Kobo e-readers to call Kobo's servers for sync instead of the local
server, resulting in no books being synced when running behind a reverse proxy.
Remove Get Parameter sort_param, where it is not valid (#3447)
Generate valid session-cookie-path(s) (fix for #3459)
Mass remove books from shelf (Fix for #3520)