Files
MusicServer/app/sdk/platforms/kw/comment.py
T
snowy-yang dfc06bf4de 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 路由验证通过
2026-10-23 21:55:45 +08:00

90 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""酷我评论(对应 sdk/kw/comment.ts)。
`ncomment.kuwo.cn/com.s` 只认安卓 Dalvik UA;`type=get_comment` 取最新评论、
`type=get_rec_comment` 取热评,两者响应字段名不同(comments/comments_counts
vs hot_comments/hot_comments_counts)。
"""
from __future__ import annotations
import math
from typing import Any
from urllib.parse import unquote
from app.sdk.common.request import http_fetch
from app.sdk.common.utils import date_format2
COMMENT_API = (
"http://ncomment.kuwo.cn/com.s?f=web&type={type}&aapiver=1&prod=kwplayer_ar_10.5.2.0"
"&digest=15&sid={songmid}&start={start}&msgflag=1&count={count}&newver=3&uid=0"
)
HEADERS = {"User-Agent": "Dalvik/2.1.0 (Linux; U; Android 9;)"}
def _to_int(value: Any) -> int:
try:
return int(float(value))
except (TypeError, ValueError):
return 0
def _reply_comment(item: dict[str, Any]) -> dict[str, Any]:
return {
"id": item.get("id"),
"text": item.get("msg"),
"time": item.get("time"),
"timeStr": date_format2(_to_int(item.get("time")) * 1000),
"userName": item.get("u_name"),
"avatar": item.get("u_pic"),
"userId": item.get("u_id"),
"likedCount": item.get("like_num"),
"images": [item["mpic"]] if item.get("mpic") else [],
}
def _filter_comment(raw_list: list[dict[str, Any]] | None) -> list[dict[str, Any]]:
if not raw_list:
return []
result: list[dict[str, Any]] = []
for item in raw_list:
data = _reply_comment(item)
data["images"] = [unquote(str(item["mpic"]))] if item.get("mpic") else []
# 顶层评论多一个楼中楼数组;原实现对父级 mpic 做 decodeURIComponent,子级不做
data["reply"] = [_reply_comment(child) for child in item.get("child_comments") or []]
result.append(data)
return result
async def _get_comment(songmid: str, page: int, limit: int, *, hot: bool) -> dict[str, Any]:
comment_type = "get_rec_comment" if hot else "get_comment"
url = COMMENT_API.format(type=comment_type, songmid=songmid, start=limit * (page - 1), count=limit)
resp = await http_fetch(url, headers=HEADERS)
body = resp.body if isinstance(resp.body, dict) else {}
if resp.status_code != 200 or str(body.get("code")) != "200":
raise RuntimeError("获取热门评论失败" if hot else "获取评论失败")
if hot:
total = _to_int(body.get("hot_comments_counts"))
comments = _filter_comment(body.get("hot_comments"))
else:
total = _to_int(body.get("comments_counts"))
comments = _filter_comment(body.get("comments"))
return {
"source": "kw",
"comments": comments,
"total": total,
"page": page,
"limit": limit,
# 原实现用 `Math.ceil(total/limit) || 1` 兜住 0 条
"maxPage": math.ceil(total / limit) if limit and total else 1,
}
async def get_comment(song_info: dict[str, Any], page: int = 1, limit: int = 20) -> dict[str, Any]:
return await _get_comment(str(song_info.get("songmid") or ""), page, limit, hot=False)
async def get_hot_comment(song_info: dict[str, Any], page: int = 1, limit: int = 100) -> dict[str, Any]:
return await _get_comment(str(song_info.get("songmid") or ""), page, limit, hot=True)