Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-audible for openSUSE:Factory
checked in at 2026-10-26 10:00:21
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-audible (Old)
and /work/SRC/openSUSE:Factory/.python-audible.new.2252680 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-audible"
Mon Oct 26 10:00:21 2026 rev:4 rq:1392393 version:0.13.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-audible/python-audible.changes
2026-08-03 18:02:14.065235035 +0200
+++
/work/SRC/openSUSE:Factory/.python-audible.new.2252680/python-audible.changes
2026-10-26 10:01:11.620711028 +0100
@@ -1,0 +2,11 @@
+Sun Oct 25 11:48:42 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 0.13.1:
+ * auth: Register new iPhone devices with the Audible 4.60
+ profile
+- update to 0.13.0:
+ * Accept PKCS#8 device private keys from Android profiles
+ * deps: Update dependency cryptography to v50 [security]
+ * deps: Declare pyasn1 as a runtime dependency
+
+-------------------------------------------------------------------
Old:
----
audible-0.12.0.tar.gz
New:
----
audible-0.13.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-audible.spec ++++++
--- /var/tmp/diff_new_pack.8uqtzn/_old 2026-10-26 10:01:12.326740649 +0100
+++ /var/tmp/diff_new_pack.8uqtzn/_new 2026-10-26 10:01:12.332740901 +0100
@@ -18,7 +18,7 @@
%{?sle15_python_module_pythons}
Name: python-audible
-Version: 0.12.0
+Version: 0.13.1
Release: 0
Summary: A(Sync) Interface for internal Audible API
License: AGPL-3.0-only
++++++ audible-0.12.0.tar.gz -> audible-0.13.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/audible-0.12.0/PKG-INFO new/audible-0.13.1/PKG-INFO
--- old/audible-0.12.0/PKG-INFO 2026-07-23 08:02:55.000000000 +0200
+++ new/audible-0.13.1/PKG-INFO 2026-10-24 10:08:28.000000000 +0200
@@ -1,6 +1,6 @@
-Metadata-Version: 2.4
+Metadata-Version: 2.5
Name: audible
-Version: 0.12.0
+Version: 0.13.1
Summary: A(Sync) Interface for internal Audible API written in pure Python.
Project-URL: Changelog, https://github.com/mkb79/Audible/releases
Author-email: mkb79 <[email protected]>
@@ -20,9 +20,10 @@
Requires-Dist: pbkdf2>=1.3
Requires-Dist: pillow>=12.3.0
Requires-Dist: pyaes>=1.6.1
+Requires-Dist: pyasn1>=0.6.1
Requires-Dist: rsa>=4.9.1
Provides-Extra: cryptography
-Requires-Dist: cryptography>=48.0.1; extra == 'cryptography'
+Requires-Dist: cryptography>=50.0.0; extra == 'cryptography'
Provides-Extra: json-fast
Requires-Dist: orjson>=3.11.6; extra == 'json-fast'
Provides-Extra: json-full
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/audible-0.12.0/pyproject.toml
new/audible-0.13.1/pyproject.toml
--- old/audible-0.12.0/pyproject.toml 2026-07-23 08:02:55.000000000 +0200
+++ new/audible-0.13.1/pyproject.toml 2026-10-24 10:08:28.000000000 +0200
@@ -1,6 +1,6 @@
[project]
name = "audible"
-version = "0.12.0"
+version = "0.13.1"
description = "A(Sync) Interface for internal Audible API written in pure
Python."
authors = [{ name = "mkb79", email = "[email protected]" }]
requires-python = ">=3.11,<3.15"
@@ -29,12 +29,13 @@
"httpx (>=0.27.2,<0.29)",
"pbkdf2 (>=1.3)",
"pyaes (>=1.6.1)",
+ "pyasn1 (>=0.6.1)",
"rsa (>=4.9.1)",
]
[project.optional-dependencies]
cryptography = [
- "cryptography (>=48.0.1)",
+ "cryptography (>=50.0.0)",
]
pycryptodome = [
"pycryptodome (>=3.23.0)",
@@ -66,6 +67,7 @@
]
docs = [
+ "git-cliff>=2.13.1",
"myst-parser>=4.0.1",
"sphinx>=8.2.3",
"sphinx-autobuild>=2025.8.25",
@@ -79,6 +81,7 @@
"types-beautifulsoup4>=4.12.0.20250516",
"types-docutils>=0.22.3.20251115",
"types-html5lib>=1.1.11.20251117",
+ "types-pyasn1>=0.6.0.20260724",
"types-pygments>=2.19.0.20251121",
]
@@ -87,7 +90,7 @@
"nox>=2025.11.12",
"pre-commit>=4.5.1",
"pre-commit-hooks>=6.0.0",
- "ruff>=0.14.11",
+ "ruff>=0.16.0",
]
audit = [
@@ -97,7 +100,7 @@
release = [
"commitizen>=4.16.5",
"git-cliff>=2.13.1",
- "twine>=6.2.0",
+ "twine>=7.0.0",
]
tests = [
@@ -117,7 +120,7 @@
]
extra-deps = [
- "cryptography>=48.0.1",
+ "cryptography>=50.0.0",
"pycryptodome>=3.23.0",
"orjson>=3.11.6",
"ujson>=5.13.0",
@@ -208,6 +211,12 @@
module = "tests.*"
disallow_untyped_decorators = false
+# sphinx ships no type information and is not in the mypy dependency group;
+# docs/source/conf.py imports it only to type its build hooks.
+[[tool.mypy.overrides]]
+module = "sphinx.application"
+ignore_missing_imports = true
+
[tool.ruff]
# missing rst-docstrings check, these should be adding through ruff
line-length = 88
@@ -256,11 +265,11 @@
force-single-line = false
# changelog and the release helpers live in tools/ and are imported by the
# tests, so they sort with the project rather than with third-party packages.
-known-first-party = ["audible", "changelog", "prepare_release"]
+known-first-party = ["audible", "changelog", "prepare_release",
"unreleased_docs"]
lines-after-imports = 2
[tool.ruff.lint.per-file-ignores]
-"tests/*" = ["S101", "PLC0415", "S106"]
+"tests/*" = ["S101", "PLC0415", "S106", "PLR0917"]
"examples/*" = ["S101", "PLC0415"]
"src/audible/json_provider/protocols.py" = ["D417"]
"src/audible/json_provider/*_provider.py" = ["PLC0415"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/audible-0.12.0/src/audible/_device.py
new/audible-0.13.1/src/audible/_device.py
--- old/audible-0.12.0/src/audible/_device.py 1970-01-01 01:00:00.000000000
+0100
+++ new/audible-0.13.1/src/audible/_device.py 2026-10-24 10:08:28.000000000
+0200
@@ -0,0 +1,5 @@
+"""Shared version values for the default Audible for iPhone device profile."""
+
+APP_VERSION = "4.60"
+OS_VERSION = "26.2"
+SOFTWARE_VERSION = "46000856"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/audible-0.12.0/src/audible/auth.py
new/audible-0.13.1/src/audible/auth.py
--- old/audible-0.12.0/src/audible/auth.py 2026-07-23 08:02:55.000000000
+0200
+++ new/audible-0.13.1/src/audible/auth.py 2026-10-24 10:08:28.000000000
+0200
@@ -17,6 +17,7 @@
import httpx
from httpx import Cookies
+from ._device import APP_VERSION
from .activation_bytes import get_activation_bytes as get_ab
from .aescipher import AESCipher, detect_file_encryption
from .crypto_provider import get_crypto_providers
@@ -43,7 +44,7 @@
"""Build the request body for an access token refresh."""
return {
"app_name": "Audible",
- "app_version": "3.56.2",
+ "app_version": APP_VERSION,
"source_token": refresh_token,
"requested_token_type": "access_token",
"source_token_type": "refresh_token",
@@ -148,7 +149,7 @@
body = {
"app_name": "Audible",
- "app_version": "3.56.2",
+ "app_version": APP_VERSION,
"source_token": refresh_token,
"requested_token_type": "auth_cookies",
"source_token_type": "refresh_token",
@@ -220,7 +221,7 @@
return profile
-def sign_request(
+def sign_request( # noqa: PLR0917
method: str,
path: str,
body: bytes,
@@ -496,7 +497,7 @@
return auth
@classmethod
- def from_login(
+ def from_login( # noqa: PLR0917
cls,
username: str,
password: str,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/audible-0.12.0/src/audible/client.py
new/audible-0.13.1/src/audible/client.py
--- old/audible-0.12.0/src/audible/client.py 2026-07-23 08:02:55.000000000
+0200
+++ new/audible-0.13.1/src/audible/client.py 2026-10-24 10:08:28.000000000
+0200
@@ -19,7 +19,7 @@
# namespace of the module using the annotation. Without the binding, tools that
# evaluate annotations (sphinx-autodoc-typehints among them) cannot resolve it.
from httpx import URL, Headers
-from httpx._models import HeaderTypes # type: ignore[attr-defined]
+from httpx._types import HeaderTypes
from ._types import TrueFalseT
from .auth import Authenticator
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/audible-0.12.0/src/audible/crypto_provider/legacy_provider.py
new/audible-0.13.1/src/audible/crypto_provider/legacy_provider.py
--- old/audible-0.12.0/src/audible/crypto_provider/legacy_provider.py
2026-07-23 08:02:55.000000000 +0200
+++ new/audible-0.13.1/src/audible/crypto_provider/legacy_provider.py
2026-10-24 10:08:28.000000000 +0200
@@ -10,10 +10,15 @@
import hmac
import logging
import warnings
+from base64 import b64decode
from collections.abc import Callable
from hashlib import sha1, sha256
+from re import DOTALL, match
from typing import TYPE_CHECKING, Any
+from pyasn1.codec.der import decoder
+from pyasn1.type import namedtype, univ
+
if TYPE_CHECKING:
from .protocols import HashAlgorithm
@@ -30,6 +35,42 @@
logger = logging.getLogger("audible.crypto_provider.legacy")
+def _convert_pkcs8_der_rsa_to_pkcs1_der(pkcs8_der: bytes) -> bytes:
+ """Converts a PKCS#8 DER RSA key to a PKCS#1 DER key.
+
+ Args:
+ pkcs8_der: An RSA key in PKCS#8 DER format.
+
+ Returns:
+ An RSA key in PKCS#1 DER format.
+
+ Raises:
+ ValueError: If PKCS#8 algorithm OID is not RSA
+ (1.2.840.113549.1.1.1).
+ """
+
+ class PrivateKeyAlgorithm(univ.Sequence):
+ componentType = namedtype.NamedTypes( # noqa: N815
+ namedtype.NamedType("algorithm", univ.ObjectIdentifier()),
+ namedtype.NamedType("parameters", univ.Any()),
+ )
+
+ class PrivateKeyInfo(univ.Sequence):
+ componentType = namedtype.NamedTypes( # noqa: N815
+ namedtype.NamedType("version", univ.Integer()),
+ namedtype.NamedType("pkalgo", PrivateKeyAlgorithm()),
+ namedtype.NamedType("key", univ.OctetString()),
+ )
+
+ logger.debug("Converting PKCS#8 RSA key to PKCS#1 (legacy provider)")
+ (key_info, _) = decoder.decode(pkcs8_der, asn1Spec=PrivateKeyInfo())
+ rsa_oid = univ.ObjectIdentifier((1, 2, 840, 113549, 1, 1, 1))
+ if key_info["pkalgo"]["algorithm"] != rsa_oid:
+ raise ValueError("key_info.pkalgo.algorithm: Algorithm is not RSA.")
+
+ return bytes(key_info["key"])
+
+
def _load_rsa_private_key_legacy(pem_data: str) -> rsa.PrivateKey:
"""Load an RSA private key from PEM.
@@ -42,9 +83,26 @@
Returns:
A parsed rsa.PrivateKey object.
+
+ Raises:
+ ValueError: If the PEM data is in an incorrect format or if key type
is not
+ 'PRIVATE KEY' or 'RSA PRIVATE KEY'.
"""
+ pem_fmt = (
+ r"^-----BEGIN (?P<private_key_type>(?:RSA )?PRIVATE KEY)-----"
+ r"(?P<der_b64_str>.*)-----END \1-----\n$"
+ )
+ key_match = match(pem_fmt, pem_data, DOTALL)
+ if not key_match:
+ raise ValueError("pem_data: Invalid token.")
+
+ private_key_type = key_match.group("private_key_type")
+ private_key_der = b64decode(key_match.group("der_b64_str"))
+ if private_key_type == "PRIVATE KEY":
+ private_key_der = _convert_pkcs8_der_rsa_to_pkcs1_der(private_key_der)
+
logger.debug("Loading RSA private key (legacy provider)")
- return rsa.PrivateKey.load_pkcs1(pem_data.encode("utf-8"))
+ return rsa.PrivateKey.load_pkcs1(private_key_der, "DER")
class LegacyAESProvider:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/audible-0.12.0/src/audible/login.py
new/audible-0.13.1/src/audible/login.py
--- old/audible-0.12.0/src/audible/login.py 2026-07-23 08:02:55.000000000
+0200
+++ new/audible-0.13.1/src/audible/login.py 2026-10-24 10:08:28.000000000
+0200
@@ -4,7 +4,7 @@
import logging
import re
import secrets
-import uuid
+import string
from collections.abc import Callable
from textwrap import dedent
from typing import Any
@@ -14,6 +14,7 @@
from bs4 import BeautifulSoup, NavigableString, Tag
from PIL import Image
+from ._device import APP_VERSION, SOFTWARE_VERSION
from .exceptions import AudibleError
from .json_provider import get_json_provider
from .metadata import encrypt_metadata, meta_audible_app
@@ -21,8 +22,10 @@
logger = logging.getLogger("audible.login")
+# WebKit freezes the OS token since iOS 26: the app's web view reports 18_7
+# while the device registers as iOS 26.x.
USER_AGENT = (
- "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) "
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) "
"AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148"
)
@@ -241,7 +244,13 @@
def build_device_serial() -> str:
- return uuid.uuid4().hex.upper()
+ """Generate a fresh 40-character alphanumeric device serial.
+
+ Returns:
+ A random serial containing only ASCII letters and digits.
+ """
+ alphabet = string.ascii_letters + string.digits
+ return "".join(secrets.choice(alphabet) for _ in range(40))
def build_client_id(serial: str) -> str:
@@ -249,7 +258,7 @@
return client_id.hex()
-def build_oauth_url(
+def build_oauth_url( # noqa: PLR0917
country_code: str,
domain: str,
market_place_id: str,
@@ -309,8 +318,11 @@
map_md_dict = {
"device_user_dictionary": [],
- "device_registration_data": {"software_version": "35602678"},
- "app_identifier": {"app_version": "3.56.2", "bundle_id":
"com.audible.iphone"},
+ "device_registration_data": {"software_version": SOFTWARE_VERSION},
+ "app_identifier": {
+ "app_version": APP_VERSION,
+ "bundle_id": "com.audible.iphone",
+ },
}
map_md_str = get_json_provider().dumps(map_md_dict)
map_md = base64.b64encode(map_md_str.encode()).decode().rstrip("=")
@@ -375,7 +387,7 @@
return False
-def login(
+def login( # noqa: PLR0917
username: str,
password: str,
country_code: str,
@@ -591,7 +603,7 @@
}
-def external_login(
+def external_login( # noqa: PLR0917
country_code: str,
domain: str,
market_place_id: str,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/audible-0.12.0/src/audible/register.py
new/audible-0.13.1/src/audible/register.py
--- old/audible-0.12.0/src/audible/register.py 2026-07-23 08:02:55.000000000
+0200
+++ new/audible-0.13.1/src/audible/register.py 2026-10-24 10:08:28.000000000
+0200
@@ -3,6 +3,7 @@
import httpx
+from ._device import APP_VERSION, OS_VERSION, SOFTWARE_VERSION
from .login import build_client_id
from .utils import build_access_token_header
@@ -42,15 +43,15 @@
"cookies": {"website_cookies": [], "domain": f".amazon.{domain}"},
"registration_data": {
"domain": "Device",
- "app_version": "3.56.2",
+ "app_version": APP_VERSION,
"device_serial": serial,
"device_type": "A2CZJZGLK2JJVM",
"device_name": (
"%FIRST_NAME%%FIRST_NAME_POSSESSIVE_STRING%%DUPE_"
"STRATEGY_1ST%Audible for iPhone"
),
- "os_version": "15.0.0",
- "software_version": "35602678",
+ "os_version": OS_VERSION,
+ "software_version": SOFTWARE_VERSION,
"device_model": "iPhone",
"app_name": "Audible",
},
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/audible-0.12.0/src/audible/utils.py
new/audible-0.13.1/src/audible/utils.py
--- old/audible-0.12.0/src/audible/utils.py 2026-07-23 08:02:55.000000000
+0200
+++ new/audible-0.13.1/src/audible/utils.py 2026-10-24 10:08:28.000000000
+0200
@@ -74,10 +74,12 @@
)
fmt = (
- r"^(?P<device_private_key>-----BEGIN RSA PRIVATE KEY-----.*"
- r"-----END RSA PRIVATE KEY-----\n)$"
+ r"^(?P<device_private_key>"
+ r"-----BEGIN (?P<private_key_type>(?:RSA )?PRIVATE KEY)-----.*"
+ r"-----END \2-----\n)$"
)
- if not re.match(fmt, value, re.S):
+ key_match = re.match(fmt, value, re.S)
+ if not key_match:
raise ValueError("device_private_key: Invalid token.")