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,122 @@
|
||||
"""SDK 共享类型约定。
|
||||
|
||||
Python 无编译期类型强制,这里的 TypedDict / Protocol 只作为迁移时的形状文档;
|
||||
运行时行为与 TS 版一致(普通 dict)。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Protocol, TypedDict, runtime_checkable
|
||||
|
||||
SOURCE_IDS = (
|
||||
"kw",
|
||||
"kg",
|
||||
"tx",
|
||||
"wy",
|
||||
"mg",
|
||||
"bd",
|
||||
"joox",
|
||||
"bili",
|
||||
"5sing",
|
||||
"apple",
|
||||
"xm",
|
||||
"me",
|
||||
"qt",
|
||||
"lr",
|
||||
)
|
||||
|
||||
QUALITIES = ("64k", "128k", "320k", "flac", "flac24bit")
|
||||
|
||||
|
||||
class SongType(TypedDict, total=False):
|
||||
type: str
|
||||
size: str
|
||||
|
||||
|
||||
class QualityInfo(TypedDict, total=False):
|
||||
size: str
|
||||
|
||||
|
||||
class SongInfo(TypedDict, total=False):
|
||||
songmid: str
|
||||
hash: str
|
||||
copyrightId: str
|
||||
songStringId: str
|
||||
albumId: str
|
||||
name: str | None
|
||||
singer: str
|
||||
source: str
|
||||
interval: str | int
|
||||
albumName: str
|
||||
img: str | None
|
||||
lrc: str | None
|
||||
otherSource: str | None
|
||||
types: list[SongType]
|
||||
_types: dict[str, QualityInfo]
|
||||
typeUrl: dict[str, str]
|
||||
|
||||
|
||||
class MusicUrlResult(TypedDict, total=False):
|
||||
type: str
|
||||
url: str
|
||||
via: str
|
||||
encrypted: bool
|
||||
fileKey: str
|
||||
|
||||
|
||||
class LyricInfo(TypedDict, total=False):
|
||||
lyric: str
|
||||
tlyric: str
|
||||
rlyric: str
|
||||
yrc: str
|
||||
|
||||
|
||||
class SearchResult(TypedDict, total=False):
|
||||
list: list[dict[str, Any]]
|
||||
allPage: int
|
||||
total: int
|
||||
limit: int
|
||||
source: str
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class MusicSearchModule(Protocol):
|
||||
async def search(self, keyword: str, page: int = 1, limit: int = 20, retry_num: int = 0) -> SearchResult: ...
|
||||
|
||||
|
||||
async def search(keyword: str, page: int = 1, limit: int = 20) -> SearchResult:
|
||||
"""占位,供类型检查引用。"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class PlatformModule(Protocol):
|
||||
"""平台模块契约(对应原 sdk/types.ts 的 PlatformModule)。
|
||||
|
||||
musicSearch / leaderboard / songList / hotSearch / singer / album / playlist /
|
||||
parse / userPlaylist 为子模块对象;get_music_url / get_lyric / get_pic 为可等待方法。
|
||||
各平台按能力自由实现,缺省表示不支持(/sources 的能力位据此判断)。
|
||||
"""
|
||||
|
||||
musicSearch: Any
|
||||
leaderboard: Any
|
||||
songList: Any
|
||||
hotSearch: Any
|
||||
singer: Any
|
||||
album: Any
|
||||
playlist: Any
|
||||
parse: Any
|
||||
userPlaylist: Any
|
||||
|
||||
|
||||
def cap(obj: Any, path: str | None = None) -> Any:
|
||||
"""按 `album.searchAlbum` 形式的嵌套路径取能力位(None 表示不支持)。"""
|
||||
if obj is None:
|
||||
return None
|
||||
if not path:
|
||||
return obj
|
||||
cur = obj
|
||||
for key in path.split("."):
|
||||
cur = getattr(cur, key, None) if not isinstance(cur, dict) else cur.get(key)
|
||||
if cur is None:
|
||||
return None
|
||||
return cur
|
||||
Reference in new issue
Block a user