feat(url): 取流链兜底——外部音源→SDK 直连→酷我跨平台匹配

/url/:source/:id 顺序尝试:外部音源脚本(SOURCE_ENABLED 启用时)→
SDK 官方直连→都失败时用 findMusic 仅在 kw 内按歌名/歌手/时长匹配
同一首歌后走 kw 通道(匿名可拿无损)。kg 的 hash 自动反查歌曲信息,
其它平台可传 name/singer/interval 查询参数启用兜底;
kw 自身不再二次兜底。响应新增 via 字段标记实际命中通道
(script/平台 id/kw,直接命中不返回),type 改为实际音质(降级链生效);
全部失败返回聚合各阶段错误的 502。findMusic/searchMusic 增加 only 参数。
This commit is contained in:
snowy-yang committed 2026-10-01 22:54:00 +08:00
1 parent 0f9cff2a08
commit b3e1cc5f67
4 files changed
+88 -26

No files matched your search

+7 -8
View File
@@ -86,19 +86,17 @@ interface MatchCandidate extends SongInfo {
* @param params.name 歌名
* @param params.singer 歌手
* @param params.excludeSource 指定平台时,聚合结果会跳过该平台
* @param params.only 只搜该平台(与 excludeSource 互斥,优先生效)
* @param params.limit 单平台结果条数
*/
export const searchMusic = async (
{ name, singer, source: excludeSource, limit = 25 }: { name: string; singer?: string; source?: string; limit?: number },
{ name, singer, source: excludeSource, only, limit = 25 }: { name: string; singer?: string; source?: string; only?: string; limit?: number },
): Promise<Array<SearchResult | null>> => {
const musicName = trimStr(name)
const tasks: Array<Promise<SearchResult | null>> = []
for (const source of SUPPORTED_SOURCES) {
if (
source.id === excludeSource ||
!source.module.musicSearch
)
continue
if (!source.module.musicSearch) continue
if (only ? source.id !== only : source.id === excludeSource) continue
tasks.push(
source.module.musicSearch
.search(`${musicName} ${singer || ''}`.trim(), 1, limit)
@@ -120,11 +118,12 @@ const pickMatching = <T>(items: T[], predicate: (item: T) => boolean): { matched
/**
* 跨平台精确匹配歌曲(按歌手、歌名、专辑、时长综合排序)
* @param params.only 只在该平台内匹配(跨平台兜底取流用)
*/
export const findMusic = async (
{ name, singer, albumName, interval, source: s }: { name: string; singer?: string; albumName?: string; interval?: string; source?: string },
{ name, singer, albumName, interval, source: s, only }: { name: string; singer?: string; albumName?: string; interval?: string; source?: string; only?: string },
): Promise<MatchCandidate[]> => {
const lists = await searchMusic({ name, singer, source: s, limit: 25 })
const lists = await searchMusic({ name, singer, source: s, only, limit: 25 })
const fMusicName = filterStr(name).toLowerCase()
const fSinger = filterStr(sortSingle(singer)).toLowerCase()