Files
MusicServer/app/sdk/platforms/tx/music_info.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

109 lines
3.9 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.
"""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