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,108 @@
|
||||
"""QQ音乐歌曲信息(移植自 sdk/tx/musicInfo.ts)。
|
||||
|
||||
走 musicu.fcg 的 music.pf_song_detail_svr/get_song_detail_yqq:songmid → track_info。
|
||||
歌词与评论模块靠它把 songmid 换成数字 songID(接口只认数字 id)。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from app.sdk.common.request import http_fetch
|
||||
from app.sdk.common.utils import format_play_time, size_formate
|
||||
from app.sdk.platforms.tx.utils import MUSICU_URL, UA_MUSICU
|
||||
|
||||
|
||||
def _build_types(file: dict[str, Any]) -> tuple[list[dict[str, Any]], dict[str, dict[str, Any]]]:
|
||||
"""file.size_* → types/_types(0 表示该档不存在,与原文一致)。"""
|
||||
types: list[dict[str, Any]] = []
|
||||
_types: dict[str, dict[str, Any]] = {}
|
||||
for key, quality in (
|
||||
("size_128mp3", "128k"),
|
||||
("size_320mp3", "320k"),
|
||||
("size_flac", "flac"),
|
||||
("size_hires", "flac24bit"),
|
||||
):
|
||||
if file.get(key):
|
||||
size = size_formate(file[key])
|
||||
types.append({"type": quality, "size": size})
|
||||
_types[quality] = {"size": size}
|
||||
return types, _types
|
||||
|
||||
|
||||
def _song_img(album_id: Any, singers: Any) -> str:
|
||||
if album_id in ("", "空", None):
|
||||
if isinstance(singers, list) and singers and singers[0].get("mid"):
|
||||
return f"https://y.gtimg.cn/music/photo_new/T001R500x500M000{singers[0]['mid']}.jpg"
|
||||
return ""
|
||||
return f"https://y.gtimg.cn/music/photo_new/T002R500x500M000{album_id}.jpg"
|
||||
|
||||
|
||||
async def get_music_info_by_mid(songmid: str) -> dict[str, Any] | None:
|
||||
"""songmid → SongInfo;无 media_mid 时返回 None(原文同样返回 null)。"""
|
||||
resp = await http_fetch(
|
||||
MUSICU_URL,
|
||||
method="post",
|
||||
headers={"User-Agent": UA_MUSICU},
|
||||
body={
|
||||
"comm": {"ct": "19", "cv": "1859", "uin": "0"},
|
||||
"req": {
|
||||
"module": "music.pf_song_detail_svr",
|
||||
"method": "get_song_detail_yqq",
|
||||
"param": {"song_type": 0, "song_mid": songmid},
|
||||
},
|
||||
},
|
||||
)
|
||||
body = resp.body if isinstance(resp.body, dict) else {}
|
||||
req = body.get("req") or {}
|
||||
if body.get("code") != 0 or req.get("code") != 0:
|
||||
raise RuntimeError("获取歌曲信息失败")
|
||||
item = (req.get("data") or {}).get("track_info") or {}
|
||||
file = item.get("file") or {}
|
||||
if not file.get("media_mid"):
|
||||
return None
|
||||
|
||||
types, _types = _build_types(file)
|
||||
album = item.get("album") or {}
|
||||
album_name = album.get("name") or ""
|
||||
album_id = album.get("mid") or ""
|
||||
singers = item.get("singer") or []
|
||||
|
||||
return {
|
||||
"singer": "、".join(str(singer.get("name")) for singer in singers if isinstance(singer, dict)),
|
||||
"name": item.get("title"),
|
||||
"albumName": album_name,
|
||||
"albumId": album_id,
|
||||
"source": "tx",
|
||||
"interval": format_play_time(item.get("interval")),
|
||||
"songId": item.get("id"),
|
||||
"albumMid": album.get("mid") or "",
|
||||
"strMediaMid": file.get("media_mid"),
|
||||
"songmid": item.get("mid"),
|
||||
"img": _song_img(album_id, singers),
|
||||
"types": types,
|
||||
"_types": _types,
|
||||
"typeUrl": {},
|
||||
}
|
||||
|
||||
|
||||
async def get_music_info(song_info: dict[str, Any]) -> dict[str, Any] | None:
|
||||
"""Platform.get_music_info 契约:song_info 里取 songmid。"""
|
||||
songmid = song_info.get("songmid") or song_info.get("hash") or ""
|
||||
if not songmid:
|
||||
return None
|
||||
return await get_music_info_by_mid(str(songmid))
|
||||
|
||||
|
||||
async def handle_music_info(song_info: dict[str, Any]) -> dict[str, Any]:
|
||||
"""补全缺失的歌名/歌手/封面;取不到时原样返回(不抛错)。"""
|
||||
if song_info.get("name") and song_info.get("singer"):
|
||||
return song_info
|
||||
try:
|
||||
info = await get_music_info(song_info)
|
||||
except (RuntimeError, OSError, TimeoutError):
|
||||
return song_info
|
||||
if not info:
|
||||
return song_info
|
||||
merged = {**info, **{k: v for k, v in song_info.items() if v}}
|
||||
return merged
|
||||
Reference in new issue
Block a user