diff --git a/src/utils/musicSdk/api-source-info.js b/src/utils/musicSdk/api-source-info.js index 836bb1b..587e850 100644 --- a/src/utils/musicSdk/api-source-info.js +++ b/src/utils/musicSdk/api-source-info.js @@ -11,7 +11,6 @@ module.exports = [ tx: ['128k'], wy: ['128k'], mg: ['128k'], - // xm: ['128k'], // bd: ['128k'], }, }, diff --git a/src/utils/musicSdk/bd/leaderboard.js b/src/utils/musicSdk/bd/leaderboard.js index 1ced147..21f2c64 100644 --- a/src/utils/musicSdk/bd/leaderboard.js +++ b/src/utils/musicSdk/bd/leaderboard.js @@ -1,6 +1,6 @@ import { httpFetch } from '../../request' // import { formatPlayTime } from '../../index' -// import jshtmlencode from 'js-htmlencode' + const boardList = [ // { id: 'bd__601', name: '歌单榜', bangid: '601' }, diff --git a/src/utils/musicSdk/bd/musicSearch.js b/src/utils/musicSdk/bd/musicSearch.js index 009231a..5ec2589 100644 --- a/src/utils/musicSdk/bd/musicSearch.js +++ b/src/utils/musicSdk/bd/musicSearch.js @@ -1,5 +1,5 @@ // import '../../polyfill/array.find' -// import jshtmlencode from 'js-htmlencode' + import { httpFetch } from '../../request' import { formatPlayTime } from '../../index' // import { debug } from '../../utils/env' @@ -62,15 +62,15 @@ export default { }) 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 (limit == null) limit = this.limit 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) - 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.page = page diff --git a/src/utils/musicSdk/kg/hotSearch.js b/src/utils/musicSdk/kg/hotSearch.js index 0496c0e..e434b0b 100644 --- a/src/utils/musicSdk/kg/hotSearch.js +++ b/src/utils/musicSdk/kg/hotSearch.js @@ -1,5 +1,5 @@ import { httpFetch } from '../../request' -import { decodeName } from '../..' +import { decodeName } from '../../index' export default { _requestObj: null, diff --git a/src/utils/musicSdk/kg/lyric.js b/src/utils/musicSdk/kg/lyric.js index 85c2efe..3e16e2a 100644 --- a/src/utils/musicSdk/kg/lyric.js +++ b/src/utils/musicSdk/kg/lyric.js @@ -1,6 +1,6 @@ import { httpFetch } from '../../request' import { decodeLyric } from './util' -import { decodeName } from '../..' +import { decodeName } from '../../index' const headExp = /^.*\[id:\$\w+\]\n/ diff --git a/src/utils/musicSdk/kg/musicSearch.js b/src/utils/musicSdk/kg/musicSearch.js index b9357cb..60944dd 100644 --- a/src/utils/musicSdk/kg/musicSearch.js +++ b/src/utils/musicSdk/kg/musicSearch.js @@ -1,5 +1,5 @@ // import '../../polyfill/array.find' -// import jshtmlencode from 'js-htmlencode' + import { httpFetch } from '../../request' import { decodeName, formatPlayTime, sizeFormate } from '../../index' // import { debug } from '../../utils/env' @@ -82,10 +82,10 @@ export default { if (limit == null) limit = this.limit // http://newlyric.kuwo.cn/newlyric.lrc?62355680 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) - 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.page = page diff --git a/src/utils/musicSdk/kg/pic.js b/src/utils/musicSdk/kg/pic.js index ed0732a..5d9c262 100644 --- a/src/utils/musicSdk/kg/pic.js +++ b/src/utils/musicSdk/kg/pic.js @@ -38,7 +38,6 @@ export default { }, ) requestObj.promise = requestObj.promise.then(({ body }) => { - // console.log(body) if (body.error_code !== 0) return Promise.reject('图片获取失败') let info = body.data[0].info const img = info.imgsize ? info.image.replace('{size}', info.imgsize[0]) : info.image diff --git a/src/utils/musicSdk/kw/album.js b/src/utils/musicSdk/kw/album.js index 5110ca0..79e641f 100644 --- a/src/utils/musicSdk/kw/album.js +++ b/src/utils/musicSdk/kw/album.js @@ -44,8 +44,8 @@ export default { } } if (formats.includes('HIRFLAC')) { - types.push({ type: 'flac32bit', size: null }) - _types.flac32bit = { + types.push({ type: 'flac24bit', size: null }) + _types.flac24bit = { size: null, } } @@ -84,6 +84,7 @@ export default { body = objStr2JSON(body) // console.log(body) if (!body.musiclist) return this.getAlbumListDetail(id, page, ++retryNum) + body.name = decodeName(body.name) return { list: this.filterListDetail(body.musiclist, body.name, body.albumid), page, @@ -93,11 +94,38 @@ export default { info: { name: body.name, img: body.img || body.hts_img, - desc: body.info, - author: body.artist, + desc: decodeName(body.info), + author: decodeName(body.artist), // 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), + // // }, + // // } + // }) + // }) + // }, } diff --git a/src/utils/musicSdk/kw/comment.js b/src/utils/musicSdk/kw/comment.js index 39c49e0..287a376 100644 --- a/src/utils/musicSdk/kw/comment.js +++ b/src/utils/musicSdk/kw/comment.js @@ -1,5 +1,5 @@ import { httpFetch } from '../../request' -import { dateFormat2 } from '../../' +import { dateFormat2 } from '../../index' export default { _requestObj: null, diff --git a/src/utils/musicSdk/kw/decodeLyric.js b/src/utils/musicSdk/kw/decodeLyric.js index 25d32ee..75fe520 100644 --- a/src/utils/musicSdk/kw/decodeLyric.js +++ b/src/utils/musicSdk/kw/decodeLyric.js @@ -34,7 +34,8 @@ const decodeLyric = async(buf, isGetLyricx) => { return iconv.decode(Buffer.from(output), 'gb18030') } -export default async({ lrcBase64, isGetLyricx }) => { - const lrc = await decodeLyric(Buffer.from(lrcBase64, 'base64'), isGetLyricx) +export default async({ lrcBuffer, isGetLyricx }) => { + const lrc = await decodeLyric(lrcBuffer, isGetLyricx) + // console.log(lrc) return Buffer.from(lrc).toString('base64') } diff --git a/src/utils/musicSdk/kw/index.js b/src/utils/musicSdk/kw/index.js index 27c8817..b943f18 100644 --- a/src/utils/musicSdk/kw/index.js +++ b/src/utils/musicSdk/kw/index.js @@ -40,7 +40,7 @@ const kw = { comment, getLyric(songInfo, isGetLyricx) { // 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) { return this.getMusicInfo(songInfo).then(info => { diff --git a/src/utils/musicSdk/kw/leaderboard.js b/src/utils/musicSdk/kw/leaderboard.js index 626d035..7c064c7 100644 --- a/src/utils/musicSdk/kw/leaderboard.js +++ b/src/utils/musicSdk/kw/leaderboard.js @@ -116,8 +116,8 @@ export default { } } if (formats.includes('HIRFLAC')) { - types.push({ type: 'flac32bit', size: null }) - _types.flac32bit = { + types.push({ type: 'flac24bit', size: null }) + _types.flac24bit = { size: null, } } diff --git a/src/utils/musicSdk/kw/lyric.js b/src/utils/musicSdk/kw/lyric.js index 5679d6b..cab9a61 100644 --- a/src/utils/musicSdk/kw/lyric.js +++ b/src/utils/musicSdk/kw/lyric.js @@ -1,6 +1,8 @@ import { httpFetch } from '../../request' +import { decodeLyric, lrcTools } from './util' import { decodeName } from '../../index' +/* export default { formatTime(time) { let m = parseInt(time / 60) @@ -12,6 +14,142 @@ export default { 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 +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) { if (lrcSet.has(item.time)) { if (lrc.length < 2) continue @@ -42,31 +180,90 @@ export default { 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('') : '暂无歌词'}` + transformLrc(tags, lrclist) { + return `${tags.join('\n')}\n${lrclist ? lrclist.map(l => `[${l.time}]${l.text}\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 (err) { - console.log(err) - 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)) : '', + parseLrc(lrc) { + const lines = lrc.split(/\r\n|\r|\n/) + let tags = [] + let lrcArr = [] + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim() + let result = timeExp.exec(line) + if (result) { + const text = line.replace(timeExp, '').trim() + let time = RegExp.$1 + if (/\.\d\d$/.test(time)) time += '0' + lrcArr.push({ + time, + text, + }) + } else if (lrcTools.rxps.tagLine.test(line)) { + tags.push(line) } + } + 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 }, diff --git a/src/utils/musicSdk/kw/musicSearch.js b/src/utils/musicSdk/kw/musicSearch.js index 9edd8e9..fdcb378 100644 --- a/src/utils/musicSdk/kw/musicSearch.js +++ b/src/utils/musicSdk/kw/musicSearch.js @@ -1,5 +1,5 @@ // import '../../polyfill/array.find' -// import jshtmlencode from 'js-htmlencode' + import { httpFetch } from '../../request' import { formatPlayTime, decodeName } from '../../index' // import { debug } from '../../utils/env' @@ -47,8 +47,8 @@ export default { if (info) { switch (info[2]) { case '4000': - types.push({ type: 'flac32bit', size: info[4] }) - _types.flac32bit = { + types.push({ type: 'flac24bit', size: info[4] }) + _types.flac24bit = { size: info[4].toLocaleUpperCase(), } break @@ -106,10 +106,10 @@ export default { // 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) + if (!result || (result.TOTAL !== '0' && result.SHOW === '0')) return this.search(str, page, limit, ++retryNum) 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.page = page diff --git a/src/utils/musicSdk/kw/util.js b/src/utils/musicSdk/kw/util.js index 625f48c..762b1f4 100644 --- a/src/utils/musicSdk/kw/util.js +++ b/src/utils/musicSdk/kw/util.js @@ -73,6 +73,7 @@ export const tokenRequest = async(url, options = {}) => { } const requestObj = httpFetch(url, options) requestObj.promise = requestObj.promise.then(resp => { + // console.log(resp) if (resp.statusCode == 200) { kw_token.token = matchToken(resp.headers) } @@ -80,3 +81,99 @@ export const tokenRequest = async(url, options = {}) => { }) 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 + }, +} diff --git a/src/utils/musicSdk/mg/comment.js b/src/utils/musicSdk/mg/comment.js index f8468b8..a5b6b08 100644 --- a/src/utils/musicSdk/mg/comment.js +++ b/src/utils/musicSdk/mg/comment.js @@ -1,6 +1,6 @@ import { httpFetch } from '../../request' import album from './album' -import { dateFormat2 } from '../../' +import { dateFormat2 } from '../../index' export default { _requestObj: null, diff --git a/src/utils/musicSdk/mg/leaderboard.js b/src/utils/musicSdk/mg/leaderboard.js index 2ac4e25..8a6733d 100644 --- a/src/utils/musicSdk/mg/leaderboard.js +++ b/src/utils/musicSdk/mg/leaderboard.js @@ -103,30 +103,30 @@ export default { let size switch (type.formatType) { case 'PQ': - size = sizeFormate(type.size) + size = sizeFormate(type.size ?? type.androidSize) types.push({ type: '128k', size }) _types['128k'] = { size, } break case 'HQ': - size = sizeFormate(type.size) + size = sizeFormate(type.size ?? type.androidSize) types.push({ type: '320k', size }) _types['320k'] = { size, } break case 'SQ': - size = sizeFormate(type.size) + size = sizeFormate(type.size ?? type.androidSize) types.push({ type: 'flac', size }) _types.flac = { size, } break case 'ZQ': - size = sizeFormate(type.size) - types.push({ type: 'flac32bit', size }) - _types.flac32bit = { + size = sizeFormate(type.size ?? type.androidSize) + types.push({ type: 'flac24bit', size }) + _types.flac24bit = { size, } break diff --git a/src/utils/musicSdk/mg/lyric.js b/src/utils/musicSdk/mg/lyric.js index 73abeb7..fc75e8e 100644 --- a/src/utils/musicSdk/mg/lyric.js +++ b/src/utils/musicSdk/mg/lyric.js @@ -1,7 +1,52 @@ import { httpFetch } from '../../request' 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) { const requestObj = httpFetch(url, { headers: { @@ -16,8 +61,13 @@ export default { return this.getText(url, ++tryNum) }) }, + getMrc(url) { + return this.getText(url).then(text => { + return this.parseLyric(decrypt(text)) + }) + }, getLrc(url) { - return this.getText(url) + return this.getText(url).then(text => ({ lxlyric: '', lyric: text })) }, getTrc(url) { if (!url) return Promise.resolve('') @@ -31,30 +81,51 @@ export default { }) : 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) if (songInfo.lrcUrl) { - return { - promise: this.getMusicInfo(songInfo).then(info => { - return Promise.all([this.getLrc(info.lrcUrl), this.getTrc(info.trcUrl)]).then(([lyric, tlyric]) => { - return { - lyric, - tlyric, - } - }) - }), - cancelHttp() {}, - } + let requestObj = httpFetch(songInfo.lrcUrl) + requestObj.promise = requestObj.promise.then(({ body, statusCode }) => { + if (statusCode !== 200) { + if (tryNum > 5) return Promise.reject('歌词获取失败') + let tryRequestObj = this.getLyricWeb(songInfo, ++tryNum) + requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj) + return tryRequestObj.promise + } + return { + lyric: body, + tlyric: '', + } + }) + return requestObj } 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: { - 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 }) => { if (body.returnCode !== '000000' || !body.lyric) { 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) return tryRequestObj.promise } @@ -66,4 +137,14 @@ export default { 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 + }, } diff --git a/src/utils/musicSdk/mg/mrc.js b/src/utils/musicSdk/mg/mrc.js new file mode 100644 index 0000000..98ebb68 --- /dev/null +++ b/src/utils/musicSdk/mg/mrc.js @@ -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)) + diff --git a/src/utils/musicSdk/mg/musicSearch.js b/src/utils/musicSdk/mg/musicSearch.js index 421f4ce..1dbd5e2 100644 --- a/src/utils/musicSdk/mg/musicSearch.js +++ b/src/utils/musicSdk/mg/musicSearch.js @@ -47,30 +47,30 @@ export default { let size switch (type.formatType) { case 'PQ': - size = sizeFormate(type.size) + size = sizeFormate(type.size ?? type.androidSize) types.push({ type: '128k', size }) _types['128k'] = { size, } break case 'HQ': - size = sizeFormate(type.size) + size = sizeFormate(type.size ?? type.androidSize) types.push({ type: '320k', size }) _types['320k'] = { size, } break case 'SQ': - size = sizeFormate(type.size) + size = sizeFormate(type.size ?? type.androidSize) types.push({ type: 'flac', size }) _types.flac = { size, } break case 'ZQ': - size = sizeFormate(type.size) - types.push({ type: 'flac32bit', size }) - _types.flac32bit = { + size = sizeFormate(type.size ?? type.androidSize) + types.push({ type: 'flac24bit', size }) + _types.flac24bit = { size, } break @@ -117,7 +117,7 @@ export default { const songResultData = result.songResultData || { result: [], totalCount: 0 } 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.page = page diff --git a/src/utils/musicSdk/tx/index.js b/src/utils/musicSdk/tx/index.js index 6c1bd56..0c755ac 100644 --- a/src/utils/musicSdk/tx/index.js +++ b/src/utils/musicSdk/tx/index.js @@ -18,7 +18,7 @@ const tx = { }, getLyric(songInfo) { // let singer = songInfo.singer.indexOf('、') > -1 ? songInfo.singer.split('、')[0] : songInfo.singer - return lyric.getLyric(songInfo.songmid) + return lyric.getLyric(songInfo) }, getPic(songInfo) { return apis('tx').getPic(songInfo) diff --git a/src/utils/musicSdk/tx/leaderboard.js b/src/utils/musicSdk/tx/leaderboard.js index b01e73b..38f34d8 100644 --- a/src/utils/musicSdk/tx/leaderboard.js +++ b/src/utils/musicSdk/tx/leaderboard.js @@ -139,8 +139,8 @@ export default { } if (item.file.size_hires !== 0) { let size = sizeFormate(item.file.size_hires) - types.push({ type: 'flac32bit', size }) - _types.flac32bit = { + types.push({ type: 'flac24bit', size }) + _types.flac24bit = { size, } } diff --git a/src/utils/musicSdk/utils.js b/src/utils/musicSdk/utils.js index ad83d10..e0ee37f 100644 --- a/src/utils/musicSdk/utils.js +++ b/src/utils/musicSdk/utils.js @@ -6,12 +6,12 @@ import crypto from 'crypto' * @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) => { const list = global.lx.qualityList[info.source] if (!list) return '128k' 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) { if (info._types[type]) return type } diff --git a/src/utils/musicSdk/wy/lyric.js b/src/utils/musicSdk/wy/lyric.js index 33c4089..f052c88 100644 --- a/src/utils/musicSdk/wy/lyric.js +++ b/src/utils/musicSdk/wy/lyric.js @@ -1,5 +1,5 @@ import { httpFetch } from '../../request' -import { linuxapi } from './utils/crypto' +import { eapi } from './utils/crypto' // import { decodeName } from '../..' // const parseLyric = (str, lrc) => { @@ -34,31 +34,209 @@ import { linuxapi } from './utils/crypto' // return lxlyric.trim() // } -export default songmid => { - const requestObj = httpFetch('https://music.163.com/api/linux/forward', { +const eapiRequest = (url, data) => { + return httpFetch('https://interface3.music.163.com/eapi/song/lyric/v1', { 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, - }, - }), + 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 = { + 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 }) => { + // console.log(body) if (body.code !== 200 || !body?.lrc?.lyric) return Promise.reject(new Error('Get lyric failed')) - return { - lyric: body.lrc.lyric, - tlyric: body.tlyric.lyric, - rlyric: body.romalrc?.lyric ?? '', - // lxlyric: parseLyric(body.klyric.lyric), - } + const info = parseTools.parse(body.yrc?.lyric, body.ytlrc?.lyric, body.yromalrc?.lyric, body.lrc.lyric, body.tlyric?.lyric, body.romalrc?.lyric) + // console.log(info) + if (!info.lyric) return Promise.reject(new Error('Get lyric failed')) + return info }) return requestObj } - diff --git a/src/utils/musicSdk/wy/musicDetail.js b/src/utils/musicSdk/wy/musicDetail.js index 5b19042..d4c4ac3 100644 --- a/src/utils/musicSdk/wy/musicDetail.js +++ b/src/utils/musicSdk/wy/musicDetail.js @@ -1,6 +1,6 @@ import { httpFetch } from '../../request' 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 export default { diff --git a/src/utils/musicSdk/wy/musicSearch.js b/src/utils/musicSdk/wy/musicSearch.js index 1fccbb7..670a634 100644 --- a/src/utils/musicSdk/wy/musicSearch.js +++ b/src/utils/musicSdk/wy/musicSearch.js @@ -2,7 +2,7 @@ // import { weapi } from './utils/crypto' import { sizeFormate, formatPlayTime } from '../../index' // import musicDetailApi from './musicDetail' -import { eapiRequest } from './utils' +import { eapiRequest } from './utils/index' export default { limit: 30, @@ -78,11 +78,11 @@ export default { 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) + if (!result || result.code !== 200) return this.search(str, page, limit, retryNum) let list = this.handleResult(result.result.songs || []) // 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.page = page diff --git a/src/utils/request.js b/src/utils/request.js index 5ae7ae4..dd65ffb 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -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 }) => { console.log('---start---', url) @@ -167,7 +180,7 @@ const fetchData = (url, { timeout = 15000, ...options }) => { return global.fetch(url, { ...options, signal: controller.signal, - }).then(resp => resp.text().then(text => { + }).then(resp => (options.binary ? resp.blob() : resp.text()).then(text => { // console.log(options, headers, text) return { headers: resp.headers.map, @@ -177,11 +190,17 @@ const fetchData = (url, { timeout = 15000, ...options }) => { ok: resp.ok, } })).then(resp => { - try { - resp.body = JSON.parse(resp.body) - } catch (_) { + if (options.binary) { + return blobToBuffer(resp.body).then(buffer => { + resp.body = buffer + return resp + }) + } else { + try { + resp.body = JSON.parse(resp.body) + } catch {} + return resp } - return resp }).catch(err => { // console.log(err, err.code, err.message) return Promise.reject(err)