Fix Nextory: bump X-App-Version, null series, missing urllib3 dep (#25)

* Fix Nextory: bump X-App-Version and handle null series

The Nextory API now rejects the old X-App-Version "5.4.1" with an
AppDeprecateError. Bump it to "2026.05.4" (current Android app version).

Also, the product API can return {"series": null} for standalone books
(not just omit the key entirely). The previous `"series" in product_info`
check passed in that case and we crashed on a NoneType subscript.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Add urllib3 as explicit dependency

grawlix/sources/storytel.py imports urllib3.util.parse_url but urllib3
was only pulled in transitively. Fresh installs (e.g. via uv tool) fail
at import time with ModuleNotFoundError.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Fix invalid escape sequence warning in saxo regex

f"\d+$" produced a SyntaxWarning on Python 3.12 (and will become a
SyntaxError in a future release). The string has no interpolation, so
the raw form r"\d+$" is the right fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
s0len 2026-06-03 09:50:17 +02:00 committed by GitHub
parent 3267398929
commit 1e6e5f161c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 6 additions and 4 deletions

View File

@ -30,7 +30,7 @@ class Nextory(Source):
self._client.headers.update( self._client.headers.update(
{ {
"X-Application-Id": "200", "X-Application-Id": "200",
"X-App-Version": "5.4.1", "X-App-Version": "2026.05.4",
"X-Locale": LOCALE, "X-Locale": LOCALE,
"X-Model": "Personal Computer", "X-Model": "Personal Computer",
"X-Device-Id": device_id, "X-Device-Id": device_id,
@ -139,9 +139,10 @@ class Nextory(Source):
@staticmethod @staticmethod
def _extract_series_name(product_info: dict) -> Optional[str]: def _extract_series_name(product_info: dict) -> Optional[str]:
if not "series" in product_info: series = product_info.get("series")
if not series:
return None return None
return product_info["series"]["name"] return series["name"]
async def _get_book_id_from_url_id(self, url_id: str) -> str: async def _get_book_id_from_url_id(self, url_id: str) -> str:

View File

@ -122,7 +122,7 @@ class Saxo(Source):
:param url: Url of book :param url: Url of book
:returns: Isbn of book :returns: Isbn of book
""" """
isbn_match = re.search(f"\d+$", url) isbn_match = re.search(r"\d+$", url)
if isbn_match and isbn_match.group(): if isbn_match and isbn_match.group():
return isbn_match.group() return isbn_match.group()
raise NotImplemented raise NotImplemented

View File

@ -21,6 +21,7 @@ dependencies = [
"pycryptodome", "pycryptodome",
"rich", "rich",
"tomli", "tomli",
"urllib3",
] ]
dynamic = ["version"] dynamic = ["version"]