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,45 @@
|
||||
"""Apple Music 歌词(对应 sdk/apple/lyric.ts,上游 apple.go GetLyrics)。
|
||||
|
||||
通道:amp-api /v1/catalog/<sf>/songs/<id>?include=lyrics&extend=extendedAssetUrls
|
||||
→ data[0].relationships.lyrics.data[*].attributes.ttml
|
||||
上游取到的是 TTML(Apple 逐字歌词格式,非 LRC),原样放入 lyric 字段返回;没有则忠实返回空串。
|
||||
|
||||
注意(2026-10 实测):匿名 developer token 请求时 Apple 不返回 lyrics relationship
|
||||
(即使 attributes.hasLyrics=true,歌单/搜索接口也一样),歌词需要 media-user-token(订阅态);
|
||||
本实现保持上游行为:拿不到就返回 ``{lyric: '', tlyric: ''}``,不抛错。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from app.sdk.common.utils import as_dict, as_list
|
||||
from app.sdk.platforms.apple.util import apple_amp_get_tracked, extract_apple_id, get_apple_config
|
||||
|
||||
|
||||
async def get_lyric(song_info: dict[str, Any]) -> dict[str, str]:
|
||||
song_id = extract_apple_id(str(song_info.get("songmid") or song_info.get("id") or ""), "song")
|
||||
if not song_id:
|
||||
raise RuntimeError("apple 歌曲 id 无效")
|
||||
cfg = get_apple_config()
|
||||
body = as_dict(
|
||||
await apple_amp_get_tracked(
|
||||
f"/v1/catalog/{cfg['storefront']}/songs/{song_id}",
|
||||
{"include": "lyrics", "extend": "extendedAssetUrls"},
|
||||
)
|
||||
)
|
||||
data = as_list(body.get("data"))
|
||||
first = as_dict(data[0]) if data else {}
|
||||
relationships = as_dict(first.get("relationships"))
|
||||
lyrics = as_list(as_dict(relationships.get("lyrics")).get("data"))
|
||||
for rel in lyrics:
|
||||
text = str(as_dict(as_dict(rel).get("attributes")).get("ttml") or "")
|
||||
if text:
|
||||
return {"lyric": text, "tlyric": ""}
|
||||
return {"lyric": "", "tlyric": ""}
|
||||
|
||||
|
||||
async def get_lyrics_text(song_id: str) -> str:
|
||||
"""非取消句柄版:直接返回歌词文本(内部/测试用)。"""
|
||||
result = await get_lyric({"songmid": song_id})
|
||||
return result.get("lyric") or ""
|
||||
Reference in new issue
Block a user