refactor(api)!: 平台 id 回退——joox/5sing/bili/apple 恢复全名,目录对齐 bd/mg
- 撤销 3381e9b/1b972ec 中 jx/5s/bl/ap 四个短码,恢复 joox/5sing/bili/apple; qian→bd、migu→mg 保留 - 目录与文件对齐保留的新 id:sdk/qian/→sdk/bd/、sdk/migu/→sdk/mg/、 sdk/apis/qian.ts→bd.ts、migu.ts→mg.ts、src/http/migu-stream.ts→mg-stream.ts (规则改为「目录=对外 id」,聚合入口的模块变量同步更名;music.migu.cn 等域名、 上游 music-lib 对照注释、miguGet 等功能标识符不动) - 对外 id 全表:kw kg tx wy qs xm me qt lr mg bd joox 5sing bili apple **破坏性变更**:joox/5sing/bili/apple 四平台的 URL、/sources 返回与 data/auth.json 凭据键恢复全名,已存短码键的登录态需重新登录。 验证:typecheck ✓、pnpm test 112/112 ✓
This commit is contained in:
1 parent
8c02fb5c15
commit
e1357dd412
50 files changed
+178
-178
No files matched your search
+2
-2
@@ -1,12 +1,12 @@
|
||||
// 5sing(5sing.kugou.com,酷狗旗下原创/翻唱音乐平台)平台模块
|
||||
// 上游 music-lib/fivesing 移植(平台 id 统一为 '5s',上游 model.Source 为 "fivesing")。
|
||||
// 上游 music-lib/fivesing 移植(平台 id 统一为 '5sing',上游 model.Source 为 "fivesing")。
|
||||
// 能力:搜索(原创 yc / 翻唱 fc / 伴奏 bz,统一种类字段 songType)/ 取流(明文直链,档位 sq→hq→lq)/
|
||||
// 歌词(newget.dynamicWords)/ 歌单(搜索、详情、链接解析)/ 单曲链接解析。
|
||||
// 不实现:专辑、封面(上游无 GetPic)、分类歌单/个人歌单(上游显式 not supported,保留抛错语义)、
|
||||
// 榜单/热搜/歌手/评论(上游均无)。
|
||||
// 取流入口在 sdk/apis/5sing.ts;songmid 约定 "<songId>|<songType>"(如 "932557|yc",与上游 ID 一致)。
|
||||
// 登录:上游 New(cookie) 的 cookie 会随请求发送(付费/受限内容可能受益);
|
||||
// 本移植从 sdk/auth/store.ts 的 getCredentials('5s') 读取 cookie(无则匿名,匿名实测可搜索/取流/歌词/歌单)。
|
||||
// 本移植从 sdk/auth/store.ts 的 getCredentials('5sing') 读取 cookie(无则匿名,匿名实测可搜索/取流/歌词/歌单)。
|
||||
|
||||
import musicSearch from './musicSearch.ts'
|
||||
import lyric from './lyric.ts'
|
||||
|
||||
@@ -106,7 +106,7 @@ export const fetchSongInfo = async(songID: string, songType: string, register?:
|
||||
link: buildSongLink(songType, songID),
|
||||
songType,
|
||||
typeName: kindName(songType),
|
||||
source: '5s' as const,
|
||||
source: '5sing' as const,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import { formatPlayTime } from '../common/utils.ts'
|
||||
import { SEARCH_API, fetch5singJson, removeEmTags, unescapeHtml, buildSongLink, estimateDuration, kindName } from './util.ts'
|
||||
|
||||
/** 上游 Search 的 list 项 → 统一 Song 形状(source 统一 '5s') */
|
||||
/** 上游 Search 的 list 项 → 统一 Song 形状(source 统一 '5sing') */
|
||||
export const mapSearchSong = (item: any) => {
|
||||
const songType = String(item?.typeEname ?? '')
|
||||
const duration = estimateDuration(Number(item?.songSize) || 0)
|
||||
@@ -29,7 +29,7 @@ export const mapSearchSong = (item: any) => {
|
||||
typeName: String(item?.typeName ?? kindName(songType)),
|
||||
size: Number(item?.songSize) || 0,
|
||||
link: buildSongLink(songType, item?.songId),
|
||||
source: '5s' as const,
|
||||
source: '5sing' as const,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export const search = async(keyword: string, page = 1, limit = 20) => {
|
||||
const word = String(keyword ?? '').trim()
|
||||
const pageNo = Math.max(1, Number(page) || 1)
|
||||
const pageSize = Math.max(1, Number(limit) || 20)
|
||||
const empty = { list: [] as any[], allPage: 0, total: 0, limit: pageSize, source: '5s' as const }
|
||||
const empty = { list: [] as any[], allPage: 0, total: 0, limit: pageSize, source: '5sing' as const }
|
||||
if (!word) return empty
|
||||
|
||||
const url = `${SEARCH_API}?keyword=${encodeURIComponent(word)}&sort=1&page=${pageNo}&filter=0&type=0`
|
||||
@@ -50,7 +50,7 @@ export const search = async(keyword: string, page = 1, limit = 20) => {
|
||||
const total = Number(pageInfo.totalCount) || list.length
|
||||
// 服务端每页 10 条(固定),allPage 直接用 pageInfo.totalPages
|
||||
const allPage = Number(pageInfo.totalPages) || (list.length >= pageSize ? pageNo + 1 : pageNo)
|
||||
return { list, allPage, total, limit: pageSize, source: '5s' as const }
|
||||
return { list, allPage, total, limit: pageSize, source: '5sing' as const }
|
||||
}
|
||||
|
||||
export default { search }
|
||||
@@ -32,7 +32,7 @@ const mapPlaylistSong = (songID: string, kind: string, name: string, artist: str
|
||||
songType: kind,
|
||||
typeName: kindName(kind),
|
||||
link: buildSongLink(kind, songID),
|
||||
source: '5s' as const,
|
||||
source: '5sing' as const,
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -103,7 +103,7 @@ const mapPlaylistSummary = (item: any) => {
|
||||
// 上游怪癖:content 为字符串 "0" 时视为空简介
|
||||
description: desc === '0' ? '' : desc,
|
||||
link: buildPlaylistLink(userId, id),
|
||||
source: '5s' as const,
|
||||
source: '5sing' as const,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ export const searchPlaylist = async(keyword: string, page = 1, limit = 20) => {
|
||||
const word = String(keyword ?? '').trim()
|
||||
const pageNo = Math.max(1, Number(page) || 1)
|
||||
const pageSize = Math.max(1, Number(limit) || 20)
|
||||
const empty = { list: [] as any[], total: 0, page: pageNo, limit: pageSize, source: '5s' as const }
|
||||
const empty = { list: [] as any[], total: 0, page: pageNo, limit: pageSize, source: '5sing' as const }
|
||||
if (!word) return empty
|
||||
|
||||
const url = `${SEARCH_API}?keyword=${encodeURIComponent(word)}&sort=1&page=${pageNo}&filter=0&type=1`
|
||||
@@ -132,7 +132,7 @@ export const searchPlaylist = async(keyword: string, page = 1, limit = 20) => {
|
||||
}
|
||||
|
||||
const total = Number(body?.pageInfo?.totalCount) || list.length
|
||||
return { list, total, page: pageNo, limit: pageSize, source: '5s' as const }
|
||||
return { list, total, page: pageNo, limit: pageSize, source: '5sing' as const }
|
||||
}
|
||||
|
||||
/** 元数据 + 全量曲目(上游 fetchPlaylistDetail 核心;HTML 不分页,一次拿全) */
|
||||
@@ -165,7 +165,7 @@ export const fetchPlaylistAll = async(playlistIdRaw: string) => {
|
||||
description: String(data?.C ?? ''),
|
||||
link: buildPlaylistLink(userId, playlistId),
|
||||
userId,
|
||||
source: '5s' as const,
|
||||
source: '5sing' as const,
|
||||
}
|
||||
|
||||
// 2) 歌单页 HTML(数字 userId 会 302 → 个性域名页,fetch5sing 已跟跳)
|
||||
@@ -191,7 +191,7 @@ export const getPlaylistDetail = async(id: string, page = 1, limit = 50) => {
|
||||
const list = songs.slice(start, start + pageSize)
|
||||
const total = songs.length
|
||||
return {
|
||||
source: '5s' as const,
|
||||
source: '5sing' as const,
|
||||
info: {
|
||||
id: playlist.id,
|
||||
name: playlist.name,
|
||||
@@ -222,7 +222,7 @@ export const parsePlaylistLink = async(link: string) => {
|
||||
if (!parsed) throw new Error('invalid 5sing playlist link')
|
||||
const { playlist, songs } = await fetchPlaylistAll(parsed.playlistId)
|
||||
return {
|
||||
source: '5s' as const,
|
||||
source: '5sing' as const,
|
||||
info: {
|
||||
id: playlist.id,
|
||||
name: playlist.name,
|
||||
|
||||
+6
-6
@@ -1,8 +1,8 @@
|
||||
// 5sing 公共工具(上游 fivesing/fivesing.go 的 getFirstValid / removeEmTags 等移植)
|
||||
// 平台 id 统一为 '5s'(上游包名 fivesing,Go 源码 model.Source 用 "fivesing";
|
||||
// 本仓库契约要求 id 为 5s,故所有对外 source 字段均为 '5s'。
|
||||
// 平台 id 统一为 '5sing'(上游包名 fivesing,Go 源码 model.Source 用 "fivesing";
|
||||
// 本仓库契约要求 id 为 5sing,故所有对外 source 字段均为 '5sing'。
|
||||
// 上游 New(cookie) 的 cookie 实际参与请求(搜索/getSongUrl/newget/歌单页),
|
||||
// 这里按约定从 sdk/auth/store.ts 的 getCredentials('5s') 读取(无则匿名)。
|
||||
// 这里按约定从 sdk/auth/store.ts 的 getCredentials('5sing') 读取(无则匿名)。
|
||||
|
||||
import { httpFetch } from '../common/request.ts'
|
||||
import { getCredentials } from '../auth/store.ts'
|
||||
@@ -65,17 +65,17 @@ export const unescapeHtml = (s: any): string =>
|
||||
})
|
||||
|
||||
/** 上游 New(cookie):cookie 仅在有值时随请求发送(匿名可用) */
|
||||
export const get5singCookie = (): string => String((getCredentials('5s') as { cookie?: string } | null)?.cookie ?? '')
|
||||
export const get5singCookie = (): string => String((getCredentials('5sing') as { cookie?: string } | null)?.cookie ?? '')
|
||||
|
||||
/** 登录态(凭据存在性判断,与 me 平台 status 对齐) */
|
||||
export const getStatus = () => {
|
||||
const cred = getCredentials('5s') as { cookie?: string, updatedAt?: string } | null
|
||||
const cred = getCredentials('5sing') as { cookie?: string, updatedAt?: string } | null
|
||||
return cred?.cookie ? { logged: true, updatedAt: cred.updatedAt } : { logged: false }
|
||||
}
|
||||
|
||||
/** 是否显式声明了非本平台的 source(上游 GetLyrics/GetDownloadURL 的 source mismatch 检查) */
|
||||
export const isForeignSource = (songInfo: any): boolean =>
|
||||
Boolean(songInfo?.source) && songInfo.source !== '5s'
|
||||
Boolean(songInfo?.source) && songInfo.source !== '5sing'
|
||||
|
||||
/**
|
||||
* 带 Cookie/UA 的请求(上游 utils.Get + http.Client 自动跟跳的等价实现)
|
||||
|
||||
Reference in new issue
Block a user