refactor(sdk): 审计 P2 卫生项清理——请求层收敛与死代码清除

- 删除旧 sdk/request.ts,全部 import 收敛到 sdk/common/request.ts(85 文件)
- 注释死代码 630+ 行、裸 console.log 吞错 6 处、未使用符号 26 处清零
- bili 状态校验去重;utils DNS 预解析失败日志改 warn 带上下文
- CODE-AUDIT.md:第四节 P2 清理记录 + 第八节线索逐条复核结论(kw/kg/tx/wy parse 骨架相似实质各异,不抽公共层)
This commit is contained in:
snowy-yang committed 2026-10-03 20:48:54 +08:00
1 parent ecca1b8458
commit 8c02fb5c15
85 files changed
+112 -839

No files matched your search

+3 -1
View File
@@ -2,7 +2,9 @@
**检查时间**:2026-10-03
**修复状态**(复查后更新):P0-1 ~ P0-3、P1-4 ~ P1-16 **全部已修复**(含测试隔离与 isolated-vm 原生构建);
第五节「文档与代码不一致」已按 `/sources` 与路由实现逐条校准;第八节列出的未复核线索与第四节 P2 项**未动**,留待后续。
第四节 P2 卫生项已于 2026-10-03 清理完毕(注释死代码 630+ 行、裸 `console.log` 吞错 6 处、未使用符号 26 处清零、bili 状态校验去重);
第八节线索逐条复核:qian 导出名、migu 异常消息、Quality `preview`、bili 重试重复均已过时(早前重写时已解决),kw/kg/tx/wy parse/playlist 仅有骨架相似、实质各异,评估后不抽公共层。
第五节「文档与代码不一致」已按 `/sources` 与路由实现逐条校准。
各条的修复要点见下文对应条目。
**检查范围**:`src/`(15 文件 / 3,103 行)、`sdk/`(173 文件 / 21,919 行)、`test/`(2 文件 / 1,081 行),共约 26,100 行 TypeScript
+1 -1
View File
@@ -4,7 +4,7 @@
// 上游 New(cookie) 的 cookie 实际参与请求(搜索/getSongUrl/newget/歌单页),
// 这里按约定从 sdk/auth/store.ts 的 getCredentials('5s') 读取(无则匿名)。
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { getCredentials } from '../auth/store.ts'
export const UserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36'
+1 -1
View File
@@ -10,7 +10,7 @@
// 统一接口约定:apis('jx').getMusicUrl(songInfo, type) → { promise, cancelHttp }
// promise resolve → { type, url }(type 为实际命中档位,可能低于请求值)
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { jooxHeaders, normalizeJooxID } from '../joox/util.ts'
import { buildTrackUrl } from '../joox/musicInfo.ts'
+1 -1
View File
@@ -3,7 +3,7 @@
// 实测(2026-10-02):免费章节匿名也返回 status=2 "收费章节未购买"——必须携带 /login/lr 登录会话;
// 成功响应 list[0].path 为音频直链。
// songmid 为复合 id "<bookId>:<section>"(/album/lr 章节列表产出);纯 bookId 视为第一章
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { getLrCookie } from '../lr/auth.ts'
const UA_MI = 'Mozilla/5.0 (Linux; U; Android 4.1.1; zh-cn; MI2 Build/JRO03L) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 XiaoMi/MiuiBrowser/1.0'
+1 -1
View File
@@ -32,7 +32,7 @@ const pickUrl = (info: any, type: string) => {
/** 剧条目 "d:<drama_id>" → 首个免费集的 sound_id(getdrama episodes.episode[]) */
const resolveDramaFirstFree = async(dramaId: string): Promise<string> => {
const { httpFetch } = await import('../request.ts')
const { httpFetch } = await import('../common/request.ts')
const { statusCode, body } = await httpFetch(`https://www.missevan.com/dramaapi/getdrama?drama_id=${dramaId}`, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36',
+2 -11
View File
@@ -2,9 +2,9 @@
// 命中可访问的最高档位;匿名仅 PQ(128k) 可用,HQ/SQ/ZQ 各档需登录 Cookie。
// 上游对应:music-lib/migu/download.go(GetDownloadInfo / buildMiguDownloadCandidates /
// selectHighestValidDownload / rewriteMiguToneURL / embeddedDownloadInfoFromResponse)
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import {
AndroidChannel, AndroidUA, AndroidVersion, buildUrl, decryptZ3D, firstNonEmpty,
AndroidChannel, AndroidUA, AndroidVersion, decryptZ3D, firstNonEmpty,
getMiguCookie, isMiguAudioMagic, miguFormatExt, TONE_QUALITY, type MiguSongItem,
} from '../migu/util.ts'
@@ -112,15 +112,6 @@ const normalizeDownloadURL = (rawURL?: string | null): string => {
return url
}
const isEncryptedDirectURL = (rawURL: string): boolean => {
try {
const decodedPath = decodeURIComponent(new URL(rawURL).pathname)
return decodedPath.includes('wav_3d/') && !decodedPath.includes('wav_3d_60s/')
} catch {
return false
}
}
interface DownloadInfo {
url: string
format: string
+1 -1
View File
@@ -7,7 +7,7 @@
// - ampGet/ampHeaders:amp-api 请求(httpFetch 不自动跟 302,首页跳转用 fetchFollow 手动跟)
// - 模型转换:appleSongFromCatalog / appleAlbumToPlaylist / applePlaylistToPlaylist(字段映射同上游)
// - ID 提取:extractAppleID / extractAppleIDFromPath / isAppleRawID(apple_test.go 的断言全部逐条保留)
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { formatPlayTime } from '../common/utils.ts'
import { getCredentials } from '../auth/store.ts'
-1
View File
@@ -11,7 +11,6 @@ import { generateSimulate } from './kgSimulate.ts'
import type { KgCredentials } from './store.ts'
const md5 = s => crypto.createHash('md5').update(s).digest('hex')
const SALT_KEY = '57ae12eb6890223e355ccfcb74edf70d'
const SALT_SIGN = 'OIlwieks28dk2k092lksi2UIkp'
const APPID = 1005
const CLIENTVER = 11430
+14 -14
View File
@@ -157,24 +157,28 @@ export const makeSong = ({ bvid, cid, name, artist, album, duration, cover, page
// ---- 视频/分P/合集接口(上游 bilibili.go 各 fetch* 的对应实现) ----
/** B 站 API 统一校验:HTTP 200 且业务码 code=0,否则抛带上下文的错误 */
const assertApiOk = (what: string, statusCode: number, body: any): void => {
if (statusCode !== 200 || !body || body.code !== 0) {
throw new Error(`B站${what}获取失败(${body?.code ?? `HTTP ${statusCode}`}${body?.message ? `: ${body.message}` : ''})`)
}
}
/** 视频详情(x/web-interface/view);bvid 或 aid 均可,返回 data 节点 */
export const fetchView = async(id: string): Promise<any> => {
const raw = String(id || '').trim()
if (!raw) throw new Error('B站视频 id 为空')
const query = /^BV/i.test(raw) ? `bvid=${encodeURIComponent(raw)}` : `aid=${encodeURIComponent(raw.replace(/^av/i, ''))}`
const { statusCode, body } = await apiGet(`https://api.bilibili.com/x/web-interface/view?${query}`)
if (statusCode !== 200 || !body || body.code !== 0 || !body.data) {
throw new Error(`B站视频信息获取失败(${body?.code ?? `HTTP ${statusCode}`}${body?.message ? `: ${body.message}` : ''})`)
}
assertApiOk('视频信息', statusCode, body)
if (!body.data) throw new Error('B站视频信息获取失败(data 为空)')
return body.data
}
/** 分P列表(x/player/pagelist),返回 data 数组 */
export const fetchPageList = async(bvid: string): Promise<any[]> => {
const { statusCode, body } = await apiGet(`https://api.bilibili.com/x/player/pagelist?bvid=${encodeURIComponent(bvid)}`)
if (statusCode !== 200 || !body || body.code !== 0) {
throw new Error(`B站分P列表获取失败(${body?.code ?? `HTTP ${statusCode}`}${body?.message ? `: ${body.message}` : ''})`)
}
assertApiOk('分P列表', statusCode, body)
return body.data ?? []
}
@@ -188,10 +192,8 @@ export const fetchSeasonArchiveIndex = async(mid: string | number, seasonId: str
let pageNum = 1
const pageSize = 30
for (;;) {
const { body } = await apiGet(`https://api.bilibili.com/x/space/ugc/season?mid=${mid}&season_id=${seasonId}&page_num=${pageNum}&page_size=${pageSize}`)
if (!body || body.code !== 0) {
throw new Error(`B站合集信息获取失败(${body?.code ?? '非 JSON 响应'}${body?.message ? `: ${body.message}` : ''})`)
}
const { statusCode, body } = await apiGet(`https://api.bilibili.com/x/space/ugc/season?mid=${mid}&season_id=${seasonId}&page_num=${pageNum}&page_size=${pageSize}`)
assertApiOk('合集信息', statusCode, body)
if (!seasonTitle) seasonTitle = body.data?.season?.title || ''
if (!seasonCover) seasonCover = body.data?.season?.cover || ''
const archives = body.data?.archives ?? []
@@ -276,10 +278,8 @@ export const fetchSeasonSongs = async(mid: string | number, seasonId: string | n
let pageNum = 1
const pageSize = 30
for (;;) {
const { body } = await apiGet(`https://api.bilibili.com/x/space/ugc/season?mid=${mid}&season_id=${seasonId}&page_num=${pageNum}&page_size=${pageSize}`)
if (!body || body.code !== 0) {
throw new Error(`B站合集列表获取失败(${body?.code ?? '非 JSON 响应'}${body?.message ? `: ${body.message}` : ''})`)
}
const { statusCode, body } = await apiGet(`https://api.bilibili.com/x/space/ugc/season?mid=${mid}&season_id=${seasonId}&page_num=${pageNum}&page_size=${pageSize}`)
assertApiOk('合集列表', statusCode, body)
if (!seasonTitle) seasonTitle = body.data?.season?.title || ''
if (!seasonCover) seasonCover = body.data?.season?.cover || ''
const archives = body.data?.archives ?? []
+1 -1
View File
@@ -97,5 +97,5 @@ export const decodeName = (str = '') => {
.replace(/&#39;/g, "'")
.replace(/&apos;/g, "'")
.replace(/&nbsp;/g, ' ')
.replace(/&#(\d+);/g, (match, num) => String.fromCharCode(parseInt(num)))
.replace(/&#(\d+);/g, (_, num) => String.fromCharCode(parseInt(num)))
}
-4
View File
@@ -176,10 +176,6 @@ export const findMusic = async (
const isEqualsInterval = (intv: number) =>
Math.abs((fInterval || intv) - (intv || fInterval)) < 5
const isIncludesName = (n: string) => fMusicName.includes(n) || n.includes(fMusicName)
const isIncludesSinger = (sng: string) =>
fSinger ? fSinger.includes(sng) || sng.includes(fSinger) : true
const isEqualsAlbum = (alb: string) => (fAlbumName ? fAlbumName === alb : true)
let candidates: MatchCandidate[] = []
for (const source of lists) {
+1 -1
View File
@@ -5,7 +5,7 @@
// 兜底:www.joox.com/<region>/album/<id> SSR __NEXT_DATA__.props.pageProps.albumData(上游做法,需 X-Forwarded-For)
// 链接解析:ParseAlbum 的正则与裸 ID 规则同上游。
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import {
jooxHeaders,
jooxSign,
+1 -1
View File
@@ -6,7 +6,7 @@
// 解码:base64 → UTF-8 → HTML 实体(&amp; 等)→ { lyric, tlyric: '' }
// 统一接口约定:{ promise, cancelHttp };promise resolve → { lyric, tlyric }
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { jooxHeaders, decodeJooxLyric, normalizeJooxID, firstNonEmpty } from './util.ts'
import { buildTrackUrl } from './musicInfo.ts'
+1 -1
View File
@@ -9,7 +9,7 @@
// 返回体含 play_url_list / refrain_url / lrc_content(base64) / qrc_content(base64) /
// has_hq / has_hifi / vip_flag / node_is_preview 等。
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import {
jooxHeaders,
jooxSign,
+2 -2
View File
@@ -4,13 +4,13 @@
// 该接口无分页参数(上游也未分页),page 参数仅为统一契约保留;allPage 恒为 1。
// 实测(2026-10-02):英文/中文关键词均 HTTP 200。
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { jooxHeaders } from './util.ts'
import { jooxSongFromSearch } from './song.ts'
const SEARCH_URL = 'https://cache.api.joox.com/openjoox/v3/search'
export const search = async(keyword: string, page = 1, limit = 20) => {
export const search = async(keyword: string, _page = 1, limit = 20) => {
const url = `${SEARCH_URL}?country=sg&lang=zh_cn&keyword=${encodeURIComponent(String(keyword ?? ''))}`
const { statusCode, body } = await httpFetch(url, { headers: jooxHeaders(), timeout: 20000 }).promise as any
if (statusCode != 200 || !body?.section_list) {
+2 -2
View File
@@ -6,8 +6,8 @@
import { normalizeJooxID } from './util.ts'
import { getTrackDetail, pickTrackUrl } from './musicInfo.ts'
import { jooxSongFromDetail } from './song.ts'
import { getAlbumDetail, parseAlbum } from './album.ts'
import { getPlaylistDetail, parsePlaylist } from './playlist.ts'
import { parseAlbum } from './album.ts'
import { parsePlaylist } from './playlist.ts'
/** 歌曲链接 → Song(上游 Parse 的正则与裸 ID 规则) */
export const parseSong = async(link: string) => {
+1 -1
View File
@@ -7,7 +7,7 @@
// 分类:www.joox.com/sg/playlist(302 → /<region> 首页)__NEXT_DATA__.props.pageProps.mlList
// 链接解析:joox.com/{region}/playlist/<id>、playlistid/id 查询参数、裸 ID
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import {
jooxHeaders,
jooxSign,
+1 -1
View File
@@ -11,7 +11,7 @@
// 在签名参数里作为 uid/usk 参与(可拿到完整曲目/更高档位)。
import crypto from 'node:crypto'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { getCredentials } from '../auth/store.ts'
export const JOOX_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36'
+1 -1
View File
@@ -1,6 +1,6 @@
import { getMusicInfosByList } from './musicInfo.ts'
import { createHttpFetch } from './util.ts'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
const albumCover = (url: any): string => String(url ?? '').replace('{size}', '240')
+1 -5
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { decodeName, dateFormat2 } from '../index.ts'
import { signatureParams } from './util.ts'
// import { getMusicInfoRaw } from './musicInfo.ts'
@@ -22,13 +22,11 @@ export default {
},
})
const { body, statusCode } = await _requestObj.promise
// console.log(body)
if (statusCode != 200 || body.err_code !== 0) throw new Error('获取评论失败')
const total = body.count ?? 0
return { source: 'kg', comments: this.filterComment(body.list || []), total, page, limit, maxPage: Math.ceil(total / limit) || 1 }
},
async getHotComment({ hash }, page = 1, limit = 20) {
// console.log(songmid)
if (this._requestObj2) this._requestObj2.cancelHttp()
let timestamp = Date.now()
const params = `dfid=0&mid=16249512204336365674023395779019&clienttime=${timestamp}&uuid=0&extdata=${hash}&appid=1005&code=fc4be23b4e972707f36b8a828a93ba8a&schash=${hash}&clientver=11409&p=${page}&clienttoken=&pagesize=${limit}&ver=10&kugouid=0`
@@ -39,7 +37,6 @@ export default {
},
})
const { body, statusCode } = await _requestObj2.promise
// console.log(body)
if (statusCode != 200 || body.err_code !== 0) throw new Error('获取热门评论失败')
const total = body.count ?? 0
return { source: 'kg', comments: this.filterComment(body.list || []), total, page, limit, maxPage: Math.ceil(total / limit) || 1 }
@@ -57,7 +54,6 @@ export default {
},
})
const { body, statusCode } = await _requestObj2.promise
// console.log(body)
if (statusCode != 200 || body.err_code !== 0) throw new Error('获取回复评论失败')
return { source: 'kg', comments: this.filterComment(body.list || []) }
},
+1 -2
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { decodeName } from '../index.ts'
import type { HttpRequestObject } from '../types.ts'
@@ -21,7 +21,6 @@ export default {
})
const { body, statusCode } = await _requestObj.promise
if (statusCode != 200 || body.errcode !== 0) throw new Error('获取热搜词失败')
// console.log(body, statusCode)
return { source: 'kg', list: this.filterList(body.data.list) }
},
filterList(rawList) {
+2 -23
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { decodeName, formatPlayTime, sizeFormate } from '../index.ts'
import { formatSingerName } from '../utils.ts'
import type { HttpRequestObject, SongType, QualityInfo } from '../types.ts'
@@ -91,7 +91,6 @@ export default {
return arr.join('、')
},
filterData(rawList) {
// console.log(rawList)
return rawList.map(item => {
const types: Array<SongType & { hash?: string }> = []
const _types: Record<string, QualityInfo & { hash?: string }> = {}
@@ -147,7 +146,6 @@ export default {
},
filterBoardsData(rawList) {
// console.log(rawList)
let list: Array<{ id: string; name: string; bangid: string }> = []
for (const board of rawList) {
if (board.isvol != 1) continue
@@ -159,24 +157,7 @@ export default {
}
return list
},
async getBoards(retryNum = 0) {
// if (++retryNum > 3) return Promise.reject(new Error('try max num'))
// let response
// try {
// response = await this.getBoardsData()
// } catch (error) {
// return this.getBoards(retryNum)
// }
// // console.log(response.body)
// if (response.statusCode !== 200 || response.body.errcode !== 0) return this.getBoards(retryNum)
// const list = this.filterBoardsData(response.body.data.info)
// console.log(list)
// // console.log(JSON.stringify(list))
// this.list = list
// return {
// list,
// source: 'kg',
// }
async getBoards() {
this.list = boardList
return {
list: boardList,
@@ -189,11 +170,9 @@ export default {
if (body.errcode != 0) return this.getList(bangid, page, retryNum)
// console.log(body)
let total = body.data.total
let limit = 100
let listData = this.filterData(body.data.info)
// console.log(listData)
return {
total,
list: listData,
+2 -24
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { decodeKrc } from './krcDecode.ts'
export default {
@@ -13,28 +13,6 @@ export default {
}
return parseInt(intv as unknown as string)
},
// getLyric(songInfo, tryNum = 0) {
// let requestObj = httpFetch(`http://m.kugou.com/app/i/krc.php?cmd=100&keyword=${encodeURIComponent(songInfo.name)}&hash=${songInfo.hash}&timelength=${songInfo._interval || this.getIntv(songInfo.interval)}&d=0.38664927426725626`, {
// headers: {
// 'KG-RC': 1,
// 'KG-THash': 'expand_search_manager.cpp:852736169:451',
// 'User-Agent': 'KuGou2012-9020-ExpandSearchManager',
// },
// })
// requestObj.promise = requestObj.promise.then(({ body, statusCode }) => {
// if (statusCode !== 200) {
// if (tryNum > 5) return Promise.reject(new Error('歌词获取失败'))
// let tryRequestObj = this.getLyric(songInfo, ++tryNum)
// requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
// return tryRequestObj.promise
// }
// return {
// lyric: body,
// tlyric: '',
// }
// })
// return requestObj
// },
searchLyric(name, hash, time, tryNum = 0) {
let requestObj = httpFetch(`http://lyrics.kugou.com/search?ver=1&man=yes&client=pc&keyword=${encodeURIComponent(name)}&hash=${hash}&timelength=${time}&lrctxt=1`, {
headers: {
@@ -92,7 +70,7 @@ export default {
return requestObj
},
getLyric(songInfo, tryNum = 0) {
getLyric(songInfo) {
let requestObj = this.searchLyric(songInfo.name, songInfo.hash, songInfo._interval || this.getIntv(songInfo.interval))
requestObj.promise = requestObj.promise.then(result => {
-1
View File
@@ -39,7 +39,6 @@ const createGetMusicInfosTask = (hashs: Array<{ hash: string }>): Array<Promise<
}
export const filterMusicInfoList = (rawList: any[]): SongInfo[] => {
// console.log(rawList)
let ids = new Set()
let list: SongInfo[] = []
rawList.forEach(item => {
+1 -1
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { decodeName, formatPlayTime, sizeFormate } from '../index.ts'
import { formatSingerName } from '../utils.ts'
import type { SongType, QualityInfo } from '../types.ts'
+1 -1
View File
@@ -3,7 +3,7 @@
// 上游对应:music-lib/kugou/song.go Parse / kugou.go fetchSongInfo
// 元数据 m.kugou.com/app/i/getSongInfo.php(songName/author_name/timeLength/album_img/bitRate)
// 另尽力合并 musicInfo.getMusicInfo 的专辑/多档 hash,直链取流失败不影响返回(上游为必得 URL)。
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { formatPlayTime, decodeName } from '../index.ts'
import kgApi from '../apis/kg.ts'
import { getMusicInfo } from './musicInfo.ts'
+1 -1
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
export default {
getPic(songInfo) {
+1 -1
View File
@@ -3,7 +3,7 @@
// 链接解析优先复用既有 sdk/kg/songList.ts 的 getUserListDetail / getListDetailBySpecialId /
// getListInfoByChain / decodeGcid 等能力(覆盖 special/single、songlist/gcid、share chain、
// zlist、global_collection_id 等形态),本模块只做统一形状归一。
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import songList from './songList.ts'
const UA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1'
+2 -145
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { decodeName, formatPlayTime, sizeFormate, dateFormat, formatPlayCount } from '../index.ts'
import { toMD5 } from '../utils.ts'
import { signatureParams } from './util.ts'
@@ -66,51 +66,6 @@ export default {
// https://www.kugou.com/yy/special/single/1067062.html
listDetailLink: /^.+\/(\d+)\.html(?:\?.*|&.*$|#.*$|$)/,
},
// async getGlobalSpecialId(specialId) {
// return httpFetch(`http://mobilecdnbj.kugou.com/api/v5/special/info?specialid=${specialId}`, {
// headers: {
// 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; HLK-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Mobile Safari/537.36 EdgA/104.0.1293.70',
// },
// }).promise.then(({ body }) => {
// // console.log(body)
// if (!body.data.global_specialid) Promise.reject(new Error('Failed to get global collection id.'))
// return body.data.global_specialid
// })
// },
// async getListInfoBySpecialId(special_id, retry = 0) {
// if (++retry > 2) throw new Error('failed')
// return httpFetch(`https://m.kugou.com/plist/list/${special_id}/?json=true`, {
// headers: {
// 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; HLK-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Mobile Safari/537.36 EdgA/104.0.1293.70',
// },
// follow_max: 2,
// }).promise.then(({ body }) => {
// // console.log(body)
// if (!body.info.list) return this.getListInfoBySpecialId(special_id, retry)
// let listinfo = body.info.list
// return {
// listInfo: {
// name: listinfo.specialname,
// image: listinfo.imgurl.replace('{size}', '150'),
// intro: listinfo.intro,
// author: listinfo.nickname,
// playcount: listinfo.playcount,
// total: listinfo.songcount,
// },
// globalSpecialId: listinfo.global_specialid,
// }
// })
// },
// async getSongListDetailByGlobalSpecialId(id, page, limit = 100, retry = 0) {
// if (++retry > 2) throw new Error('failed')
// console.log(id)
// const params = `specialid=0&need_sort=1&module=CloudMusic&clientver=11409&pagesize=${limit}&global_collection_id=${id}&userid=0&page=${page}&type=1&area_code=1&appid=1005`
// return httpFetch(`http://pubsongscdn.tx.kugou.com/v2/get_other_list_file?${params}&signature=${signatureParams(params)}`).promise.then(({ body }) => {
// // console.log(body)
// if (body.data?.info == null) return this.getSongListDetailByGlobalSpecialId(id, page, limit, retry)
// return body.data.info
// })
// },
parseHtmlDesc(html) {
const prefix = '<div class="pc_specail_text pc_singer_tab_content" id="specailIntroduceWrap">'
let index = html.indexOf(prefix)
@@ -148,30 +103,9 @@ export default {
name,
img: pic,
desc,
// author: body.result.info.userinfo.username,
// play_count: formatPlayCount(body.result.listen_num),
},
}
// const globalSpecialId = await this.getGlobalSpecialId(id)
// const limit = 100
// const listData = await this.getSongListDetailByGlobalSpecialId(globalSpecialId, page, limit)
// if (!Array.isArray(listData))
// return this.getUserListDetail2(globalSpecialId)
// return {
// list: this.filterDatav9(listData),
// page,
// limit,
// total: listInfo.total,
// source: 'kg',
// info: {
// name: listInfo.name,
// img: listInfo.image,
// desc: listInfo.intro,
// author: listInfo.author,
// play_count: formatPlayCount(listInfo.playcount),
// },
// }
},
getInfoUrl(tagId?) {
return tagId
@@ -276,10 +210,9 @@ export default {
try {
result = await httpFetch(url, options).promise
} catch (err) {
console.log(err)
console.warn(`[kg] 歌单请求失败将重试(${retryNum + 1}/3) ${url}: ${(err as Error).message}`)
return this.createHttp(url, options, ++retryNum)
}
// console.log(result.statusCode, result.body)
if (result.statusCode !== 200 ||
(
(result.body.error_code !== undefined
@@ -349,7 +282,6 @@ export default {
},
body: { appid: 1001, clientver: 9020, mid: '21511157a05844bd085308bc76ef3343', clienttime: 640612895, key: '36164c4015e704673c588ee202b9ecb8', data: id },
})
// console.log(songInfo)
// type 1单曲,2歌单,3电台,4酷狗码,5别人的播放队列
let songList
let info = songInfo.info
@@ -372,7 +304,6 @@ export default {
},
body: { appid: 1001, clientver: 9020, mid: '21511157a05844bd085308bc76ef3343', clienttime: 640612895, key: '36164c4015e704673c588ee202b9ecb8', data: { id: info.id, type: 3, userid: info.userid, collect_type: 0, page: 1, pagesize: info.count } },
})
// console.log(songList)
}
let list = await this.getMusicInfos(songList || songInfo.list)
return {
@@ -402,7 +333,6 @@ export default {
else return this.getUserListDetail4(songInfo, chain, page).catch(() => this.getUserListDetail5(chain))
}
let list = await this.getMusicInfos(songInfo.list)
// console.log(info, songInfo)
return {
list,
page: 1,
@@ -468,7 +398,6 @@ export default {
}
let result = await Promise.all(tasks).then(([...datas]) => datas.flat())
result = await this.getMusicInfos(result)
// console.log(result)
return {
list: result,
page,
@@ -519,7 +448,6 @@ export default {
})
const songInfo = await this.createGetListDetail2Task(id, info.songcount)
let list = await this.getMusicInfos(songInfo)
// console.log(info, songInfo, list)
return {
list,
page: 1,
@@ -561,7 +489,6 @@ export default {
if (result) result = JSON.parse(result[1])
this.cache.set(chain, result)
result = await this.getMusicInfos(result)
// console.log(info, songInfo)
return result
},
@@ -618,9 +545,7 @@ export default {
},
})
// console.log(info)
let result = await this.getMusicInfos(info.info)
// console.log(info, songInfo)
return result
},
@@ -655,10 +580,8 @@ export default {
})
const { headers: { location }, statusCode, body } = await requestObj_listDetailLink.promise
const loc = location as string | undefined
// console.log(body, location)
if (statusCode > 400) return this.getUserListDetail(link, page, ++retryNum)
if (loc) {
// console.log(location)
if (loc.includes('global_collection_id')) return this.getUserListDetail2(loc.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1'))
if (loc.includes('gcid_')) {
let gcid = link.match(/gcid_\w+/)?.[0]
@@ -679,7 +602,6 @@ export default {
return this.getUserListDetail(link, page, ++retryNum)
} else return this.getUserListDetail3(loc.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page)
}
// console.log('location', location)
return this.getUserListDetail(loc, page, ++retryNum)
}
if (typeof body == 'string') {
@@ -713,7 +635,6 @@ export default {
return this.getListDetailBySpecialId(id, page)
},
filterData(rawList) {
// console.log(rawList)
return rawList.map(item => {
const types: Array<{ type: string; size: string; hash?: string }> = []
const _types: Record<string, QualityInfo & { hash?: string }> = {}
@@ -766,72 +687,9 @@ export default {
}
})
},
// getSinger(singers) {
// let arr = []
// singers?.forEach(singer => {
// arr.push(singer.name)
// })
// return arr.join('、')
// },
// v9 API
// filterDatav9(rawList) {
// console.log(rawList)
// return rawList.map(item => {
// const types = []
// const _types = {}
// item.relate_goods.forEach(qualityObj => {
// if (qualityObj.level === 2) {
// let size = sizeFormate(qualityObj.size)
// types.push({ type: '128k', size, hash: qualityObj.hash })
// _types['128k'] = {
// size,
// hash: qualityObj.hash,
// }
// } else if (qualityObj.level === 4) {
// let size = sizeFormate(qualityObj.size)
// types.push({ type: '320k', size, hash: qualityObj.hash })
// _types['320k'] = {
// size,
// hash: qualityObj.hash,
// }
// } else if (qualityObj.level === 5) {
// let size = sizeFormate(qualityObj.size)
// types.push({ type: 'flac', size, hash: qualityObj.hash })
// _types.flac = {
// size,
// hash: qualityObj.hash,
// }
// } else if (qualityObj.level === 6) {
// let size = sizeFormate(qualityObj.size)
// types.push({ type: 'flac24bit', size, hash: qualityObj.hash })
// _types.flac24bit = {
// size,
// hash: qualityObj.hash,
// }
// }
// })
// const nameInfo = item.name.split(' - ')
// return {
// singer: this.getSinger(item.singerinfo),
// name: decodeName((nameInfo[1] ?? nameInfo[0]).trim()),
// albumName: decodeName(item.albuminfo.name),
// albumId: item.albuminfo.id,
// songmid: item.audio_id,
// source: 'kg',
// interval: formatPlayTime(item.timelen / 1000),
// img: null,
// lrc: null,
// hash: item.hash,
// types,
// _types,
// typeUrl: {},
// }
// })
// },
// hash list filter
filterData2(rawList) {
// console.log(rawList)
let ids = new Set()
let list: SongInfo[] = []
rawList.forEach(item => {
@@ -959,7 +817,6 @@ export default {
return httpFetch(`http://msearchretry.kugou.com/api/v3/search/special?keyword=${encodeURIComponent(text)}&page=${page}&pagesize=${limit}&showtype=10&filter=0&version=7910&sver=2`)
.promise.then(({ body }) => {
if (body.errcode != 0) throw new Error('filed')
// console.log(body.data.info)
return {
list: body.data.info.map(item => {
return {
+2 -4
View File
@@ -1,9 +1,8 @@
import { toMD5 } from '../utils.ts'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import type { HttpOptions } from '../types.ts'
// s.content[0].lyricContent.forEach(([str]) => {
// console.log(str)
// })
/**
@@ -32,10 +31,9 @@ export const createHttpFetch = async(url: string, options?: HttpOptions, retryNu
try {
result = await httpFetch(url, options).promise
} catch (err) {
console.log(err)
console.warn(`[kg] 请求失败将重试(${retryNum + 1}/3) ${url}: ${(err as Error).message}`)
return createHttpFetch(url, options, ++retryNum)
}
// console.log(result.statusCode, result.body)
if (result.statusCode !== 200 ||
(
result.body.error_code ??
+2 -32
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { decodeName } from '../index.ts'
import { formatSinger, objStr2JSON } from './util.ts'
import type { Quality } from '../types.ts'
@@ -17,9 +17,7 @@ export default {
limit_list: 36,
limit_song: 1000,
filterListDetail(rawList, albumName, albumId) {
// console.log(rawList)
// console.log(rawList.length, rawList2.length)
return rawList.map((item, inedx) => {
return rawList.map(item => {
let formats = item.formats.split('|')
let types: Array<{ type: Quality; size: string | null }> = []
let _types: Partial<Record<Quality, { size: string | null }>> = {}
@@ -92,7 +90,6 @@ export default {
return requestObj_listDetail.promise.then(({ statusCode, body }) => {
if (statusCode !== 200) return this.getAlbumListDetail(id, page, ++retryNum)
body = objStr2JSON(body)
// console.log(body)
if (!body.musiclist) return this.getAlbumListDetail(id, page, ++retryNum)
body.name = decodeName(body.name)
return {
@@ -235,31 +232,4 @@ export default {
}
throw new Error('无效的酷我专辑链接')
},
// getAlbumListDetail(id, page, retryNum = 0) {
// if (retryNum > 2) return Promise.reject(new Error('try max num'))
// return tokenRequest(`http://www.kuwo.cn/api/www/album/albumInfo?albumId=${id}&pn=${page}&rn=${this.limit_song}&httpsStatus=1`).then((resp) => {
// return resp.promise.then(({ statusCode, body }) => {
// console.log(body)
// return Promise.reject(new Error('failed'))
// // if (statusCode !== 200) return this.getAlbumListDetail(id, page, ++retryNum)
// // const data = body.data
// // console.log(data)
// // if (!data.musicList) return this.getAlbumListDetail(id, page, ++retryNum)
// // return {
// // list: this.filterListDetail(data.musiclist),
// // page,
// // limit: this.limit_song,
// // total: data.total,
// // source: 'kw',
// // info: {
// // name: data.album,
// // img: data.pic,
// // desc: data.albuminfo,
// // author: data.artist,
// // play_count: this.formatPlayCount(data.playCnt),
// // },
// // }
// })
// })
// },
}
+1 -3
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { dateFormat2 } from '../index.ts'
import type { HttpRequestObject } from '../types.ts'
@@ -15,7 +15,6 @@ export default {
})
const { body, statusCode } = await _requestObj.promise
if (statusCode != 200 || body.code != '200') throw new Error('获取评论失败')
// console.log(body)
const total = body.comments_counts
return {
@@ -37,7 +36,6 @@ export default {
})
const { body, statusCode } = await _requestObj2.promise
if (statusCode != 200 || body.code != '200') throw new Error('获取热门评论失败')
// console.log(body)
const total = body.hot_comments_counts
return {
+1 -2
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import type { HttpRequestObject } from '../types.ts'
export default {
@@ -14,7 +14,6 @@ export default {
})
const { body, statusCode } = await _requestObj.promise
if (statusCode != 200 || body.status !== 'ok') throw new Error('获取热搜词失败')
// console.log(body, statusCode)
return { source: 'kw', list: this.filterList(body.tagvalue) }
},
filterList(rawList) {
+1 -21
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import musicSearch from './musicSearch.ts'
import { formatSinger } from './util.ts'
import leaderboard from './leaderboard.ts'
@@ -21,22 +21,6 @@ const kw = {
album,
playlist,
parse,
// context: null,
// init(context) {
// if (this.isInited) return
// this.isInited = true
// this.context = context
// // this.musicSearch.search('我又想你了').then(res => {
// // console.log(res)
// // })
// // this.getMusicUrl('62355680', '320k').then(url => {
// // console.log(url)
// // })
// },
musicSearch,
leaderboard,
@@ -49,7 +33,6 @@ const kw = {
},
handleMusicInfo(songInfo) {
return this.getMusicInfo(songInfo).then(info => {
// console.log(JSON.stringify(info))
songInfo.name = info.name
songInfo.singer = formatSinger(info.artist)
songInfo.img = info.pic
@@ -99,9 +82,6 @@ const kw = {
return `http://www.kuwo.cn/play_detail/${songInfo.songmid}`
},
// init() {
// return getToken()
// },
}
export default kw
+2 -24
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { formatPlayTime, decodeName } from '../index.ts'
import { formatSinger, wbdCrypto } from './util.ts'
import type { HttpRequestObject, Quality, QualityInfo, SongType } from '../types.ts'
@@ -167,7 +167,6 @@ export default {
},
filterBoardsData(rawList) {
// console.log(rawList)
let list: any[] = []
for (const board of rawList) {
if (board.source != '1') continue
@@ -179,24 +178,7 @@ export default {
}
return list
},
async getBoards(retryNum = 0) {
// if (++retryNum > 3) return Promise.reject(new Error('try max num'))
// let response
// try {
// response = await this.getBoardsData()
// } catch (error) {
// return this.getBoards(retryNum)
// }
// console.log(response.body)
// if (response.statusCode !== 200 || !response.body.child) return this.getBoards(retryNum)
// const list = this.filterBoardsData(response.body.child)
// // console.log(list)
// console.log(JSON.stringify(list))
// this.list = list
// return {
// list,
// source: 'kw',
// }
async getBoards() {
this.list = boardList
return {
list: boardList,
@@ -213,7 +195,6 @@ export default {
return request.then(({ statusCode, body }) => {
const rawData = wbdCrypto.decodeData(body)
// console.log(rawData)
const data = rawData.data
if (statusCode !== 200 || rawData.code != 200 || !data.musiclist) return this.getList(id, page, retryNum)
@@ -230,7 +211,4 @@ export default {
})
},
// getDetailPageUrl(id) {
// return `http://www.kuwo.cn/rankList/${id}`
// },
}
+1 -151
View File
@@ -1,71 +1,7 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { decodeLyric, lrcTools } from './util.ts'
import { decodeName } from '../index.ts'
/*
export default {
formatTime(time) {
let m = parseInt(time / 60)
let s = (time % 60).toFixed(2)
return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s)
},
sortLrcArr(arr) {
const lrcSet = new Set()
let lrc = []
let lrcT = []
for (const item of arr) {
if (lrcSet.has(item.time)) {
const tItem = lrc.pop()
tItem.time = lrc[lrc.length - 1].time
lrcT.push(tItem)
lrc.push(item)
} else {
lrc.push(item)
lrcSet.add(item.time)
}
}
if (lrcT.length && lrc.length > lrcT.length) {
const tItem = lrc.pop()
tItem.time = lrc[lrc.length - 1].time
lrcT.push(tItem)
}
return {
lrc,
lrcT,
}
},
transformLrc(songinfo, lrclist) {
return `[ti:${songinfo.songName}]\n[ar:${songinfo.artist}]\n[al:${songinfo.album}]\n[by:]\n[offset:0]\n${lrclist ? lrclist.map(l => `[${this.formatTime(l.time)}]${l.lineLyric}\n`).join('') : '暂无歌词'}`
},
getLyric(songId) {
const requestObj = httpFetch(`http://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId=${songId}`)
requestObj.promise = requestObj.promise.then(({ body }) => {
// console.log(body)
if (!body.data?.lrclist?.length) return Promise.reject(new Error('Get lyric failed'))
let lrcInfo
try {
lrcInfo = this.sortLrcArr(body.data.lrclist)
} catch {
return Promise.reject(new Error('Get lyric failed'))
}
// console.log(body.data.lrclist)
// console.log(lrcInfo.lrc, lrcInfo.lrcT)
// console.log({
// lyric: decodeName(this.transformLrc(body.data.songinfo, lrc)),
// tlyric: decodeName(this.transformLrc(body.data.songinfo, lrcT)),
// })
return {
lyric: decodeName(this.transformLrc(body.data.songinfo, lrcInfo.lrc)),
tlyric: lrcInfo.lrcT.length ? decodeName(this.transformLrc(body.data.songinfo, lrcInfo.lrcT)) : '',
}
})
return requestObj
},
}
*/
const buf_key = Buffer.from('yeelion')
const buf_key_len = buf_key.length
@@ -87,65 +23,11 @@ const buildParams = (id: string, isGetLyricx: boolean): string => {
return Buffer.from(output).toString('base64')
}
// console.log(buildParams('207527604', false))
// console.log(buildParams('207527604', true))
const timeExp = /^\[([\d:.]*)\]{1}/g
const existTimeExp = /\[\d{1,2}:.*\d{1,4}\]/
const lyricxTag = /^<-?\d+,-?\d+>/
export default {
/* sortLrcArr(arr) {
const lrcSet = new Set()
let lrc = []
let lrcT = []
let markIndex = []
for (const item of arr) {
if (lrcSet.has(item.time)) {
if (lrc.length < 2) continue
const index = lrc.findIndex(l => l.time == item.time)
markIndex.push(index)
if (index == lrc.length - 1) {
lrcT.push({ ...lrc[index], time: item.time })
lrc.push(item)
} else {
lrcT.push({ ...lrc[index], time: lrc[index + 1].time })
if (item.text) {
// const lastIndex = lrc.length - 1
// markIndex.push(lastIndex)
// lrcT.push({ ...lrc[lastIndex], time: lrc[lastIndex - 1].time })
lrc.push(item)
}
}
} else {
lrc.push(item)
lrcSet.add(item.time)
}
}
// console.log(markIndex)
markIndex = Array.from(new Set(markIndex))
for (let index = markIndex.length - 1; index >= 0; index--) {
lrc.splice(markIndex[index], 1)
}
// if (lrcT.length) {
// if (lrc.length * 0.4 < lrcT.length) { // 翻译数量需大于歌词数量的0.4倍,否则认为没有翻译
// const tItem = lrc.pop()
// tItem.time = lrc[lrc.length - 1].time
// lrcT.push(tItem)
// } else {
// lrc = arr
// lrcT = []
// }
// }
console.log(lrc, lrcT)
return {
lrc,
lrcT,
}
}, */
sortLrcArr(arr) {
const lrcSet = new Set()
let lrc: any[] = []
@@ -211,48 +93,17 @@ export default {
tlyric: lrcInfo.lrcT.length ? decodeName(this.transformLrc(tags, lrcInfo.lrcT)) : '',
}
},
// getLyric2(musicInfo, isGetLyricx = true) {
// const requestObj = httpFetch(`http://newlyric.kuwo.cn/newlyric.lrc?${buildParams(musicInfo.songmid, isGetLyricx)}`)
// requestObj.promise = requestObj.promise.then(({ statusCode, body, raw }) => {
// if (statusCode != 200) return Promise.reject(new Error(JSON.stringify(body)))
// return decodeLyric({ lrcBase64: raw.toString('base64'), isGetLyricx }).then(base64Data => {
// let lrcInfo
// console.log(Buffer.from(base64Data, 'base64').toString())
// try {
// lrcInfo = this.parseLrc(Buffer.from(base64Data, 'base64').toString())
// } catch {
// return Promise.reject(new Error('Get lyric failed'))
// }
// if (lrcInfo.tlyric) lrcInfo.tlyric = lrcInfo.tlyric.replace(lrcTools.rxps.wordTimeAll, '')
// lrcInfo.yrc = lrcTools.parse(lrcInfo.lyric)
// // console.log(lrcInfo.lyric)
// // console.log(lrcInfo.tlyric)
// // console.log(lrcInfo.yrc)
// // console.log(JSON.stringify(lrcInfo))
// })
// })
// return requestObj
// },
getLyric(musicInfo, isGetLyricx = true) {
// this.getLyric2(musicInfo)
const requestObj = httpFetch(`http://newlyric.kuwo.cn/newlyric.lrc?${buildParams(musicInfo.songmid, isGetLyricx)}`)
requestObj.promise = requestObj.promise.then(({ statusCode, body, raw }) => {
if (statusCode != 200) return Promise.reject(new Error(JSON.stringify(body)))
return decodeLyric({ lrcBase64: raw!.toString('base64'), isGetLyricx }).then(base64Data => {
// let lrcInfo
// try {
// lrcInfo = this.parseLrc(Buffer.from(base64Data, 'base64').toString())
// } catch {
// return Promise.reject(new Error('Get lyric failed'))
// }
let lrcInfo
// console.log(Buffer.from(base64Data, 'base64').toString())
try {
lrcInfo = this.parseLrc(Buffer.from(base64Data, 'base64').toString())
} catch (err) {
return Promise.reject(new Error('Get lyric failed'))
}
// console.log(lrcInfo)
if (lrcInfo.tlyric) lrcInfo.tlyric = lrcInfo.tlyric.replace(lrcTools.rxps.wordTimeAll, '')
try {
lrcInfo.yrc = lrcTools.parse(lrcInfo.lyric)
@@ -261,7 +112,6 @@ export default {
}
lrcInfo.lyric = lrcInfo.lyric.replace(lrcTools.rxps.wordTimeAll, '')
if (!existTimeExp.test(lrcInfo.lyric)) return Promise.reject(new Error('Get lyric failed'))
// console.log(lrcInfo)
return lrcInfo
})
})
+1 -11
View File
@@ -1,6 +1,6 @@
// import '../../polyfill/array.find.js'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { formatPlayTime, decodeName } from '../index.ts'
// import { debug } from '../../utils/env.ts'
import { formatSinger } from './util.ts'
@@ -14,21 +14,13 @@ export default {
total: 0,
page: 0,
allPage: 1,
// cancelFn: null,
musicSearch(str: string, page: number, limit: number) {
const musicSearchRequestObj = httpFetch(`http://search.kuwo.cn/r.s?client=kt&all=${encodeURIComponent(str)}&pn=${page - 1}&rn=${limit}&uid=794762570&ver=kwplayer_ar_9.2.2.1&vipver=1&show_copyright_off=1&newver=1&ft=music&cluster=0&strategy=2012&encoding=utf8&rformat=json&vermerge=1&mobi=1&issubtitle=1`)
return musicSearchRequestObj.promise
},
// getImg(songId) {
// return httpGet(`http://player.kuwo.cn/webmusic/sj/dtflagdate?flag=6&rid=MUSIC_${songId}`)
// },
// getLrc(songId) {
// return httpGet(`http://mobile.kuwo.cn/mpage/html5/songinfoandlrc?mid=${songId}&flag=0`)
// },
handleResult(rawData: any) {
const result: SongInfo[] = []
if (!rawData) return result
// console.log(rawData)
for (let i = 0; i < rawData.length; i++) {
const info = rawData[i]
let songId = info.MUSICRID.replace('MUSIC_', '')
@@ -97,7 +89,6 @@ export default {
typeUrl: {},
})
}
// console.log(result)
return result
},
search(str: string, page = 1, limit?: number, retryNum = 0) {
@@ -105,7 +96,6 @@ export default {
if (limit == null) limit = this.limit
// http://newlyric.kuwo.cn/newlyric.lrc?62355680
return this.musicSearch(str, page, limit).then(({ body: result }) => {
// console.log(result)
if (!result || (result.TOTAL !== '0' && result.SHOW === '0')) return this.search(str, page, limit, ++retryNum)
let list = this.handleResult(result.abslist)
+1 -1
View File
@@ -2,7 +2,7 @@
// 歌曲:https://www.kuwo.cn/play_detail/<rid>(含 ?/# 尾串)、m.kuwo.cn 的 musicId=<rid>、裸 rid
// 上游对应:music-lib/kuwo/song.go Parse / kuwo.go fetchFullSongInfo(元数据 m.kuwo.cn + 尽力附直链)
// 专辑 / 歌单分别复用 album.parseAlbumLink、playlist.parsePlaylistLink。
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { formatPlayTime } from '../index.ts'
import kwApi from '../apis/kw.ts'
import album from './album.ts'
+1 -1
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import type { SongInfo } from '../types.ts'
export default {
+1 -9
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { formatPlayTime, decodeName } from '../index.ts'
import { formatSinger, objStr2JSON } from './util.ts'
import album from './album.ts'
@@ -147,7 +147,6 @@ export default {
}))
},
filterList2(rawData) {
// console.log(rawData)
const list: any[] = []
rawData.forEach(item => {
if (!item.label) return
@@ -196,7 +195,6 @@ export default {
// 本方法须返回 detailId(调用方据此再查曲目),失败时应重试自身;
// 原先误调 getListDetail(返回歌单详情对象、且把 tryNum 当 page)导致返回类型错位
if (statusCode != 200 || !body.child) return this.getListDetailDigest5Info(id, ++tryNum)
// console.log(body)
return body.child.length ? body.child[0].sourceid : null
})
},
@@ -204,7 +202,6 @@ export default {
if (tryNum > 2) return Promise.reject(new Error('try max num'))
const requestObj = httpFetch(`http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=${id}&pn=${page - 1}&rn=${this.limit_song}&encode=utf-8&keyset=pl2012&identity=kuwo&pcmp4=1`)
return requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.result !== 'ok') return this.getListDetail(id, page, ++tryNum)
return {
list: this.filterListDetail(body.musiclist),
@@ -314,7 +311,6 @@ export default {
if (infoData.code != 200) return null
// console.log(infoData)
return {
name: infoData.data.userInfo.nickname + '喜欢的音乐',
img: infoData.data.userInfo.headImg,
@@ -367,13 +363,11 @@ export default {
author: '',
play_count: '',
}
// console.log(listData)
return listData
},
// 获取歌曲列表内的音乐
getListDetail(id, page, retryNum = 0) {
// console.log(id)
// https://h5app.kuwo.cn/m/bodian/collection.html?uid=000&playlistId=000&source=5&ownerId=000
// https://h5app.kuwo.cn/m/bodian/collection.html?uid=000&playlistId=000&source=4&ownerId=
if (/\/bodian\//.test(id)) return this.getListDetailMusicListByBD(id, page)
@@ -393,7 +387,6 @@ export default {
return this.getListDetailDigest8(id, page, retryNum)
},
filterListDetail(rawData) {
// console.log(rawData)
return rawData.map(item => {
let infoArr = item.N_MINFO.split(';')
let types: SongType[] = []
@@ -464,7 +457,6 @@ export default {
return httpFetch(`http://search.kuwo.cn/r.s?all=${encodeURIComponent(text)}&pn=${page - 1}&rn=${limit}&rformat=json&encoding=utf8&ver=mbox&vipver=MUSIC_8.7.7.0_BCS37&plat=pc&devid=28156413&ft=playlist&pay=0&needliveshow=0`)
.promise.then(({ body }) => {
body = objStr2JSON(body)
// console.log(body)
return {
list: body.abslist.map(item => {
return {
-67
View File
@@ -1,33 +1,7 @@
// import { httpGet, httpFetch } from '../request.ts'
import { handleKwDecodeLyric } from './lyricDecode.ts'
import { createCipheriv, createDecipheriv } from 'crypto'
import { toMD5 } from '../utils.ts'
// const kw_token = {
// token: null,
// isGetingToken: false,
// }
// const translationMap = {
// "{'": '{"',
// "'}\n": '"}',
// "'}": '"}',
// "':'": '":"',
// "','": '","',
// "':{'": '":{"',
// "':['": '":["',
// "'}],'": '"}],"',
// "':[{'": '":[{"',
// "'},'": '"},"',
// "'},{'": '"},{"',
// "':[],'": '":[],"',
// "':{},'": '":{},"',
// "'}]}": '"}]}',
// }
// export const objStr2JSON = str => {
// return JSON.parse(str.replace(/(^{'|'}\n$|'}$|':'|','|':\[{'|'}\],'|':{'|'},'|'},{'|':\['|':\[\],'|':{},'|'}]})/g, s => translationMap[s]))
// }
export const objStr2JSON = (str: string): any => {
return JSON.parse(str.replace(/('(?=(,\s*')))|('(?=:))|((?<=([:,]\s*))')|((?<={)')|('(?=}))/g, '"'))
@@ -44,46 +18,9 @@ export const matchToken = (headers: Record<string, any>): string | null => {
}
}
// const wait = time => new Promise(resolve => setTimeout(() => resolve(), time))
// export const getToken = (retryNum = 0) => new Promise((resolve, reject) => {
// if (retryNum > 2) return Promise.reject(new Error('try max num'))
// if (kw_token.isGetingToken) return wait(1000).then(() => getToken(retryNum).then(token => resolve(token)))
// if (kw_token.token) return resolve(kw_token.token)
// kw_token.isGetingToken = true
// httpGet('http://www.kuwo.cn/', (err, resp) => {
// kw_token.isGetingToken = false
// if (err) return getToken(++retryNum)
// if (resp.statusCode != 200) return reject(new Error('获取失败'))
// const token = kw_token.token = matchToken(resp.headers)
// resolve(token)
// })
// })
export const decodeLyric = handleKwDecodeLyric
// export const tokenRequest = async(url, options = {}) => {
// let token = kw_token.token
// if (!token) token = await getToken()
// if (!options.headers) {
// options.headers = {
// Referer: 'http://www.kuwo.cn/',
// csrf: token,
// cookie: 'kw_token=' + token,
// }
// }
// const requestObj = httpFetch(url, options)
// requestObj.promise = requestObj.promise.then(resp => {
// // console.log(resp)
// if (resp.statusCode == 200) {
// kw_token.token = matchToken(resp.headers)
// }
// return resp
// })
// return requestObj
// }
interface LrcWordInfo {
startTime: number
@@ -117,7 +54,6 @@ export const lrcTools = {
}
prevWord.newTimeStr = `<${prevWord.startTime},${prevWord.endTime - prevWord.startTime}>`
// console.log(prevWord)
}
}
return {
@@ -137,7 +73,6 @@ export const lrcTools = {
}
const wordTimes = words.match(this.rxps.wordTimeAll)
if (!wordTimes) return
// console.log(wordTimes)
let preTimeInfo
for (const timeStr of wordTimes) {
const result = this.rxps.wordTime.exec(timeStr)
@@ -167,7 +102,6 @@ export const lrcTools = {
}
},
parse(lrc: string): string {
// console.log(lrc)
const lines = lrc.split(/\r\n|\r|\n/)
const tools = Object.create(this)
tools.isOK = true
@@ -183,7 +117,6 @@ export const lrcTools = {
if (!tools.lines.length) return ''
let lrcs = tools.lines.join('\n')
if (tools.tags.length) lrcs = `${tools.tags.join('\n')}\n${lrcs}`
// console.log(lrcs)
return lrcs
},
}
+1 -1
View File
@@ -5,7 +5,7 @@
// 端点:/yyting/search/searchBatch.action(q=2150)
// 注意:章节/取流 App 端点(q=1930/2224)游客 token 已失效(482 请登录),取流走 web 通道(见 apis/lr.ts)
import crypto from 'node:crypto'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
const DOMAIN = 'http://dapis.mting.info'
const SECRET = 'iJ0DgxmdC83#I&j@iwg'
+1 -1
View File
@@ -2,7 +2,7 @@
// 参考社区下载器(yoval/lrts 等):POST form {account, pwd: base64(password)},
// 成功响应 json.phone 非空,Set-Cookie 种下会话;取流接口必须携带该会话
// (免费章节匿名也返回"收费章节未购买",2026-10-02 实测)。
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { getCredentials, setCredentials } from '../auth/store.ts'
const UA_MI = 'Mozilla/5.0 (Linux; U; Android 4.1.1; zh-cn; MI2 Build/JRO03L) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 XiaoMi/MiuiBrowser/1.0'
+1 -3
View File
@@ -5,15 +5,13 @@
import lrApi from '../apis/lr.ts'
import musicSearch from './musicSearch.ts'
import { getBookMenu } from './menu.ts'
import { getLrCookie } from './auth.ts'
const lr = {
musicSearch,
getMusicUrl(songInfo, type) {
return lrApi.getMusicUrl(songInfo, type)
},
getPic(songInfo) {
const bookId = String(songInfo.songmid ?? '').split(':')[0]
getPic() {
// 书籍封面无独立免签接口;搜索结果的 img 字段已带封面(这里返回空由调用方回退)
return Promise.resolve(null)
},
+1 -1
View File
@@ -2,7 +2,7 @@
// 接口: https://m.lrts.me/ajax/getBookMenu?bookId=&pageNum=&pageSize=&sortType=0
// 实测(2026-10-02)匿名可用;章节条目 {section, name, listenPrice, state}
// 注意:专辑类内容用 getAlbumAudios(entityType=2),本实现仅收书(entityType=3)
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
const UA_MI = 'Mozilla/5.0 (Linux; U; Android 4.1.1; zh-cn; MI2 Build/JRO03L) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 XiaoMi/MiuiBrowser/1.0'
const MENU_URL = 'https://m.lrts.me/ajax/getBookMenu'
+1 -1
View File
@@ -2,7 +2,7 @@
// 接口: https://www.missevan.com/dramaapi/getdramaepisodes?drama_id=&season=&page=&order=0&pageSize=
// 结果在 info.episodes.episode[](name/sound_id/pay_type/vip);剧名在 info.drama.name
// 注意:getdrama(不带 type=2 时)的 info.episodes.episode 同样可用(MusicFree 插件用法),本接口支持翻页故用之
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { formatPlayTime } from '../index.ts'
const EPISODES_URL = 'https://www.missevan.com/dramaapi/getdramaepisodes'
+1 -1
View File
@@ -3,7 +3,7 @@
// ——网页版搜索排前面的正剧来自这里;getsearch 的 type 参数只有 1UP主/2音单/3声音/4声优,没有剧集类型
// 声音(补足): https://www.missevan.com/sound/getsearch?s=&p=&type=3&page_size=&sort=2 → info.Datas[]
// 剧条目 songmid 用 "d:<drama_id>" 前缀(取流时自动取首个免费集,见 apis/me.ts);声音条目为裸 sound_id
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { formatPlayTime } from '../index.ts'
const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36'
+1 -1
View File
@@ -7,7 +7,7 @@
// 该通道返回的是免签名 CDN 明文直链(sound-ali*-cdn-cn.maoercdn.com,m4a 原文件/-128k 变体);
// 网页版 hls.m3u8 为 Widevine 加密,与此无关。
import crypto from 'node:crypto'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { getCredentials } from '../auth/store.ts'
const APP_SECRET = 'f8Yw1M3u0e5MV7z34PcbY9wgP56YwJ'
+2 -2
View File
@@ -1,7 +1,7 @@
// 咪咕歌词 —— resourceinfo.do 取 lrcUrl/lyricUrl 再下载文本
// 上游对应:music-lib/migu/lyric.go
import { buildUrl, fetchSongDetail, miguGet, UserAgent } from './util.ts'
import { httpFetch } from '../request.ts'
import { buildUrl, fetchSongDetail, miguGet } from './util.ts'
import { httpFetch } from '../common/request.ts'
const LRC_REFERER = 'https://y.migu.cn/'
const LRC_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36'
+1 -2
View File
@@ -2,7 +2,7 @@
// 上游对应:music-lib/migu/migu.go(convertItemToSong*、collectMiguFormats、pickMiguImage 等)
// 说明:匿名态 listen-url 仅放行 PQ(128k mp3);HQ/SQ/ZQ 各档需携带登录 Cookie(对话框「登录后免费试听」)。
import crypto from 'node:crypto'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { getCredentials } from '../auth/store.ts'
import type { SongType } from '../types.ts'
@@ -328,7 +328,6 @@ export const convertItemToSong = (item: MiguSongItem, allowPaid = true): MiguSon
const best = candidates[0]!
const bestFormat = rateFormats[best.index]!
const bitrate = duration > 0 && best.size > 0 ? Math.floor(best.size * 8 / 1000 / duration) : 0
const coverURL = normalizeMiguImageURL(firstNonEmpty(
pickMiguImage(item.imgItems), pickMiguImage(item.albumImgs), item.img1, item.img2, item.img3,
))
+1 -1
View File
@@ -1,7 +1,7 @@
// 千千音乐歌词
// 链路:/v1/song/info → data[0].lyric 为歌词文本 URL(static-qianqian.taihe.com/*.txt)→ 直接下载文本。
// 返回 { lyric, tlyric: '' }(千千只有原文歌词,无翻译)。
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { getSongInfo } from './musicInfo.ts'
import { requestHeaders } from './util.ts'
+1 -1
View File
@@ -4,7 +4,7 @@
// 该平台匿名即可搜索 / 查专辑 / 查歌单 / 拿免费曲目直链;cookie 仅影响 VIP、收藏等登录态资源,
// MusicServer 从 sdk/auth/store.ts 的 getCredentials('bd') 读取(可选,未登录留空)。
import crypto from 'node:crypto'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { getCredentials } from '../auth/store.ts'
export const APP_ID = '16073360'
+1 -1
View File
@@ -4,7 +4,7 @@
// 逐字歌词(lyric.content)与试听播放信息(track_player.url_player_info / video_model)
// 注意:seo_track 属分享页/SEO 数据面,track_player 里只有 30 秒试听资源;
// 完整音频需真机凭证走 /luna/pc/track_v2,且音频经 CENC 加密,见 apis/qs.ts 头注释
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import type { RequestObj } from '../types.ts'
export default (trackId: string): RequestObj<any> => {
+1 -1
View File
@@ -1,6 +1,6 @@
// 蜻蜓FM SSR 请求助手 —— 域名迁移(www.qingting.fm → www.qtfm.cn)伴随华为 WAF 会话,
// 302 时需带上 Set-Cookie 的 HWWAFSESID/HWWAFSESTIME 再请求 Location,否则可能被 WAF 拦
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36'
-5
View File
@@ -1,5 +0,0 @@
// SDK 根请求模块 —— 唯一实现在 common/request.ts,此处仅转发
// sdk 内 `import { ... } from '../request.ts'` 的解析目标
export { httpFetch } from './common/request.ts'
export type { HttpOptions, HttpRequestObject, HttpFetchResponse } from './common/request.ts'
+1 -1
View File
@@ -3,7 +3,7 @@
// 曲目:music.musichallAlbum.AlbumSongList GetAlbumSongList 按 100 一批翻页(上游 fetchAlbumDetail 的曲目段)
// 详情:GetAlbumInfo 详情接口已停用:专辑名取自曲目自带的 album 信息,作者由曲目歌手去重合成,封面走平台固定 URL 规则
// 链接解析:albumDetail/<mid>、album/<mid>、albummid=<mid>(上游 ParseAlbum)
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { filterMusicInfoItem } from './singer.ts'
import { createMusicuFetch } from './utils/index.ts'
+1 -3
View File
@@ -1,5 +1,5 @@
import type { HttpRequestObject } from '../types.ts'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { dateFormat2 } from '../index.ts'
import getMusicInfo from './musicInfo.ts'
@@ -114,7 +114,6 @@ export default {
})
const { body, statusCode } = await _requestObj.promise
if (statusCode != 200 || body.code !== 0) throw new Error('获取评论失败')
// console.log(body, statusCode)
const comment = body.comment
return {
source: 'tx',
@@ -183,7 +182,6 @@ export default {
},
})
const { body, statusCode } = await _requestObj2.promise
// console.log('body', body)
if (statusCode != 200 || body.code !== 0 || body.req.code !== 0) throw new Error('获取热门评论失败')
const comment = body.req.data.CommentList
return {
+1 -3
View File
@@ -1,5 +1,5 @@
import type { HttpRequestObject } from '../types.ts'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
export default {
_requestObj: null as HttpRequestObject | null,
@@ -44,9 +44,7 @@ export default {
},
})
const { body, statusCode } = await _requestObj.promise
// console.log(body)
if (statusCode != 200 || body.code !== 0) throw new Error('获取热搜词失败')
// console.log(body)
return { source: 'tx', list: this.filterList(body.hotkey.data.vec_hotkey) }
},
filterList(rawList) {
+3 -23
View File
@@ -1,5 +1,5 @@
import type { HttpRequestObject, SongInfo, SongType, Quality, QualityInfo } from '../types.ts'
import { httpFetch } from '../request.ts'
import type { HttpRequestObject, SongType, Quality, QualityInfo } from '../types.ts'
import { httpFetch } from '../common/request.ts'
import { formatPlayTime, sizeFormate } from '../index.ts'
import { formatSingerName } from '../utils.ts'
@@ -65,7 +65,6 @@ export default {
},
],
listDetailRequest(id, period, limit) {
// console.log(id, period, limit)
return httpFetch('https://u.y.qq.com/cgi-bin/musicu.fcg', {
method: 'post',
headers: {
@@ -107,7 +106,6 @@ export default {
return requestDataObj.promise
},
filterData(rawList) {
// console.log(rawList)
return rawList.map(item => {
let types: SongType[] = []
let _types: Partial<Record<Quality, QualityInfo>> = {}
@@ -180,7 +178,6 @@ export default {
})
},
filterBoardsData(rawList) {
// console.log(rawList)
let list: any[] = []
for (const board of rawList) {
// 排除 MV榜
@@ -198,24 +195,7 @@ export default {
}
return list
},
async getBoards(retryNum = 0) {
// if (++retryNum > 3) return Promise.reject(new Error('try max num'))
// let response
// try {
// response = await this.getBoardsData()
// } catch (error) {
// return this.getBoards(retryNum)
// }
// // console.log(response.body)
// if (response.statusCode !== 200 || response.body.code !== 0) return this.getBoards(retryNum)
// const list = this.filterBoardsData(response.body.data.topList)
// console.log(list)
// console.log(JSON.stringify(list))
// this.list = list
// return {
// list,
// source: 'tx',
// }
async getBoards() {
this.list = boardList
return {
list: boardList,
+1 -35
View File
@@ -1,5 +1,4 @@
import type { HttpRequestObject } from '../types.ts'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import getMusicInfo from './musicInfo.ts'
import { decodeQrc } from './qrcDecode.ts'
@@ -111,8 +110,6 @@ const parseTools = {
return parseInt(m) * 3600000 + parseInt(s) * 1000 + parseInt(ms)
},
fixRlrcTimeTag(rlrc, lrc) {
// console.log(lrc)
// console.log(rlrc)
const rlrcLines = rlrc.split('\n')
let lrcLines = lrc.split('\n')
// let temp: string[] = []
@@ -141,8 +138,6 @@ const parseTools = {
return newLrc.join('\n')
},
fixTlrcTimeTag(tlrc, lrc) {
// console.log(lrc)
// console.log(tlrc)
const tlrcLines = tlrc.split('\n')
let lrcLines = lrc.split('\n')
// let temp: string[] = []
@@ -185,14 +180,9 @@ const parseTools = {
let { lyric, yrc } = this.parseLyric(this.removeTag(lrc))
info.lyric = lyric
info.yrc = yrc
// console.log(lyric)
// console.log(yrc)
}
if (rlrc) info.rlyric = this.fixRlrcTimeTag(this.parseRlyric(this.removeTag(rlrc)), info.lyric)
if (tlrc) info.tlyric = this.fixTlrcTimeTag(tlrc, info.lyric)
// console.log(info.lyric)
// console.log(info.tlyric)
// console.log(info.rlyric)
return info
},
@@ -218,9 +208,6 @@ export default {
// return {
// }
// console.log(lyric)
// console.log(tlyric)
// console.log(rlyric)
return parseTools.parse(lyric, tlyric, rlyric)
},
getLyric(mInfo, retryNum = 0) {
@@ -266,7 +253,6 @@ export default {
},
})
return requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.code != this.successCode || body.req.code != this.successCode) return this.getLyric(songId, ++retryNum)
const data = body.req.data
return this.parseLyric(data.lyric, data.trans, data.roma)
@@ -276,23 +262,3 @@ export default {
},
}
// export default {
// regexps: {
// matchLrc: /.+"lyric":"([\w=+/]*)".+/,
// },
// getLyric(songmid) {
// const requestObj = httpFetch(`https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?songmid=${songmid}&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&platform=yqq`, {
// headers: {
// Referer: 'https://y.qq.com/portal/player.html',
// },
// })
// requestObj.promise = requestObj.promise.then(({ body }) => {
// if (body.code != 0 || !body.lyric) return Promise.reject(new Error('Get lyric failed'))
// return {
// lyric: decodeName(b64DecodeUnicode(body.lyric)),
// tlyric: decodeName(b64DecodeUnicode(body.trans)),
// }
// })
// return requestObj
// },
// }
+2 -3
View File
@@ -1,5 +1,5 @@
import type { HttpRequestObject, SongType, Quality, QualityInfo } from '../types.ts'
import { httpFetch } from '../request.ts'
import type { SongType, Quality, QualityInfo } from '../types.ts'
import { httpFetch } from '../common/request.ts'
import { formatPlayTime, sizeFormate } from '../index.ts'
const getSinger = (singers) => {
@@ -33,7 +33,6 @@ export default (songmid) => {
},
})
return requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.code != 0 || body.req.code != 0) return Promise.reject(new Error('获取歌曲信息失败'))
const item = body.req.data.track_info
if (!item.file?.media_mid) return null
-14
View File
@@ -61,26 +61,13 @@ export default {
},
})
return searchRequest.then(({ body }) => {
// console.log(body)
if (!body || !body.req || body.code != this.successCode || body.req.code != this.successCode) {
return this.musicSearch(str, page, limit, ++retryNum)
}
return body.req.data
})
},
// randomInt(min, max) {
// return Math.floor(Math.random() * (max - min + 1)) + min
// },
// getSearchId() {
// const e = BigInt(this.randomInt(1, 20))
// const t = e * 18014398509481984n
// const n = BigInt(this.randomInt(0, 4194304)) * 4294967296n
// const a = BigInt(Date.now())
// const r = (a * 1000n) % (24n * 60n * 60n * 1000n)
// return String(t + n + r)
// },
handleResult(rawList) {
// console.log(rawList)
if (!rawList || !Array.isArray(rawList)) return []
const list: SongInfo[] = []
rawList.forEach(item => {
@@ -144,7 +131,6 @@ export default {
typeUrl: {},
})
})
// console.log(list)
return list
},
search(str, page = 1, limit) {
+1 -1
View File
@@ -5,7 +5,7 @@
// https://i.y.qq.com/v8/playsong.html?songid=<数字 id>(先反查 songmid)
// ...?songmid=<songmid>
// 返回 Song(含 songmid);直链 url/type 为尽力附带,失败不影响元数据返回(与上游一致)。
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { formatPlayTime, sizeFormate } from '../index.ts'
import { formatSingerName } from '../utils.ts'
import txApi from '../apis/tx.ts'
+1 -1
View File
@@ -2,7 +2,7 @@
// 搜索/分类复用现有 sdk/tx/songList.ts(handleParseId / getListId / getListDetail / getTag / getList)
// 推荐:musicu.fcg playlist.HotRecommendServer/get_hot_recommend(上游 GetRecommendedPlaylists)
// 链接解析:extractPlaylistId 为上游 extractQQPlaylistID 的 1:1 移植(含单测用例断言)
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { formatPlayCount } from '../index.ts'
import songList from './songList.ts'
+1 -7
View File
@@ -1,5 +1,5 @@
import type { HttpRequestObject, SongType, Quality, QualityInfo } from '../types.ts'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { decodeName, formatPlayTime, sizeFormate, dateFormat, formatPlayCount } from '../index.ts'
import { formatSingerName } from '../utils.ts'
@@ -120,7 +120,6 @@ export default {
this._requestObj_list = httpFetch(
this.getListUrl(sortId, tagId, page),
)
// console.log(this.getListUrl(sortId, tagId, page))
return this._requestObj_list!.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum)
return tagId ? this.filterList2(body.playlist.data, page) : this.filterList(body.playlist.data, page)
@@ -148,7 +147,6 @@ export default {
}
},
filterList2({ content }, page) {
// console.log(content.v_item)
return {
list: content.v_item.map(({ basic }) => ({
play_count: formatPlayCount(basic.play_cnt),
@@ -173,7 +171,6 @@ export default {
const requestObj_listDetailLink = httpFetch(link)
const { headers: { location }, statusCode } = await requestObj_listDetailLink.promise
// console.log(headers)
if (statusCode > 400) return this.handleParseId(link, ++retryNum)
return location == null ? link : location
},
@@ -189,7 +186,6 @@ export default {
if (!result) throw new Error('failed')
}
id = result[1]
// console.log(id)
}
return id
},
@@ -225,7 +221,6 @@ export default {
}
},
filterListDetail(rawList) {
// console.log(rawList)
return rawList.map(item => {
let types: SongType[] = []
let _types: Partial<Record<Quality, QualityInfo>> = {}
@@ -300,7 +295,6 @@ export default {
})
.promise.then(({ body }) => {
if (body.code != 0) return this.search(text, page, limit, ++retryNum)
// console.log(body.data.list)
return {
list: body.data.list.map(item => {
return {
+1 -1
View File
@@ -6,7 +6,7 @@
// 2) fcg_get_profile_order_asset reqtype=3 → 收藏歌单
// 3) fcg_get_profile_order_asset reqtype=1 → 「我喜欢的歌曲」虚拟歌单摘要(有歌才附上)
// 说明:虚拟条目的 id 为 profile:favorites(与上游一致),歌曲需凭 cookie 走同一接口拉取
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { formatPlayCount } from '../index.ts'
import { getCredentials } from '../auth/store.ts'
import type { TxCredentials } from '../auth/store.ts'
+2 -6
View File
@@ -1,12 +1,8 @@
import type { HttpRequestObject } from '../../types.ts'
import { httpFetch } from '../../request.ts'
import { httpFetch } from '../../common/request.ts'
import { zzcSign } from './crypto.ts'
export const signRequest = async(data) => {
// console.log(data)
const sign = await zzcSign(JSON.stringify(data))
// console.log('sign', sign)
return httpFetch(`https://u.y.qq.com/cgi-bin/musics.fcg?sign=${sign}`, {
method: 'post',
headers: {
@@ -36,7 +32,7 @@ export const createMusicuFetch = async(data, retryNum = 0) => {
},
}).promise
} catch (err) {
console.log(err)
console.warn(`[tx] musicu 请求失败将重试(${retryNum + 1}/3): ${(err as Error).message}`)
return createMusicuFetch(data, ++retryNum)
}
if (result.statusCode !== 200 || result.body.code != 0) return createMusicuFetch(data, ++retryNum)
+1 -1
View File
@@ -20,7 +20,7 @@ export const getHostIp = (hostname: string): CachedIp | undefined => {
// family: 4,
all: false,
}, (err, address, family) => {
if (err) return console.log(err)
if (err) return console.warn(`[utils] DNS 预解析 ${hostname} 失败: ${err.message}`)
ipMap.set(hostname, { address, family })
})
}
+1 -1
View File
@@ -2,7 +2,7 @@
// 搜索:cloudsearch pc type=10(eapi 通道,与现有 songList.search 同一通路)
// 详情/曲目:weapi/v1/album/<id>(匿名可用;登录 cookie 可选)
// 链接解析:#/album?id= / /album?id= / /album/<id>(上游 parseNeteaseLink)
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { weapi } from './utils/crypto.ts'
import { formatPlayTime, sizeFormate } from '../index.ts'
import { formatSingerName } from '../utils.ts'
+1 -2
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { weapi } from './utils/crypto.ts'
import { dateFormat2 } from '../index.ts'
import type { HttpRequestObject } from '../types.ts'
@@ -149,7 +149,6 @@ export default {
}),
})
const { body, statusCode } = await _requestObj.promise
// console.log(body)
if (statusCode != 200 || body.code !== 200) throw new Error('获取评论失败')
cursorTools.setCursor(songmid, body.data.cursor, cursorInfo.orderType, cursorInfo.offset, page)
return { source: 'wy', comments: this.filterComment(body.data.comments), total: body.data.totalCount, page, limit, maxPage: Math.ceil(body.data.totalCount / limit) || 1 }
+3 -24
View File
@@ -1,5 +1,5 @@
import { weapi } from './utils/crypto.ts'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import musicDetailApi from './musicDetail.ts'
import type { HttpRequestObject } from '../types.ts'
@@ -129,7 +129,6 @@ export default {
},
filterBoardsData(rawList) {
// console.log(rawList)
let list: any[] = []
for (const board of rawList) {
// 排除 MV榜
@@ -142,24 +141,7 @@ export default {
}
return list
},
async getBoards(retryNum = 0) {
// if (++retryNum > 3) return Promise.reject(new Error('try max num'))
// let response
// try {
// response = await this.getBoardsData()
// } catch (error) {
// return this.getBoards(retryNum)
// }
// console.log(response.body)
// if (response.statusCode !== 200 || response.body.code !== 200) return this.getBoards(retryNum)
// const list = this.filterBoardsData(response.body.list)
// console.log(list)
// console.log(JSON.stringify(list))
// this.list = list
// return {
// list,
// source: 'wy',
// }
async getBoards() {
this.list = topList
return {
list: topList,
@@ -168,7 +150,6 @@ export default {
},
async getList(bangid, page, retryNum = 0) {
if (++retryNum > 6) return Promise.reject(new Error('try max num'))
// console.log(bangid)
let resp
try {
resp = await this.getData(bangid)
@@ -180,19 +161,17 @@ export default {
}
}
if (resp.statusCode !== 200 || resp.body.code !== 200) return this.getList(bangid, page, retryNum)
// console.log(resp.body)
let musicDetail
try {
musicDetail = await (musicDetailApi as any).getList(resp.body.playlist.trackIds.map(trackId => trackId.id))
} catch (err: any) {
console.log(err)
console.warn(`[wy] 榜单歌曲详情获取失败将重试(${retryNum + 1}): ${err.message}`)
if (err.message == 'try max num') {
throw err
} else {
return this.getList(bangid, page, retryNum)
}
}
// console.log(musicDetail)
return {
total: musicDetail.list.length,
list: musicDetail.list,
+2 -75
View File
@@ -1,39 +1,6 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { eapi } from './utils/crypto.ts'
import type { LyricInfo, HttpRequestObject } from '../types.ts'
// import { decodeName } from '../...ts'
// const parseLyric = (str, lrc) => {
// if (!str) return ''
// str = str.replace(/\r/g, '')
// let yrc = str.replace(/\[((\d+),\d+)\].*/g, str => {
// let result = str.match(/\[((\d+),\d+)\].*/)
// let time = parseInt(result[2])
// let ms = time % 1000
// time /= 1000
// let m = parseInt(time / 60).toString().padStart(2, '0')
// time %= 60
// let s = parseInt(time).toString().padStart(2, '0')
// time = `${m}:${s}.${ms}`
// str = str.replace(result[1], time)
// let startTime = 0
// str = str.replace(/\(0,1\) /g, ' ').replace(/\(\d+,\d+\)/g, time => {
// const [start, end] = time.replace(/^\((\d+,\d+)\)$/, '$1').split(',')
// time = `<${parseInt(startTime + parseInt(start))},${end}>`
// startTime = parseInt(startTime + parseInt(end))
// return time
// })
// return str
// })
// yrc = decodeName(yrc)
// return yrc.trim()
// }
import type { HttpRequestObject } from '../types.ts'
const eapiRequest = (url: string, data: object) => {
return httpFetch('https://interface3.music.163.com/eapi/song/lyric/v1', {
@@ -41,18 +8,9 @@ const eapiRequest = (url: string, data: object) => {
headers: {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
origin: 'https://music.163.com',
// cookie: 'os=pc; deviceId=A9C064BB4584D038B1565B58CB05F95290998EE8B025AA2D07AE; osver=Microsoft-Windows-10-Home-China-build-19043-64bit; appver=2.5.2.197409; channel=netease; MUSIC_A=37a11f2eb9de9930cad479b2ad495b0e4c982367fb6f909d9a3f18f876c6b49faddb3081250c4980dd7e19d4bd9bf384e004602712cf2b2b8efaafaab164268a00b47359f85f22705cc95cb6180f3aee40f5be1ebf3148d888aa2d90636647d0c3061cd18d77b7a0; __csrf=05b50d54082694f945d7de75c210ef94; mode=Z7M-KP5(7)GZ; NMTID=00OZLp2VVgq9QdwokUgq3XNfOddQyIAAAF_6i8eJg; ntes_kaola_ad=1',
},
form: eapi(url, data),
})
// requestObj.promise = requestObj.promise.then(({ body }) => {
// // console.log(raw)
// console.log(body)
// // console.log(eapiDecrypt(raw))
// // return eapiDecrypt(raw)
// return body
// })
// return requestObj
}
const parseTools = {
@@ -218,35 +176,6 @@ const parseTools = {
}
// https://github.com/Binaryify/NeteaseCloudMusicApi/pull/1523/files
// export default songmid => {
// const requestObj = httpFetch('https://music.163.com/api/linux/forward', {
// method: 'post',
// 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
// form: linuxapi({
// method: 'POST',
// url: 'https://music.163.com/api/song/lyric?_nmclfl=1',
// params: {
// id: songmid,
// tv: -1,
// lv: -1,
// rv: -1,
// kv: -1,
// },
// }),
// })
// requestObj.promise = requestObj.promise.then(({ body }) => {
// if (body.code !== 200 || !body?.lrc?.lyric) return Promise.reject(new Error('Get lyric failed'))
// // console.log(body)
// return {
// lyric: body.lrc.lyric,
// tlyric: body.tlyric?.lyric ?? '',
// rlyric: body.romalrc?.lyric ?? '',
// // yrc: parseLyric(body.klyric.lyric),
// }
// })
// return requestObj
// }
// 参考社区通用的歌词时间标签修正思路
const fixTimeLabel = (lrc: string, tlrc?: string, romalrc?: string) => {
@@ -277,11 +206,9 @@ export default (songmid: string): HttpRequestObject => {
yrv: 0,
})
requestObj.promise = requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.code !== 200 || !body?.lrc?.lyric) return Promise.reject(new Error('Get lyric failed'))
const fixTimeLabelLrc = fixTimeLabel(body.lrc.lyric, body.tlyric?.lyric, body.romalrc?.lyric)
const info = parseTools.parse(body.yrc?.lyric, body.ytlrc?.lyric, body.yromalrc?.lyric, fixTimeLabelLrc.lrc, fixTimeLabelLrc.tlrc, fixTimeLabelLrc.romalrc)
// console.log(info)
if (!info.lyric) return Promise.reject(new Error('Get lyric failed'))
return info
}) as any
+1 -4
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { weapi } from './utils/crypto.ts'
import { formatPlayTime, sizeFormate } from '../index.ts'
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/song_detail.js
@@ -12,7 +12,6 @@ export default {
return arr.join('、')
},
filterList({ songs, privileges }) {
// console.log(songs, privileges)
const list: any[] = []
songs.forEach((item, index) => {
const types: Array<{ type: string, size: string | null }> = []
@@ -87,7 +86,6 @@ export default {
})
}
})
// console.log(list)
return list
},
async getList(ids = [], retryNum = 0) {
@@ -106,7 +104,6 @@ export default {
})
const { body, statusCode } = await requestObj.promise
if (statusCode != 200 || body.code !== 200) throw new Error('获取歌曲详情失败')
// console.log(body)
return { source: 'wy', list: this.filterList(body) }
},
}
+1 -2
View File
@@ -1,5 +1,5 @@
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/song_detail.js
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { weapi } from './utils/crypto.ts'
import type { RequestObj } from '../types.ts'
@@ -17,7 +17,6 @@ export default (songmid: string): RequestObj<any> => {
}),
})
requestObj.promise = requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.code !== 200 || !body.songs.length) return Promise.reject(new Error('获取歌曲信息失败'))
return body.songs[0]
})
+1 -4
View File
@@ -1,4 +1,4 @@
// import { httpFetch } from '../request.ts'
// import { httpFetch } from '../common/request.ts'
// import { weapi } from './utils/crypto.ts'
import { sizeFormate, formatPlayTime } from '../index.ts'
// import musicDetailApi from './musicDetail.ts'
@@ -36,7 +36,6 @@ export default {
return arr.join('、')
},
handleResult(rawList) {
// console.log(rawList)
if (!rawList) return []
return rawList.map(item => {
item = item.baseInfo.simpleSongData
@@ -95,10 +94,8 @@ export default {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
if (limit == null) limit = this.limit
return this.musicSearch(str, page, limit).then(result => {
// console.log(result)
if (!result || result.code !== 200) return this.search(str, page, limit, retryNum)
let list = this.handleResult(result.data.resources || [])
// console.log(list)
if (list == null) return this.search(str, page, limit, retryNum)
+1 -1
View File
@@ -6,7 +6,7 @@
// 裸数字 id 也接受(补充便利;上游仅接受链接)
// 直链 url/type 为尽力附带,失败不影响元数据返回(与上游一致);
// 歌单/专辑链接抛清晰错误,提示使用对应 parsePlaylist/parseAlbum。
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { weapi } from './utils/crypto.ts'
import { getWyCookie } from '../auth/store.ts'
import wyApi from '../apis/wy.ts'
+1 -1
View File
@@ -3,7 +3,7 @@
// 推荐:weapi/personalized/playlist(上游 GetRecommendedPlaylists,匿名可用)
// 详情/曲目:复用 songList.getListDetail(linuxapi forward),按 page/limit 切片
// 链接解析:#/playlist?id= / /playlist?id= / /playlist/<id>/<n>/... / 裸数字 id(上游 parseNeteaseLink)
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { weapi } from './utils/crypto.ts'
import { formatPlayCount } from '../index.ts'
import { getWyCookie } from '../auth/store.ts'
+2 -12
View File
@@ -4,7 +4,7 @@
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/playlist_detail.js
import { weapi, linuxapi } from './utils/crypto.ts'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { formatPlayTime, sizeFormate, dateFormat, formatPlayCount } from '../index.ts'
import musicDetailApi from './musicDetail.ts'
import { eapiRequest } from './utils/index.ts'
@@ -39,7 +39,6 @@ export default {
const requestObj_listDetailLink = httpFetch(link)
const { headers: { location }, statusCode } = await requestObj_listDetailLink.promise
// console.log(headers)
if (statusCode > 400) return this.handleParseId(link, ++retryNum)
const url = location == null ? link : location
return this.regExps.listDetailLink.test(url)
@@ -62,7 +61,6 @@ export default {
} else {
id = await this.handleParseId(id)
}
// console.log(id)
}
return { id, cookie }
},
@@ -92,7 +90,6 @@ export default {
if (statusCode !== 200 || body.code !== this.successCode) return this.getListDetail(id, page, ++tryNum)
let limit = 1000
let rangeStart = (page - 1) * limit
// console.log(body)
let list
if (body.playlist.trackIds.length == body.privileges.length) {
list = this.filterListDetail(body)
@@ -100,7 +97,7 @@ export default {
try {
list = (await (musicDetailApi as any).getList(body.playlist.trackIds.slice(rangeStart, limit * page).map(trackId => trackId.id))).list
} catch (err: any) {
console.log(err)
console.warn(`[wy] 歌单歌曲详情获取失败将重试(${tryNum + 1}): ${err.message}`)
if (err.message == 'try max num') {
throw err
} else {
@@ -108,7 +105,6 @@ export default {
}
}
}
// console.log(list)
return {
list,
page,
@@ -125,7 +121,6 @@ export default {
}
},
filterListDetail({ playlist: { tracks }, privileges }) {
// console.log(tracks, privileges)
const list: any[] = []
tracks.forEach((item, index) => {
const types: Array<{ type: string, size: string | null }> = []
@@ -218,7 +213,6 @@ export default {
}),
})
return this._requestObj_list!.promise.then(({ body }) => {
// console.log(body)
if (body.code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum)
return {
list: this.filterList(body.playlists),
@@ -230,7 +224,6 @@ export default {
})
},
filterList(rawData) {
// console.log(rawData)
return rawData.map(item => ({
play_count: formatPlayCount(item.playCount),
id: String(item.id),
@@ -254,7 +247,6 @@ export default {
form: weapi({}),
})
return this._requestObj_tags!.promise.then(({ body }) => {
// console.log(JSON.stringify(body))
if (body.code !== this.successCode) return this.getTag(++tryNum)
return this.filterTagInfo(body)
})
@@ -292,7 +284,6 @@ export default {
form: weapi({}),
})
return this._requestObj_hotTags!.promise.then(({ body }) => {
// console.log(JSON.stringify(body))
if (body.code !== this.successCode) return this.getTag(++tryNum)
return this.filterHotTagInfo(body.tags)
})
@@ -324,7 +315,6 @@ export default {
})
.promise.then(({ body }) => {
if (body.code != this.successCode) throw new Error('filed')
// console.log(body)
return {
list: this.filterList(body.result.playlists),
limit,
+1 -1
View File
@@ -3,7 +3,7 @@
// 未登录抛出清晰错误,不返回空列表
// 1) weapi/nuser/account/get → 当前用户 userId
// 2) weapi/user/playlist → 用户歌单列表(分页 limit/offset)
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { weapi } from './utils/crypto.ts'
import { formatPlayCount } from '../index.ts'
import { getCredentials, getWyCookie } from '../auth/store.ts'
+1 -9
View File
@@ -1,4 +1,4 @@
import { httpFetch } from '../../request.ts'
import { httpFetch } from '../../common/request.ts'
import { eapi } from './crypto.ts'
import { getWyCookie } from '../../auth/store.ts'
import type { HttpRequestObject } from '../../types.ts'
@@ -15,12 +15,4 @@ export const eapiRequest = (url: string, data: object): HttpRequestObject => {
},
form: eapi(url, data),
})
// requestObj.promise = requestObj.promise.then(({ body }) => {
// // console.log(raw)
// console.log(body)
// // console.log(eapiDecrypt(raw))
// // return eapiDecrypt(raw)
// return body
// })
// return requestObj
}
+1 -1
View File
@@ -2,7 +2,7 @@
// 接口: https://www.ximalaya.com/revision/album/getTracksList?albumId=&pageNum=&sort=0
// 实测(2026-10-02)匿名可翻全部页(固定每页 30 条);仅曲目元信息(trackId/title/isPaid),
// 播放地址需另走 baseInfo。注意 v1 版接口(/revision/album/v1/getTracksList)需要 webtk 签名且有风控。
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { XM_UA } from './sign.ts'
const TRACKS_URL = 'https://www.ximalaya.com/revision/album/getTracksList'
+1 -1
View File
@@ -7,7 +7,7 @@
// 响应 trackInfo 顶层挂载(playUrlList/title/cover*/duration/isPaid/isAuthorized/isXimiUhqTrack),
// playUrlList[].url 为 AES-ECB 加密地址(key 取自官方客户端 4.0.14 asar),见 decoder.ts
import crypto from 'node:crypto'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { makeXmSign, XM_UA } from './sign.ts'
import { getCredentials } from '../auth/store.ts'
+1 -1
View File
@@ -4,7 +4,7 @@
// risk invalid 风控,本端点 + condition=relation&device=web 不受影响)
// 返回 data.result.response.docs[],字段 id/title/duration(秒)/nickname/album_id/album_title/
// album_cover_path/is_paid/is_trailer
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { formatPlayTime } from '../index.ts'
import { XM_UA } from './sign.ts'
+1 -1
View File
@@ -7,7 +7,7 @@
import zlib from 'node:zlib'
import crypto from 'node:crypto'
import { httpFetch } from '../request.ts'
import { httpFetch } from '../common/request.ts'
import { DEVICE_INFO } from './deviceInfo.ts'
const SIGN_KEY = 'm9ZtRrz:qujT8@da'
+1 -1
View File
@@ -431,7 +431,7 @@ export const loginRoutes = async(fastify) => {
description: '用 B站 App 扫码后轮询 /login/bl/qr/check 完成登录。返回 key 需原样回传,qrimg 为 data URL 可直接展示。',
tags: ['登录'],
},
}, async(request, reply) => {
}, async(_request, reply) => {
try {
return await biliAuth.createQRLogin()
} catch (err) {