feat(python): 完成 Python 全量迁移——剩余平台、登录通道与公共层修复
- 新增 5sing/apple/xm/me/qt/lr 平台包与取流实现(含 xm-sign、MissEvan 签名、蜻蜓 HMAC、Apple developer token) - 新增 wy/kg/tx/lr 登录:扫码、手机号、账密(kg 20028 风控指纹)、cookie 绑定、kg/tx 续期 - request 层:保留多值 Set-Cookie;JSON body 紧凑化(修复 kg 网关验签);直连不经系统代理(对齐 Node) - 登录路由统一 Body(embed=True),修复扫码轮询 422;lifespan 接入登录续期调度 - ruff/format/pyright 全绿;14 平台真实联网与 HTTP 路由验证通过
This commit is contained in:
1 parent
800871ef6e
commit
dfc06bf4de
182 files changed
+26620
No files matched your search
@@ -0,0 +1,59 @@
|
||||
"""懒人听书登录(对应 sdk/lr/auth.ts,web 通道 m.lrts.me/ajax/logon)。
|
||||
|
||||
参考社区下载器(yoval/lrts 等):POST form {account, pwd: base64(password)},
|
||||
成功响应 json.phone 非空,Set-Cookie 种下会话;取流接口必须携带该会话
|
||||
(免费章节匿名也返回"收费章节未购买",2026-10-02 实测)。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
from typing import Any
|
||||
from urllib.parse import quote
|
||||
|
||||
from app.http.api_error import ApiError
|
||||
from app.sdk.auth_store import get_credentials, set_credentials
|
||||
from app.sdk.common.request import http_fetch
|
||||
|
||||
UA_MI = (
|
||||
"Mozilla/5.0 (Linux; U; Android 4.1.1; zh-cn; MI2 Build/JRO03L) AppleWebKit/534.30 "
|
||||
"(KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 XiaoMi/MiuiBrowser/1.0"
|
||||
)
|
||||
LOGON_URL = "https://m.lrts.me/ajax/logon"
|
||||
|
||||
|
||||
async def login(account: str, password: str) -> dict[str, Any]:
|
||||
"""账号密码登录(成功保存会话 Cookie)。"""
|
||||
form = f"account={quote(str(account), safe='')}&pwd={base64.b64encode(str(password).encode()).decode()}"
|
||||
resp = await http_fetch(
|
||||
LOGON_URL,
|
||||
method="POST",
|
||||
headers={
|
||||
"User-Agent": UA_MI,
|
||||
"Referer": "https://m.lrts.me/",
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body=form,
|
||||
timeout=15_000,
|
||||
)
|
||||
body = resp.body if isinstance(resp.body, dict) else {}
|
||||
if resp.status_code != 200 or not body.get("phone"):
|
||||
raise ApiError(401, str(body.get("msg") or "登录失败(账号或密码错误)"))
|
||||
cookie = "; ".join(part for part in (item.split(";")[0].strip() for item in resp.set_cookies) if part)
|
||||
if not cookie:
|
||||
raise RuntimeError("登录成功但未返回会话 Cookie")
|
||||
set_credentials("lr", {"cookie": cookie, "phone": str(body["phone"])})
|
||||
return {"logged": True, "phone": str(body["phone"])}
|
||||
|
||||
|
||||
def get_lr_cookie() -> str:
|
||||
"""登录会话 Cookie(未登录返回空串)。"""
|
||||
return str((get_credentials("lr") or {}).get("cookie") or "")
|
||||
|
||||
|
||||
def status() -> dict[str, Any]:
|
||||
"""登录状态。"""
|
||||
cred = get_credentials("lr") or {}
|
||||
if cred.get("cookie"):
|
||||
return {"logged": True, "phone": cred.get("phone"), "updatedAt": cred.get("updatedAt")}
|
||||
return {"logged": False}
|
||||
Reference in new issue
Block a user