补上最后两个多字母 id,至此 15 个平台对外 id 全部为两字母: kw kg tx wy qs xm me qt lr mg bd jx bl 5s ap **破坏性变更**:id 出现在所有 URL、/sources 返回以及 data/auth.json 的 凭据键里,咪咕与 Apple Music 已存的登录态会失效,需重新登录。 - sdk/index.ts:id 与 supportQuality 映射键(展示名 `咪咕音乐` / `Apple Music` 保持不动) - sdk/types.ts:Source 联合类型 - src/routes.ts:/stream 的 enum ['qs','migu'] → ['qs','mg'],以及 /url/mg、/stream/mg 等路径与 schema 描述 - src/http/migu-stream.ts、src/routes-login.ts、src/server.ts - test/smoke-test.ts、README.md、docs/API.md 同步 有意未改:域名与 API 路径(music.migu.cn、music.apple.com、 amp-api.music.apple.com、/v1/catalog/cn/apple-curators/…)、 标识符(registerMiguStream、appleAmpGet、APPLE_AMP_API_URL 等)、 sdk/migu 与 sdk/apple 的目录与文件名、错误文案里的平台名。 验证:typecheck ✓、pnpm test 112/112 ✓;另起临时实例实证 /sources (15/15 平台 id 与展示名全对)与 /login/status,旧 id 零残留。
75 lines
2.6 KiB
TypeScript
75 lines
2.6 KiB
TypeScript
// Apple Music 搜索(上游 apple.go 的 Search / SearchAlbum / SearchPlaylist)
|
||
// 通道:amp-api /v1/catalog/<storefront>/search(Bearer developer token,见 util.ts)
|
||
// 单次请求上限 25 条,page/limit 用 offset 翻页;响应没有 total,
|
||
// 因此 total = 已翻过的条数 + 本页条数(下一页是否存在由 next 决定,allPage=页码+1)。
|
||
import {
|
||
appleAmpGet,
|
||
appleSongFromCatalog,
|
||
appleAlbumToPlaylist,
|
||
applePlaylistToPlaylist,
|
||
getAppleConfig,
|
||
} from './util.ts'
|
||
|
||
/** amp-api search 单页硬上限 */
|
||
export const SEARCH_PAGE_MAX = 25
|
||
|
||
const catalogSearch = async(keyword: string, types: string, page: number, limit: number) => {
|
||
const word = String(keyword ?? '').trim()
|
||
const p = Math.max(1, Number(page) || 1)
|
||
const l = Math.min(Math.max(1, Number(limit) || 20), 100)
|
||
if (!word) return { body: null, page: p, limit: l }
|
||
const cfg = getAppleConfig()
|
||
const body: any = await appleAmpGet(`/v1/catalog/${cfg.storefront}/search`, {
|
||
term: word,
|
||
types,
|
||
limit: Math.min(l, SEARCH_PAGE_MAX),
|
||
offset: (p - 1) * l,
|
||
})
|
||
return { body, page: p, limit: l }
|
||
}
|
||
|
||
/** 上游 Search:搜索歌曲 */
|
||
export const search = async(keyword: string, page = 1, limit = 20) => {
|
||
const { body, page: p, limit: l } = await catalogSearch(keyword, 'songs', page, limit)
|
||
const bucket = body?.results?.songs ?? {}
|
||
const list = (bucket.data ?? []).map((item: any) => appleSongFromCatalog(item))
|
||
const hasNext = Boolean(bucket.next)
|
||
return {
|
||
list,
|
||
allPage: hasNext ? p + 1 : p,
|
||
total: (p - 1) * l + list.length,
|
||
limit: l,
|
||
source: 'ap' as const,
|
||
}
|
||
}
|
||
|
||
/** 上游 SearchAlbum:搜索专辑(返回歌单摘要形状) */
|
||
export const searchAlbum = async(keyword: string, page = 1, limit = 20) => {
|
||
const { body, page: p, limit: l } = await catalogSearch(keyword, 'albums', page, limit)
|
||
const items = body?.results?.albums?.data ?? []
|
||
const list = items.map((item: any) => appleAlbumToPlaylist(item))
|
||
return {
|
||
list,
|
||
total: (p - 1) * l + list.length,
|
||
page: p,
|
||
limit: l,
|
||
source: 'ap' as const,
|
||
}
|
||
}
|
||
|
||
/** 上游 SearchPlaylist:搜索歌单 */
|
||
export const searchPlaylist = async(keyword: string, page = 1, limit = 20) => {
|
||
const { body, page: p, limit: l } = await catalogSearch(keyword, 'playlists', page, limit)
|
||
const items = body?.results?.playlists?.data ?? []
|
||
const list = items.map((item: any) => applePlaylistToPlaylist(item))
|
||
return {
|
||
list,
|
||
total: (p - 1) * l + list.length,
|
||
page: p,
|
||
limit: l,
|
||
source: 'ap' as const,
|
||
}
|
||
}
|
||
|
||
export default { search, searchAlbum, searchPlaylist }
|