同步PC musicSdk代码

This commit is contained in:
lyswhut 2023-02-19 15:45:09 +08:00
parent a152028a48
commit fe7179d242
27 changed files with 820 additions and 117 deletions

View File

@ -11,7 +11,6 @@ module.exports = [
tx: ['128k'], tx: ['128k'],
wy: ['128k'], wy: ['128k'],
mg: ['128k'], mg: ['128k'],
// xm: ['128k'],
// bd: ['128k'], // bd: ['128k'],
}, },
}, },

View File

@ -1,6 +1,6 @@
import { httpFetch } from '../../request' import { httpFetch } from '../../request'
// import { formatPlayTime } from '../../index' // import { formatPlayTime } from '../../index'
// import jshtmlencode from 'js-htmlencode'
const boardList = [ const boardList = [
// { id: 'bd__601', name: '歌单榜', bangid: '601' }, // { id: 'bd__601', name: '歌单榜', bangid: '601' },

View File

@ -1,5 +1,5 @@
// import '../../polyfill/array.find' // import '../../polyfill/array.find'
// import jshtmlencode from 'js-htmlencode'
import { httpFetch } from '../../request' import { httpFetch } from '../../request'
import { formatPlayTime } from '../../index' import { formatPlayTime } from '../../index'
// import { debug } from '../../utils/env' // import { debug } from '../../utils/env'
@ -62,15 +62,15 @@ export default {
}) })
return list return list
}, },
search(str, page = 1, { limit } = {}, retryNum = 0) { search(str, page = 1, limit, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num')) if (++retryNum > 3) return Promise.reject(new Error('try max num'))
if (limit == null) limit = this.limit if (limit == null) limit = this.limit
return this.musicSearch(str, page, limit).then(result => { return this.musicSearch(str, page, limit).then(result => {
if (!result || result.error_code !== 22000) return this.search(str, page, { limit }, retryNum) if (!result || result.error_code !== 22000) return this.search(str, page, limit, retryNum)
let list = this.handleResult(result.result.song_info.song_list) let list = this.handleResult(result.result.song_info.song_list)
if (list == null) return this.search(str, page, { limit }, retryNum) if (list == null) return this.search(str, page, limit, retryNum)
this.total = result.result.song_info.total this.total = result.result.song_info.total
this.page = page this.page = page

View File

@ -1,5 +1,5 @@
import { httpFetch } from '../../request' import { httpFetch } from '../../request'
import { decodeName } from '../..' import { decodeName } from '../../index'
export default { export default {
_requestObj: null, _requestObj: null,

View File

@ -1,6 +1,6 @@
import { httpFetch } from '../../request' import { httpFetch } from '../../request'
import { decodeLyric } from './util' import { decodeLyric } from './util'
import { decodeName } from '../..' import { decodeName } from '../../index'
const headExp = /^.*\[id:\$\w+\]\n/ const headExp = /^.*\[id:\$\w+\]\n/

View File

@ -1,5 +1,5 @@
// import '../../polyfill/array.find' // import '../../polyfill/array.find'
// import jshtmlencode from 'js-htmlencode'
import { httpFetch } from '../../request' import { httpFetch } from '../../request'
import { decodeName, formatPlayTime, sizeFormate } from '../../index' import { decodeName, formatPlayTime, sizeFormate } from '../../index'
// import { debug } from '../../utils/env' // import { debug } from '../../utils/env'
@ -82,10 +82,10 @@ export default {
if (limit == null) limit = this.limit if (limit == null) limit = this.limit
// http://newlyric.kuwo.cn/newlyric.lrc?62355680 // http://newlyric.kuwo.cn/newlyric.lrc?62355680
return this.musicSearch(str, page, limit).then(result => { return this.musicSearch(str, page, limit).then(result => {
if (!result || result.errcode !== 0) return this.search(str, page, { limit }, retryNum) if (!result || result.errcode !== 0) return this.search(str, page, limit, retryNum)
let list = this.handleResult(result.data.info) let list = this.handleResult(result.data.info)
if (list == null) return this.search(str, page, { limit }, retryNum) if (list == null) return this.search(str, page, limit, retryNum)
this.total = result.data.total this.total = result.data.total
this.page = page this.page = page

View File

@ -38,7 +38,6 @@ export default {
}, },
) )
requestObj.promise = requestObj.promise.then(({ body }) => { requestObj.promise = requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.error_code !== 0) return Promise.reject('图片获取失败') if (body.error_code !== 0) return Promise.reject('图片获取失败')
let info = body.data[0].info let info = body.data[0].info
const img = info.imgsize ? info.image.replace('{size}', info.imgsize[0]) : info.image const img = info.imgsize ? info.image.replace('{size}', info.imgsize[0]) : info.image

View File

@ -44,8 +44,8 @@ export default {
} }
} }
if (formats.includes('HIRFLAC')) { if (formats.includes('HIRFLAC')) {
types.push({ type: 'flac32bit', size: null }) types.push({ type: 'flac24bit', size: null })
_types.flac32bit = { _types.flac24bit = {
size: null, size: null,
} }
} }
@ -84,6 +84,7 @@ export default {
body = objStr2JSON(body) body = objStr2JSON(body)
// console.log(body) // console.log(body)
if (!body.musiclist) return this.getAlbumListDetail(id, page, ++retryNum) if (!body.musiclist) return this.getAlbumListDetail(id, page, ++retryNum)
body.name = decodeName(body.name)
return { return {
list: this.filterListDetail(body.musiclist, body.name, body.albumid), list: this.filterListDetail(body.musiclist, body.name, body.albumid),
page, page,
@ -93,11 +94,38 @@ export default {
info: { info: {
name: body.name, name: body.name,
img: body.img || body.hts_img, img: body.img || body.hts_img,
desc: body.info, desc: decodeName(body.info),
author: body.artist, author: decodeName(body.artist),
// play_count: this.formatPlayCount(body.playnum), // play_count: this.formatPlayCount(body.playnum),
}, },
} }
}) })
}, },
// 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),
// // },
// // }
// })
// })
// },
} }

View File

@ -1,5 +1,5 @@
import { httpFetch } from '../../request' import { httpFetch } from '../../request'
import { dateFormat2 } from '../../' import { dateFormat2 } from '../../index'
export default { export default {
_requestObj: null, _requestObj: null,

View File

@ -34,7 +34,8 @@ const decodeLyric = async(buf, isGetLyricx) => {
return iconv.decode(Buffer.from(output), 'gb18030') return iconv.decode(Buffer.from(output), 'gb18030')
} }
export default async({ lrcBase64, isGetLyricx }) => { export default async({ lrcBuffer, isGetLyricx }) => {
const lrc = await decodeLyric(Buffer.from(lrcBase64, 'base64'), isGetLyricx) const lrc = await decodeLyric(lrcBuffer, isGetLyricx)
// console.log(lrc)
return Buffer.from(lrc).toString('base64') return Buffer.from(lrc).toString('base64')
} }

View File

@ -40,7 +40,7 @@ const kw = {
comment, comment,
getLyric(songInfo, isGetLyricx) { getLyric(songInfo, isGetLyricx) {
// let singer = songInfo.singer.indexOf('、') > -1 ? songInfo.singer.split('、')[0] : songInfo.singer // let singer = songInfo.singer.indexOf('、') > -1 ? songInfo.singer.split('、')[0] : songInfo.singer
return lyric.getLyric(songInfo.songmid, isGetLyricx) return lyric.getLyric(songInfo, isGetLyricx)
}, },
handleMusicInfo(songInfo) { handleMusicInfo(songInfo) {
return this.getMusicInfo(songInfo).then(info => { return this.getMusicInfo(songInfo).then(info => {

View File

@ -116,8 +116,8 @@ export default {
} }
} }
if (formats.includes('HIRFLAC')) { if (formats.includes('HIRFLAC')) {
types.push({ type: 'flac32bit', size: null }) types.push({ type: 'flac24bit', size: null })
_types.flac32bit = { _types.flac24bit = {
size: null, size: null,
} }
} }

View File

@ -1,6 +1,8 @@
import { httpFetch } from '../../request' import { httpFetch } from '../../request'
import { decodeLyric, lrcTools } from './util'
import { decodeName } from '../../index' import { decodeName } from '../../index'
/*
export default { export default {
formatTime(time) { formatTime(time) {
let m = parseInt(time / 60) let m = parseInt(time / 60)
@ -12,6 +14,142 @@ export default {
let lrc = [] let lrc = []
let lrcT = [] 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
const buildParams = (id, isGetLyricx) => {
let params = `user=12345,web,web,web&requester=localhost&req=1&rid=MUSIC_${id}`
if (isGetLyricx) params += '&lrcx=1'
const buf_str = Buffer.from(params)
const buf_str_len = buf_str.length
const output = new Uint16Array(buf_str_len)
let i = 0
while (i < buf_str_len) {
let j = 0
while (j < buf_key_len && i < buf_str_len) {
output[i] = buf_key[j] ^ buf_str[i]
i++
j++
}
}
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}\]/
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 = []
let lrcT = []
for (const item of arr) { for (const item of arr) {
if (lrcSet.has(item.time)) { if (lrcSet.has(item.time)) {
if (lrc.length < 2) continue if (lrc.length < 2) continue
@ -42,31 +180,90 @@ export default {
lrcT, lrcT,
} }
}, },
transformLrc(songinfo, lrclist) { transformLrc(tags, 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('') : '暂无歌词'}` return `${tags.join('\n')}\n${lrclist ? lrclist.map(l => `[${l.time}]${l.text}\n`).join('') : '暂无歌词'}`
}, },
getLyric(songId) { parseLrc(lrc) {
const requestObj = httpFetch(`http://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId=${songId}`) const lines = lrc.split(/\r\n|\r|\n/)
requestObj.promise = requestObj.promise.then(({ body }) => { let tags = []
// console.log(body) let lrcArr = []
if (!body.data?.lrclist?.length) return Promise.reject(new Error('Get lyric failed')) for (let i = 0; i < lines.length; i++) {
let lrcInfo const line = lines[i].trim()
try { let result = timeExp.exec(line)
lrcInfo = this.sortLrcArr(body.data.lrclist) if (result) {
} catch (err) { const text = line.replace(timeExp, '').trim()
console.log(err) let time = RegExp.$1
return Promise.reject(new Error('Get lyric failed')) if (/\.\d\d$/.test(time)) time += '0'
} lrcArr.push({
// console.log(body.data.lrclist) time,
// console.log(lrcInfo.lrc, lrcInfo.lrcT) text,
// console.log({ })
// lyric: decodeName(this.transformLrc(body.data.songinfo, lrc)), } else if (lrcTools.rxps.tagLine.test(line)) {
// tlyric: decodeName(this.transformLrc(body.data.songinfo, lrcT)), tags.push(line)
// })
return {
lyric: decodeName(this.transformLrc(body.data.songinfo, lrcInfo.lrc)),
tlyric: lrcInfo.lrcT.length ? decodeName(this.transformLrc(body.data.songinfo, lrcInfo.lrcT)) : '',
} }
}
const lrcInfo = this.sortLrcArr(lrcArr)
return {
lyric: decodeName(this.transformLrc(tags, lrcInfo.lrc)),
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.lxlyric = lrcTools.parse(lrcInfo.lyric)
// // console.log(lrcInfo.lyric)
// // console.log(lrcInfo.tlyric)
// // console.log(lrcInfo.lxlyric)
// // 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)}`, {
cache: false,
binary: true,
})
requestObj.promise = requestObj.promise.then(({ statusCode, body }) => {
if (statusCode != 200) return Promise.reject(new Error(JSON.stringify(body)))
return decodeLyric({ lrcBuffer: body, 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.lxlyric = lrcTools.parse(lrcInfo.lyric)
} catch {
lrcInfo.lxlyric = ''
}
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
})
}) })
return requestObj return requestObj
}, },

View File

@ -1,5 +1,5 @@
// import '../../polyfill/array.find' // import '../../polyfill/array.find'
// import jshtmlencode from 'js-htmlencode'
import { httpFetch } from '../../request' import { httpFetch } from '../../request'
import { formatPlayTime, decodeName } from '../../index' import { formatPlayTime, decodeName } from '../../index'
// import { debug } from '../../utils/env' // import { debug } from '../../utils/env'
@ -47,8 +47,8 @@ export default {
if (info) { if (info) {
switch (info[2]) { switch (info[2]) {
case '4000': case '4000':
types.push({ type: 'flac32bit', size: info[4] }) types.push({ type: 'flac24bit', size: info[4] })
_types.flac32bit = { _types.flac24bit = {
size: info[4].toLocaleUpperCase(), size: info[4].toLocaleUpperCase(),
} }
break break
@ -106,10 +106,10 @@ export default {
// http://newlyric.kuwo.cn/newlyric.lrc?62355680 // http://newlyric.kuwo.cn/newlyric.lrc?62355680
return this.musicSearch(str, page, limit).then(({ body: result }) => { return this.musicSearch(str, page, limit).then(({ body: result }) => {
// console.log(result) // console.log(result)
if (!result || (result.TOTAL !== '0' && result.SHOW === '0')) return this.search(str, page, { limit }, ++retryNum) if (!result || (result.TOTAL !== '0' && result.SHOW === '0')) return this.search(str, page, limit, ++retryNum)
let list = this.handleResult(result.abslist) let list = this.handleResult(result.abslist)
if (list == null) return this.search(str, page, { limit }, ++retryNum) if (list == null) return this.search(str, page, limit, ++retryNum)
this.total = parseInt(result.TOTAL) this.total = parseInt(result.TOTAL)
this.page = page this.page = page

View File

@ -73,6 +73,7 @@ export const tokenRequest = async(url, options = {}) => {
} }
const requestObj = httpFetch(url, options) const requestObj = httpFetch(url, options)
requestObj.promise = requestObj.promise.then(resp => { requestObj.promise = requestObj.promise.then(resp => {
// console.log(resp)
if (resp.statusCode == 200) { if (resp.statusCode == 200) {
kw_token.token = matchToken(resp.headers) kw_token.token = matchToken(resp.headers)
} }
@ -80,3 +81,99 @@ export const tokenRequest = async(url, options = {}) => {
}) })
return requestObj return requestObj
} }
export const lrcTools = {
rxps: {
wordLine: /^(\[\d{1,2}:.*\d{1,4}\])\s*(\S+(?:\s+\S+)*)?\s*/,
tagLine: /\[(ver|ti|ar|al|offset|by|kuwo):\s*(\S+(?:\s+\S+)*)\s*\]/,
wordTimeAll: /<(-?\d+),(-?\d+)(?:,-?\d+)?>/g,
wordTime: /<(-?\d+),(-?\d+)(?:,-?\d+)?>/,
},
offset: 1,
offset2: 1,
isOK: false,
lines: [],
tags: [],
getWordInfo(str, str2, prevWord) {
const offset = parseInt(str)
const offset2 = parseInt(str2)
let startTime = Math.abs((offset + offset2) / (this.offset * 2))
let endTime = Math.abs((offset - offset2) / (this.offset2 * 2)) + startTime
if (prevWord) {
if (startTime < prevWord.endTime) {
prevWord.endTime = startTime
if (prevWord.startTime > prevWord.endTime) {
prevWord.startTime = prevWord.endTime
}
prevWord.newTimeStr = `<${prevWord.startTime},${prevWord.endTime - prevWord.startTime}>`
// console.log(prevWord)
}
}
return {
startTime,
endTime,
timeStr: `<${startTime},${endTime - startTime}>`,
}
},
parseLine(line) {
if (line.length < 6) return
let result = this.rxps.wordLine.exec(line)
if (result) {
const time = result[1]
let words = result[2]
if (words == null) {
words = ''
}
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)
const wordInfo = this.getWordInfo(result[1], result[2], preTimeInfo)
words = words.replace(timeStr, wordInfo.timeStr)
if (preTimeInfo?.newTimeStr) words = words.replace(preTimeInfo.timeStr, preTimeInfo.newTimeStr)
preTimeInfo = wordInfo
}
this.lines.push(time + words)
return
}
result = this.rxps.tagLine.exec(line)
if (!result) return
if (result[1] == 'kuwo') {
let content = result[2]
if (content != null && content.includes('][')) {
content = content.substring(0, content.indexOf(']['))
}
const valueOf = parseInt(content, 8)
this.offset = Math.trunc(valueOf / 10)
this.offset2 = Math.trunc(valueOf % 10)
if (this.offset == 0 || Number.isNaN(this.offset) || this.offset2 == 0 || Number.isNaN(this.offset2)) {
this.isOK = false
}
} else {
this.tags.push(line)
}
},
parse(lrc) {
// console.log(lrc)
const lines = lrc.split(/\r\n|\r|\n/)
const tools = Object.create(this)
tools.isOK = true
tools.offset = 1
tools.offset2 = 1
tools.lines = []
tools.tags = []
for (const line of lines) {
if (!tools.isOK) throw new Error('failed')
tools.parseLine(line)
}
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
},
}

View File

@ -1,6 +1,6 @@
import { httpFetch } from '../../request' import { httpFetch } from '../../request'
import album from './album' import album from './album'
import { dateFormat2 } from '../../' import { dateFormat2 } from '../../index'
export default { export default {
_requestObj: null, _requestObj: null,

View File

@ -103,30 +103,30 @@ export default {
let size let size
switch (type.formatType) { switch (type.formatType) {
case 'PQ': case 'PQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '128k', size }) types.push({ type: '128k', size })
_types['128k'] = { _types['128k'] = {
size, size,
} }
break break
case 'HQ': case 'HQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '320k', size }) types.push({ type: '320k', size })
_types['320k'] = { _types['320k'] = {
size, size,
} }
break break
case 'SQ': case 'SQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac', size }) types.push({ type: 'flac', size })
_types.flac = { _types.flac = {
size, size,
} }
break break
case 'ZQ': case 'ZQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac32bit', size }) types.push({ type: 'flac24bit', size })
_types.flac32bit = { _types.flac24bit = {
size, size,
} }
break break

View File

@ -1,7 +1,52 @@
import { httpFetch } from '../../request' import { httpFetch } from '../../request'
import musicSearch from './musicSearch' import musicSearch from './musicSearch'
import { decrypt } from './mrc'
export default { const mrcTools = {
rxps: {
lineTime: /^\s*\[(\d+),\d+\]/,
wordTime: /\(\d+,\d+\)/,
wordTimeAll: /(\(\d+,\d+\))/g,
},
parseLyric(str) {
str = str.replace(/\r/g, '')
const lines = str.split('\n')
const lxlrcLines = []
const lrcLines = []
for (const line of lines) {
if (line.length < 6) continue
let result = this.rxps.lineTime.exec(line)
if (!result) continue
const startTime = parseInt(result[1])
let time = startTime
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}`
let words = line.replace(this.rxps.lineTime, '')
lrcLines.push(`[${time}]${words.replace(this.rxps.wordTimeAll, '')}`)
let times = words.match(this.rxps.wordTimeAll)
if (!times) continue
times = times.map(time => {
const result = /\((\d+),(\d+)\)/.exec(time)
return `<${parseInt(result[1]) - startTime},${result[2]}>`
})
const wordArr = words.split(this.rxps.wordTime)
const newWords = times.map((time, index) => `${time}${wordArr[index]}`).join('')
lxlrcLines.push(`[${time}]${newWords}`)
}
return {
lyric: lrcLines.join('\n'),
lxlyric: lxlrcLines.join('\n'),
}
},
getText(url, tryNum = 0) { getText(url, tryNum = 0) {
const requestObj = httpFetch(url, { const requestObj = httpFetch(url, {
headers: { headers: {
@ -16,8 +61,13 @@ export default {
return this.getText(url, ++tryNum) return this.getText(url, ++tryNum)
}) })
}, },
getMrc(url) {
return this.getText(url).then(text => {
return this.parseLyric(decrypt(text))
})
},
getLrc(url) { getLrc(url) {
return this.getText(url) return this.getText(url).then(text => ({ lxlyric: '', lyric: text }))
}, },
getTrc(url) { getTrc(url) {
if (!url) return Promise.resolve('') if (!url) return Promise.resolve('')
@ -31,30 +81,51 @@ export default {
}) })
: Promise.resolve({ lrcUrl: songInfo.lrcUrl, mrcUrl: songInfo.mrcUrl, trcUrl: songInfo.trcUrl }) : Promise.resolve({ lrcUrl: songInfo.lrcUrl, mrcUrl: songInfo.mrcUrl, trcUrl: songInfo.trcUrl })
}, },
getLyric(songInfo, tryNum = 0) { getLyric(songInfo) {
return {
promise: this.getMusicInfo(songInfo).then(info => {
let p
if (info.mrcUrl) p = this.getMrc(info.mrcUrl)
else if (info.lrcUrl) p = this.getLrc(info.lrcUrl)
if (p == null) return Promise.reject('获取歌词失败')
return Promise.all([p, this.getTrc(info.trcUrl)]).then(([lrcInfo, tlyric]) => {
lrcInfo.tlyric = tlyric
return lrcInfo
})
}),
cancelHttp() {},
}
},
}
export default {
getLyricWeb(songInfo, tryNum = 0) {
// console.log(songInfo.copyrightId) // console.log(songInfo.copyrightId)
if (songInfo.lrcUrl) { if (songInfo.lrcUrl) {
return { let requestObj = httpFetch(songInfo.lrcUrl)
promise: this.getMusicInfo(songInfo).then(info => { requestObj.promise = requestObj.promise.then(({ body, statusCode }) => {
return Promise.all([this.getLrc(info.lrcUrl), this.getTrc(info.trcUrl)]).then(([lyric, tlyric]) => { if (statusCode !== 200) {
return { if (tryNum > 5) return Promise.reject('歌词获取失败')
lyric, let tryRequestObj = this.getLyricWeb(songInfo, ++tryNum)
tlyric, requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
} return tryRequestObj.promise
}) }
}), return {
cancelHttp() {}, lyric: body,
} tlyric: '',
}
})
return requestObj
} else { } else {
let requestObj = httpFetch(`http://music.migu.cn/v3/api/music/audioPlayer/getLyric?copyrightId=${songInfo.copyrightId}`, { let requestObj = httpFetch(`https://music.migu.cn/v3/api/music/audioPlayer/getLyric?copyrightId=${songInfo.copyrightId}`, {
headers: { headers: {
Referer: 'http://music.migu.cn/v3/music/player/audio?from=migu', Referer: 'https://music.migu.cn/v3/music/player/audio?from=migu',
}, },
}) })
requestObj.promise = requestObj.promise.then(({ body }) => { requestObj.promise = requestObj.promise.then(({ body }) => {
if (body.returnCode !== '000000' || !body.lyric) { if (body.returnCode !== '000000' || !body.lyric) {
if (tryNum > 5) return Promise.reject(new Error('Get lyric failed')) if (tryNum > 5) return Promise.reject(new Error('Get lyric failed'))
let tryRequestObj = this.getLyric(songInfo, ++tryNum) let tryRequestObj = this.getLyricWeb(songInfo, ++tryNum)
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj) requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
return tryRequestObj.promise return tryRequestObj.promise
} }
@ -66,4 +137,14 @@ export default {
return requestObj return requestObj
} }
}, },
getLyric(songInfo) {
let requestObj = mrcTools.getLyric(songInfo)
requestObj.promise = requestObj.promise.catch(() => {
let webRequestObj = this.getLyricWeb(songInfo)
requestObj.cancelHttp = webRequestObj.cancelHttp.bind(webRequestObj)
return webRequestObj.promise
})
return requestObj
},
} }

View File

@ -0,0 +1,104 @@
// const key = 'karakal@123Qcomyidongtiantianhaoting'
const DELTA = 2654435769n
const MIN_LENGTH = 32
// const SPECIAL_CHAR = '0'
const keyArr = [
27303562373562475n,
18014862372307051n,
22799692160172081n,
34058940340699235n,
30962724186095721n,
27303523720101991n,
27303523720101998n,
31244139033526382n,
28992395054481524n,
]
const teaDecrypt = (data, key) => {
const length = data.length
const lengthBitint = BigInt(length)
if (length >= 1) {
// let j = data[data.length - 1];
let j2 = data[0]
let j3 = toLong((6n + (52n / lengthBitint)) * DELTA)
while (true) {
let j4 = j3
if (j4 == 0n) break
let j5 = toLong(3n & toLong(j4 >> 2n))
let j6 = lengthBitint
while (true) {
j6--
if (j6 > 0n) {
let j7 = data[(j6 - 1n)]
let i = j6
j2 = toLong(data[i] - (toLong(toLong(j2 ^ j4) + toLong(j7 ^ key[toLong(toLong(3n & j6) ^ j5)])) ^ toLong(toLong(toLong(j7 >> 5n) ^ toLong(j2 << 2n)) + toLong(toLong(j2 >> 3n) ^ toLong(j7 << 4n)))))
data[i] = j2
} else break
}
let j8 = data[lengthBitint - 1n]
j2 = toLong(data[0n] - toLong(toLong(toLong(key[toLong(toLong(j6 & 3n) ^ j5)] ^ j8) + toLong(j2 ^ j4)) ^ toLong(toLong(toLong(j8 >> 5n) ^ toLong(j2 << 2n)) + toLong(toLong(j2 >> 3n) ^ toLong(j8 << 4n)))))
data[0] = j2
j3 = toLong(j4 - DELTA)
}
}
return data
}
const longArrToString = (data) => {
const arrayList = []
for (const j of data) arrayList.push(longToBytes(j).toString('utf16le'))
return arrayList.join('')
}
// https://stackoverflow.com/a/29132118
const longToBytes = (l) => {
const result = Buffer.alloc(8)
for (let i = 0; i < 8; i++) {
result[i] = parseInt(l & 0xFFn)
l >>= 8n
}
return result
}
const toBigintArray = (data) => {
const length = Math.floor(data.length / 16)
const jArr = Array(length)
for (let i = 0; i < length; i++) {
jArr[i] = toLong(data.substring(i * 16, (i * 16) + 16))
}
return jArr
}
// https://github.com/lyswhut/lx-music-desktop/issues/445#issuecomment-1139338682
const MAX = 9223372036854775807n
const MIN = -9223372036854775808n
const toLong = str => {
const num = typeof str == 'string' ? BigInt('0x' + str) : str
if (num > MAX) return toLong(num - (1n << 64n))
else if (num < MIN) return toLong(num + (1n << 64n))
return num
}
export const decrypt = (data) => {
// console.log(data.length)
// -3551594764563790630
// console.log(toLongArrayFromArr(Buffer.from(key)))
// console.log(teaDecrypt(toBigintArray(data), keyArr))
// console.log(longArrToString(teaDecrypt(toBigintArray(data), keyArr)))
// console.log(toByteArray(teaDecrypt(toBigintArray(data), keyArr)))
return (data == null || data.length < MIN_LENGTH)
? data
: longArrToString(teaDecrypt(toBigintArray(data), keyArr))
}
// console.log(14895149309145760986n - )
// console.log(toLong('14895149309145760986'))
// console.log(decrypt(str))
// console.log(decrypt(str))
// console.log(toByteArray([6048138644744000495n]))
// console.log(toByteArray([16325999628386395n]))
// console.log(toLong(90994076459972177136n))

View File

@ -47,30 +47,30 @@ export default {
let size let size
switch (type.formatType) { switch (type.formatType) {
case 'PQ': case 'PQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '128k', size }) types.push({ type: '128k', size })
_types['128k'] = { _types['128k'] = {
size, size,
} }
break break
case 'HQ': case 'HQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '320k', size }) types.push({ type: '320k', size })
_types['320k'] = { _types['320k'] = {
size, size,
} }
break break
case 'SQ': case 'SQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac', size }) types.push({ type: 'flac', size })
_types.flac = { _types.flac = {
size, size,
} }
break break
case 'ZQ': case 'ZQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac32bit', size }) types.push({ type: 'flac24bit', size })
_types.flac32bit = { _types.flac24bit = {
size, size,
} }
break break
@ -117,7 +117,7 @@ export default {
const songResultData = result.songResultData || { result: [], totalCount: 0 } const songResultData = result.songResultData || { result: [], totalCount: 0 }
let list = this.handleResult(songResultData.result) let list = this.handleResult(songResultData.result)
if (list == null) return this.search(str, page, { limit }, retryNum) if (list == null) return this.search(str, page, limit, retryNum)
this.total = parseInt(songResultData.totalCount) this.total = parseInt(songResultData.totalCount)
this.page = page this.page = page

View File

@ -18,7 +18,7 @@ const tx = {
}, },
getLyric(songInfo) { getLyric(songInfo) {
// let singer = songInfo.singer.indexOf('、') > -1 ? songInfo.singer.split('、')[0] : songInfo.singer // let singer = songInfo.singer.indexOf('、') > -1 ? songInfo.singer.split('、')[0] : songInfo.singer
return lyric.getLyric(songInfo.songmid) return lyric.getLyric(songInfo)
}, },
getPic(songInfo) { getPic(songInfo) {
return apis('tx').getPic(songInfo) return apis('tx').getPic(songInfo)

View File

@ -139,8 +139,8 @@ export default {
} }
if (item.file.size_hires !== 0) { if (item.file.size_hires !== 0) {
let size = sizeFormate(item.file.size_hires) let size = sizeFormate(item.file.size_hires)
types.push({ type: 'flac32bit', size }) types.push({ type: 'flac24bit', size })
_types.flac32bit = { _types.flac24bit = {
size, size,
} }
} }

View File

@ -6,12 +6,12 @@ import crypto from 'crypto'
* @param {*} type * @param {*} type
*/ */
const types = ['flac32bit', 'flac', 'wav', 'ape', '320k', '192k', '128k'] export const QUALITYS = ['flac24bit', 'flac', 'wav', 'ape', '320k', '192k', '128k']
export const getMusicType = (info, type) => { export const getMusicType = (info, type) => {
const list = global.lx.qualityList[info.source] const list = global.lx.qualityList[info.source]
if (!list) return '128k' if (!list) return '128k'
if (!list.includes(type)) type = list[list.length - 1] if (!list.includes(type)) type = list[list.length - 1]
const rangeType = types.slice(types.indexOf(type)) const rangeType = QUALITYS.slice(QUALITYS.indexOf(type))
for (const type of rangeType) { for (const type of rangeType) {
if (info._types[type]) return type if (info._types[type]) return type
} }

View File

@ -1,5 +1,5 @@
import { httpFetch } from '../../request' import { httpFetch } from '../../request'
import { linuxapi } from './utils/crypto' import { eapi } from './utils/crypto'
// import { decodeName } from '../..' // import { decodeName } from '../..'
// const parseLyric = (str, lrc) => { // const parseLyric = (str, lrc) => {
@ -34,31 +34,209 @@ import { linuxapi } from './utils/crypto'
// return lxlyric.trim() // return lxlyric.trim()
// } // }
export default songmid => { const eapiRequest = (url, data) => {
const requestObj = httpFetch('https://music.163.com/api/linux/forward', { return httpFetch('https://interface3.music.163.com/eapi/song/lyric/v1', {
method: 'post', 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', headers: {
form: linuxapi({ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
method: 'POST', origin: 'https://music.163.com',
url: 'https://music.163.com/api/song/lyric?_nmclfl=1', // 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',
params: { },
id: songmid, form: eapi(url, data),
tv: -1, })
lv: -1, // requestObj.promise = requestObj.promise.then(({ body }) => {
rv: -1, // // console.log(raw)
kv: -1, // console.log(body)
}, // // console.log(eapiDecrypt(raw))
}), // // return eapiDecrypt(raw)
// return body
// })
// return requestObj
}
const parseTools = {
rxps: {
info: /^{"/,
lineTime: /^\[(\d+),\d+\]/,
wordTime: /\(\d+,\d+,\d+\)/,
wordTimeAll: /(\(\d+,\d+,\d+\))/g,
},
msFormat(timeMs) {
if (Number.isNaN(timeMs)) return ''
let ms = timeMs % 1000
timeMs /= 1000
let m = parseInt(timeMs / 60).toString().padStart(2, '0')
timeMs %= 60
let s = parseInt(timeMs).toString().padStart(2, '0')
return `[${m}:${s}.${ms}]`
},
parseLyric(lines) {
const lxlrcLines = []
const lrcLines = []
for (let line of lines) {
line = line.trim()
let result = this.rxps.lineTime.exec(line)
if (!result) {
if (line.startsWith('[offset')) {
lxlrcLines.push(line)
lrcLines.push(line)
}
continue
}
const startMsTime = parseInt(result[1])
const startTimeStr = this.msFormat(startMsTime)
if (!startTimeStr) continue
let words = line.replace(this.rxps.lineTime, '')
lrcLines.push(`${startTimeStr}${words.replace(this.rxps.wordTimeAll, '')}`)
let times = words.match(this.rxps.wordTimeAll)
if (!times) continue
times = times.map(time => {
const result = /\((\d+),(\d+),\d+\)/.exec(time)
return `<${Math.max(parseInt(result[1]) - startMsTime, 0)},${result[2]}>`
})
const wordArr = words.split(this.rxps.wordTime)
wordArr.shift()
const newWords = times.map((time, index) => `${time}${wordArr[index]}`).join('')
lxlrcLines.push(`${startTimeStr}${newWords}`)
}
return {
lyricLines: lrcLines,
lxlyricLines: lxlrcLines,
}
},
parseHeaderInfo(str) {
str = str.trim()
str = str.replace(/\r/g, '')
if (!str) return null
const lines = str.split('\n')
return lines.map(line => {
if (!this.rxps.info.test(line)) return line
try {
const info = JSON.parse(line)
const timeTag = this.msFormat(info.t)
return timeTag ? `${timeTag}${info.c.map(t => t.tx).join('')}` : ''
} catch {
return ''
}
})
},
fixTimeTag(lrcLines, targetLrcLines) {
const timeTagRxp = /^\[[\d:.]+\]/
return targetLrcLines.map((line, index) => {
const timeTag = timeTagRxp.exec(lrcLines[index])
if (!timeTag) return line
return line.replace(timeTagRxp, timeTag[0])
})
},
parse(ylrc, ytlrc, yrlrc, lrc, tlrc, rlrc) {
const info = {
lyric: '',
tlyric: '',
rlyric: '',
lxlyric: '',
}
if (ylrc) {
let lines = this.parseHeaderInfo(ylrc)
if (lines) {
const result = this.parseLyric(lines)
if (ytlrc) {
const lines = this.parseHeaderInfo(ytlrc)
if (lines) {
if (lines.length == result.lyricLines.length) {
info.tlyric = this.fixTimeTag(result.lyricLines, lines).join('\n')
} else info.tlyric = lines.join('\n')
}
}
if (yrlrc) {
const lines = this.parseHeaderInfo(yrlrc)
if (lines) {
if (lines.length == result.lyricLines.length) {
info.rlyric = this.fixTimeTag(result.lyricLines, lines).join('\n')
} else info.rlyric = lines.join('\n')
}
}
const timeRxp = /^\[[\d:.]+\]/
const headers = lines.filter(l => timeRxp.test(l)).join('\n')
info.lyric = `${headers}\n${result.lyricLines.join('\n')}`
info.lxlyric = result.lxlyricLines.join('\n')
return info
}
}
if (lrc) {
const lines = this.parseHeaderInfo(lrc)
if (lines) info.lyric = lines.join('\n')
}
if (tlrc) {
const lines = this.parseHeaderInfo(tlrc)
if (lines) info.tlyric = lines.join('\n')
}
if (rlrc) {
const lines = this.parseHeaderInfo(rlrc)
if (lines) info.rlyric = lines.join('\n')
}
return info
},
}
// 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 ?? '',
// // lxlyric: parseLyric(body.klyric.lyric),
// }
// })
// return requestObj
// }
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/lyric_new.js
export default songmid => {
const requestObj = eapiRequest('/api/song/lyric/v1', {
id: songmid,
cp: false,
tv: 0,
lv: 0,
rv: 0,
kv: 0,
yv: 0,
ytv: 0,
yrv: 0,
}) })
requestObj.promise = requestObj.promise.then(({ body }) => { requestObj.promise = requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.code !== 200 || !body?.lrc?.lyric) return Promise.reject(new Error('Get lyric failed')) if (body.code !== 200 || !body?.lrc?.lyric) return Promise.reject(new Error('Get lyric failed'))
return { const info = parseTools.parse(body.yrc?.lyric, body.ytlrc?.lyric, body.yromalrc?.lyric, body.lrc.lyric, body.tlyric?.lyric, body.romalrc?.lyric)
lyric: body.lrc.lyric, // console.log(info)
tlyric: body.tlyric.lyric, if (!info.lyric) return Promise.reject(new Error('Get lyric failed'))
rlyric: body.romalrc?.lyric ?? '', return info
// lxlyric: parseLyric(body.klyric.lyric),
}
}) })
return requestObj return requestObj
} }

View File

@ -1,6 +1,6 @@
import { httpFetch } from '../../request' import { httpFetch } from '../../request'
import { weapi } from './utils/crypto' import { weapi } from './utils/crypto'
import { formatPlayTime, sizeFormate } from '../..' import { formatPlayTime, sizeFormate } from '../../index'
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/song_detail.js // https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/song_detail.js
export default { export default {

View File

@ -2,7 +2,7 @@
// import { weapi } from './utils/crypto' // import { weapi } from './utils/crypto'
import { sizeFormate, formatPlayTime } from '../../index' import { sizeFormate, formatPlayTime } from '../../index'
// import musicDetailApi from './musicDetail' // import musicDetailApi from './musicDetail'
import { eapiRequest } from './utils' import { eapiRequest } from './utils/index'
export default { export default {
limit: 30, limit: 30,
@ -78,11 +78,11 @@ export default {
if (limit == null) limit = this.limit if (limit == null) limit = this.limit
return this.musicSearch(str, page, limit).then(result => { return this.musicSearch(str, page, limit).then(result => {
// console.log(result) // console.log(result)
if (!result || result.code !== 200) return this.search(str, page, { limit }, retryNum) if (!result || result.code !== 200) return this.search(str, page, limit, retryNum)
let list = this.handleResult(result.result.songs || []) let list = this.handleResult(result.result.songs || [])
// console.log(list) // console.log(list)
if (list == null) return this.search(str, page, { limit }, retryNum) if (list == null) return this.search(str, page, limit, retryNum)
this.total = result.result.songCount || 0 this.total = result.result.songCount || 0
this.page = page this.page = page

View File

@ -153,6 +153,19 @@ const handleRequestData = async(url, {
} }
} }
// https://stackoverflow.com/a/64945178
const blobToBuffer = (blob) => {
return new Promise((resolve, reject) => {
const reader = new global.FileReader()
reader.onerror = reject
reader.onload = () => {
const data = reader.result.slice(reader.result.indexOf('base64,') + 7)
resolve(Buffer.from(data, 'base64'))
}
reader.readAsDataURL(blob)
})
}
const fetchData = (url, { timeout = 15000, ...options }) => { const fetchData = (url, { timeout = 15000, ...options }) => {
console.log('---start---', url) console.log('---start---', url)
@ -167,7 +180,7 @@ const fetchData = (url, { timeout = 15000, ...options }) => {
return global.fetch(url, { return global.fetch(url, {
...options, ...options,
signal: controller.signal, signal: controller.signal,
}).then(resp => resp.text().then(text => { }).then(resp => (options.binary ? resp.blob() : resp.text()).then(text => {
// console.log(options, headers, text) // console.log(options, headers, text)
return { return {
headers: resp.headers.map, headers: resp.headers.map,
@ -177,11 +190,17 @@ const fetchData = (url, { timeout = 15000, ...options }) => {
ok: resp.ok, ok: resp.ok,
} }
})).then(resp => { })).then(resp => {
try { if (options.binary) {
resp.body = JSON.parse(resp.body) return blobToBuffer(resp.body).then(buffer => {
} catch (_) { resp.body = buffer
return resp
})
} else {
try {
resp.body = JSON.parse(resp.body)
} catch {}
return resp
} }
return resp
}).catch(err => { }).catch(err => {
// console.log(err, err.code, err.message) // console.log(err, err.code, err.message)
return Promise.reject(err) return Promise.reject(err)