Run session-tracks serialization, request cache IO, session cache dir removal (now outside the store lock), history reads and gzip over 1 MiB on worker threads; cache the per-key PBKDF2 namespace and key_id.
Every structured error carries a short ref, appended to its message and logged with the request; /api/dashboard/status gains per-service error counts. Gzip now compresses in place.
Renames 314 defs and 41 module/class-level constants. Keeps 11 names underscored where the stripped form collides in scope, plus 5.5.0 shims for the three symbols external service repos call.
Move the supported interpreter range to >=3.11,<3.15, dropping the EOL 3.10 floor and unblocking installs on 3.13/3.14.
- pyproject: requires-python ">=3.10,<3.13" -> ">=3.11,<3.15"
- update_checker: replace deprecated asyncio.get_event_loop() with asyncio.to_thread() (warns on 3.12+, slated for removal)
- test_compression: simplify _run() to plain asyncio.run()
- README: bump documented Python range to 3.11 - 3.14
- uv.lock: regenerate for the new interpreter floor
Missing requested subtitle and audio languages now warn and continue when --best-available is set instead of hard-exiting. Without the flag, missing languages still produce an error and exit, matching the prior strict behavior. Audio missing-lang detection is now symmetric with subtitles.
- add find_missing_langs helper in core/utilities for reuse between s_lang and a_lang paths (skips all/best/orig sentinels)
- refactor dl.py s_lang/a_lang checks to share the helper
- add tests/lang_selection covering match primitives, helper output, and tricky langcodes corners (zh-Hans/zh-Hant/zh-CN/zh-TW/zh-HK, cmn/yue, fil/tl/tgl)
- clean up unused-var ruff F841 in tests/remote/unit/
Covers RemoteClient/RemoteService, REST routes, handlers, SessionStore, InputBridge, DownloadQueueManager, errors, compression, and serve CLI. E2e tier opts in via --live and can auto-spawn its own serve.