- 新增 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 路由验证通过
44 lines
2.3 KiB
Python
44 lines
2.3 KiB
Python
"""5sing 取流(对应 sdk/apis/5sing.ts,上游 fivesing/download.go 的 GetDownloadURL)。
|
||
|
||
链路:GET http://mobileapi.5sing.kugou.com/song/getSongUrl?songid=<id>&songtype=<yc|fc|bz>
|
||
→ { code: 1000, data: { squrl/squrl_backup, hqurl/hqurl_backup, lqurl/lqurl_backup, ... } }
|
||
直链为 CDN 明文地址(wsaudiobssdlbig.kugou.com,https,无需 Referer/防盗链签名;实测 Range 206 可播)。
|
||
|
||
档位:上游固定 sq(最高)→ hq → lq,返回首个非空(含 _backup);只有显式请求 64k(或 lq)时
|
||
才倒序优先 lq,返回实际命中档位。songmid 约定 ``<songId>|<songType>``;
|
||
Parse 出来的歌曲带 url 时直接复用(上游 GetDownloadURL: if s.URL != "" return)。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Any
|
||
|
||
from app.sdk.platforms.fivesing.music_info import fetch_audio_link
|
||
from app.sdk.platforms.fivesing.util import extract_song_id, is_foreign_source
|
||
|
||
#: 上游只有 sq/hq/lq 三档,实际命中档位以返回 type 为准(sq 常缺,多命中 hq)
|
||
SUPPORT_QUALITYS: list[str] = ["sq", "hq", "lq"]
|
||
|
||
|
||
async def get_download_url(song_info: dict[str, Any], quality: str | None = None) -> dict[str, str]:
|
||
"""上游 GetDownloadURL 的等价实现。"""
|
||
if is_foreign_source(song_info):
|
||
raise RuntimeError("source mismatch")
|
||
# 上游逻辑:Song.URL 已带直链(Parse 场景)直接复用
|
||
if song_info.get("url"):
|
||
return {"type": str(song_info.get("type") or quality or "sq"), "url": str(song_info["url"])}
|
||
ids = extract_song_id(
|
||
song_info.get("songmid") or song_info.get("id") or song_info.get("songid"), song_info.get("songType")
|
||
)
|
||
if not ids["songID"] or not ids["songType"]:
|
||
raise RuntimeError('invalid id structure: 5sing songmid 需为 "<songId>|<songType>"(songType 为 yc/fc/bz)')
|
||
prefer_low = quality in ("64k", "lq")
|
||
return await fetch_audio_link(ids["songID"], ids["songType"], prefer_low)
|
||
|
||
|
||
async def get_music_url(song_info: dict[str, Any], quality: str, retry_num: int = 0) -> dict[str, Any]:
|
||
"""统一取流:返回 ``{type: 'sq'|'hq'|'lq', url}``(type 为实际命中档位,可能低于请求档位)。"""
|
||
del retry_num # 契约参数,重试已在 fetch_5sing_json 内
|
||
hit = await get_download_url(song_info, quality)
|
||
return {"type": hit["type"], "url": hit["url"]}
|