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,128 @@
|
||||
"""酷狗评论(对应 sdk/kg/comment.ts)。
|
||||
|
||||
签名:`signatureParams(params)` —— 参数串按键排序拼接后以 android 盐包裹取 MD5。
|
||||
返回形状是 `{source, comments, total, page, limit, maxPage}`(不是 `list`),
|
||||
`filterComment` 还要处理 `atlist` 替换与 `pcontent`(楼中楼)两种形态。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from app.sdk.common.request import http_fetch
|
||||
from app.sdk.common.utils import date_format2, decode_name
|
||||
from app.sdk.platforms.kg.util import signature_params
|
||||
|
||||
_UA = (
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||
"Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.24"
|
||||
)
|
||||
|
||||
|
||||
def _comment_params(hash_value: str, page: int, limit: int) -> str:
|
||||
return (
|
||||
"dfid=0&mid=16249512204336365674023395779019"
|
||||
f"&clienttime={int(time.time() * 1000)}&uuid=0&extdata={hash_value}"
|
||||
"&appid=1005&code=fc4be23b4e972707f36b8a828a93ba8a"
|
||||
f"&schash={hash_value}&clientver=11409&p={page}&clienttoken=&pagesize={limit}"
|
||||
"&ver=10&kugouid=0"
|
||||
)
|
||||
|
||||
|
||||
def _replace_at(raw: str, at_list: list[dict[str, Any]]) -> str:
|
||||
for item in at_list:
|
||||
raw = raw.replace(f"[at={item.get('id')}]", f"@{item.get('name')} ")
|
||||
return raw
|
||||
|
||||
|
||||
def _format_comment(raw_list: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
result: list[dict[str, Any]] = []
|
||||
for item in raw_list:
|
||||
content = item.get("content") or ""
|
||||
if item.get("atlist"):
|
||||
content = _replace_at(str(content), item["atlist"])
|
||||
images = [image.get("url") for image in item.get("images") or []] if item.get("images") else []
|
||||
like = item.get("like") or {}
|
||||
data = {
|
||||
"id": item.get("id"),
|
||||
"text": decode_name(content),
|
||||
"images": images,
|
||||
"location": item.get("location"),
|
||||
"time": item.get("addtime"),
|
||||
"timeStr": date_format2(item.get("addtime") or 0),
|
||||
"userName": item.get("user_name"),
|
||||
"avatar": item.get("user_pic"),
|
||||
"userId": item.get("user_id"),
|
||||
"likedCount": like.get("likenum"),
|
||||
"replyNum": item.get("reply_num"),
|
||||
"reply": [],
|
||||
}
|
||||
if item.get("pcontent"):
|
||||
# 楼中楼:父评论包一层,原评论挂在 reply 里
|
||||
result.append(
|
||||
{
|
||||
"id": item.get("id"),
|
||||
"text": decode_name(item["pcontent"]),
|
||||
"time": None,
|
||||
"userName": item.get("puser"),
|
||||
"avatar": None,
|
||||
"userId": item.get("puser_id"),
|
||||
"likedCount": None,
|
||||
"replyNum": None,
|
||||
"reply": [data],
|
||||
}
|
||||
)
|
||||
else:
|
||||
result.append(data)
|
||||
return result
|
||||
|
||||
|
||||
async def _get_comment(hash_value: str, page: int, limit: int, hot: bool) -> dict[str, Any]:
|
||||
path = "topliked" if hot else "newest"
|
||||
params = _comment_params(hash_value, page, limit)
|
||||
resp = await http_fetch(
|
||||
f"http://m.comment.service.kugou.com/r/v1/rank/{path}?{params}&signature={signature_params(params)}",
|
||||
headers={"User-Agent": _UA},
|
||||
)
|
||||
body = resp.body if isinstance(resp.body, dict) else {}
|
||||
if resp.status_code != 200 or body.get("err_code") != 0:
|
||||
raise RuntimeError("获取热门评论失败" if hot else "获取评论失败")
|
||||
total = body.get("count") or 0
|
||||
return {
|
||||
"source": "kg",
|
||||
"comments": _format_comment(body.get("list") or []),
|
||||
"total": total,
|
||||
"page": page,
|
||||
"limit": limit,
|
||||
"maxPage": math.ceil(total / limit) if limit else 1,
|
||||
}
|
||||
|
||||
|
||||
async def get_comment(song_info: dict[str, Any], page: int = 1, limit: int = 20) -> dict[str, Any]:
|
||||
hash_value = str(song_info.get("hash") or song_info.get("songmid") or "")
|
||||
return await _get_comment(hash_value, page, limit, False)
|
||||
|
||||
|
||||
async def get_hot_comment(song_info: dict[str, Any], page: int = 1, limit: int = 20) -> dict[str, Any]:
|
||||
hash_value = str(song_info.get("hash") or song_info.get("songmid") or "")
|
||||
return await _get_comment(hash_value, page, limit, True)
|
||||
|
||||
|
||||
async def get_reply_comment(
|
||||
song_info: dict[str, Any], reply_id: Any, page: int = 1, limit: int = 100
|
||||
) -> dict[str, Any]:
|
||||
songmid = str(song_info.get("songmid") or "")
|
||||
children_id = str(song_info.get("audioId") or "").split("_")[0] if len(songmid) == 32 else songmid
|
||||
resp = await http_fetch(
|
||||
"http://comment.service.kugou.com/index.php?r=commentsv2/getReplyWithLike"
|
||||
"&code=fc4be23b4e972707f36b8a828a93ba8a"
|
||||
f"&p={page}&pagesize={limit}&ver=1.01&clientver=8373&kugouid=687373022&need_show_image=1"
|
||||
f"&appid=1001&childrenid={children_id}&tid={reply_id}",
|
||||
headers={"User-Agent": "Android712-AndroidPhone-8983-18-0-COMMENT-wifi"},
|
||||
)
|
||||
body = resp.body if isinstance(resp.body, dict) else {}
|
||||
if resp.status_code != 200 or body.get("err_code") != 0:
|
||||
raise RuntimeError("获取回复评论失败")
|
||||
return {"source": "kg", "comments": _format_comment(body.get("list") or [])}
|
||||
Reference in new issue
Block a user