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,70 @@
|
||||
"""喜马拉雅 xm-sign 生成(对应 sdk/xm/sign.ts)—— du_web_sdk 签名链。
|
||||
|
||||
协议:device_info → JSON → URL 编码字节 → zlib(level 6) → AES-128-ECB(PKCS7, KEY) →
|
||||
POST https://hdaa.shuzilm.cn/report?v=1.2.0&e=1&c=1&r=<uuid>(octet-stream)→
|
||||
Base64 响应 → AES-ECB 解密 → JSON 取 cadd/sid → xm-sign = "{cadd}&&{sid}"
|
||||
|
||||
KEY 为 du_web_sdk _getDeviceKey(0) 的硬编码密钥;每次签名都重新上报(不缓存)。
|
||||
模板为纯 ASCII(Base64/十六进制字段),latin1 取字节与参考实现的 charCode 语义一致。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import json
|
||||
import time
|
||||
import uuid
|
||||
import zlib
|
||||
from typing import Any
|
||||
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
|
||||
from app.sdk.common.request import http_fetch
|
||||
from app.sdk.platforms.kg.util import pkcs7_pad, pkcs7_unpad
|
||||
from app.sdk.platforms.xm.device_info import DEVICE_INFO
|
||||
|
||||
SIGN_KEY = b"m9ZtRrz:qujT8@da"
|
||||
REPORT_URL = "https://hdaa.shuzilm.cn/report?v=1.2.0&e=1&c=1&r="
|
||||
#: 与 device_info.ew1.yV2(Chrome 125 模板)保持一致
|
||||
XM_UA = (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
|
||||
)
|
||||
|
||||
|
||||
def _aes_ecb(data: bytes, decrypt: bool) -> bytes:
|
||||
cipher = Cipher(algorithms.AES(SIGN_KEY), modes.ECB())
|
||||
if decrypt:
|
||||
decryptor = cipher.decryptor()
|
||||
return pkcs7_unpad(decryptor.update(data) + decryptor.finalize())
|
||||
encryptor = cipher.encryptor()
|
||||
return encryptor.update(pkcs7_pad(data)) + encryptor.finalize()
|
||||
|
||||
|
||||
async def make_xm_sign() -> str:
|
||||
"""生成 xm-sign(形如 "cadd&&sid");每次调用重新上报设备指纹。"""
|
||||
info: dict[str, Any] = {**DEVICE_INFO, "Zf5": int(time.time() * 1000)}
|
||||
raw = json.dumps(info, ensure_ascii=False, separators=(",", ":")).encode("latin-1")
|
||||
payload = _aes_ecb(zlib.compress(raw, 6), False)
|
||||
|
||||
resp = await http_fetch(
|
||||
f"{REPORT_URL}{uuid.uuid4()}",
|
||||
method="POST",
|
||||
headers={
|
||||
"Content-Type": "application/octet-stream",
|
||||
"User-Agent": XM_UA,
|
||||
"Host": "hdaa.shuzilm.cn",
|
||||
},
|
||||
body=payload,
|
||||
timeout=15_000,
|
||||
)
|
||||
text = resp.raw.decode("utf-8", errors="replace") if resp.raw else str(resp.body or "")
|
||||
if resp.status_code != 200:
|
||||
raise RuntimeError(f"xm-sign 上报失败: HTTP {resp.status_code}")
|
||||
|
||||
decrypted = _aes_ecb(base64.b64decode(text), True).decode("utf-8", errors="replace")
|
||||
obj = json.loads(decrypted)
|
||||
cadd = str(obj.get("cadd") or "")
|
||||
sid = str(obj.get("sid") or "")
|
||||
if not cadd or not sid:
|
||||
raise RuntimeError("xm-sign 上报响应缺少 cadd/sid")
|
||||
return f"{cadd}&&{sid}"
|
||||
Reference in new issue
Block a user