From ea35ed25d80a4dd6264b7b4d8f0645b9ea8f4cce Mon Sep 17 00:00:00 2001 From: lyswhut Date: Wed, 12 May 2021 14:04:08 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dkw=E9=83=A8=E5=88=86=E6=AD=8C?= =?UTF-8?q?=E5=8D=95=E6=97=A0=E6=B3=95=E6=89=93=E5=BC=80=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- publish/changeLog.md | 2 +- src/renderer/utils/music/kw/album.js | 101 ++++++++++++++++++++++ src/renderer/utils/music/kw/songList.js | 76 ++++++++++++++-- src/renderer/utils/music/kw/tempSearch.js | 23 ++--- src/renderer/utils/music/kw/util.js | 67 +++++++++++--- 5 files changed, 231 insertions(+), 38 deletions(-) create mode 100644 src/renderer/utils/music/kw/album.js diff --git a/publish/changeLog.md b/publish/changeLog.md index c64a3d29..db2a6970 100644 --- a/publish/changeLog.md +++ b/publish/changeLog.md @@ -3,4 +3,4 @@ - 修复全局快捷键对桌面歌词无效的问题 - 修复快捷键设置框内的提示问题 - 修复在当前正常播放的列表中使用稍后播放功能时,播放完后稍后播放的歌曲后不会恢复原来播放位置播放的问题 - +- 修复kw部分歌单无法打开的问题 diff --git a/src/renderer/utils/music/kw/album.js b/src/renderer/utils/music/kw/album.js new file mode 100644 index 00000000..63e254cb --- /dev/null +++ b/src/renderer/utils/music/kw/album.js @@ -0,0 +1,101 @@ +import { httpFetch } from '../../request' +import { decodeName } from '../../index' +import { formatSinger, objStr2JSON } from './util' + +// let requestObj_list +let requestObj_listDetail +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) => { + let formats = item.formats.split('|') + let types = [] + let _types = {} + if (formats.includes('MP3128')) { + types.push({ type: '128k', size: null }) + _types['128k'] = { + size: null, + } + } + // if (formats.includes('MP3192')) { + // types.push({ type: '192k', size: null }) + // _types['192k'] = { + // size: null, + // } + // } + if (formats.includes('MP3H')) { + types.push({ type: '320k', size: null }) + _types['320k'] = { + size: null, + } + } + // if (formats.includes('AL')) { + // types.push({ type: 'ape', size: null }) + // _types.ape = { + // size: null, + // } + // } + if (formats.includes('ALFLAC')) { + types.push({ type: 'flac', size: null }) + _types.flac = { + size: null, + } + } + // types.reverse() + return { + singer: formatSinger(decodeName(item.artist)), + name: decodeName(item.name), + albumName, + albumId, + songmid: item.id, + source: 'kw', + interval: null, + img: item.pic, + lrc: null, + otherSource: null, + types, + _types, + typeUrl: {}, + } + }) + }, + /** + * 格式化播放数量 + * @param {*} num + */ + formatPlayCount(num) { + if (num > 100000000) return parseInt(num / 10000000) / 10 + '亿' + if (num > 10000) return parseInt(num / 1000) / 10 + '万' + return num + }, + getAlbumListDetail(id, page, retryNum = 0) { + if (requestObj_listDetail) { + requestObj_listDetail.cancelHttp() + } + if (retryNum > 2) return Promise.reject(new Error('try max num')) + requestObj_listDetail = httpFetch(`http://search.kuwo.cn/r.s?pn=${page - 1}&rn=${this.limit_song}&stype=albuminfo&albumid=${id}&show_copyright_off=0&encoding=utf&vipver=MUSIC_9.1.0`) + 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) + return { + list: this.filterListDetail(body.musiclist, body.name, body.albumid), + page, + limit: this.limit_song, + total: parseInt(body.songnum), + source: 'kw', + info: { + name: body.name, + img: body.img || body.hts_img, + desc: body.info, + author: body.artist, + // play_count: this.formatPlayCount(body.playnum), + }, + } + }) + }, +} diff --git a/src/renderer/utils/music/kw/songList.js b/src/renderer/utils/music/kw/songList.js index ce8f8796..c8936d19 100644 --- a/src/renderer/utils/music/kw/songList.js +++ b/src/renderer/utils/music/kw/songList.js @@ -1,6 +1,7 @@ import { httpFetch } from '../../request' import { formatPlayTime, decodeName } from '../../index' import { formatSinger } from './util' +import album from './album' export default { _requestObj_tags: null, @@ -133,7 +134,7 @@ export default { filterList(rawData) { return rawData.map(item => ({ play_count: this.formatPlayCount(item.listencnt), - id: item.id, + id: `digest-${item.digest}__${item.id}`, author: item.uname, name: item.name, // time: item.publish_time, @@ -144,32 +145,31 @@ export default { })) }, filterList2(rawData) { + // console.log(rawData) const list = [] rawData.forEach(item => { if (!item.label) return list.push(...item.list.map(item => ({ - play_count: item.play_count === undefined ? null : this.formatPlayCount(item.listencnt), - id: item.id, + play_count: item.play_count && this.formatPlayCount(item.listencnt), + id: `digest-${item.digest}__${item.id}`, author: item.uname, name: item.name, // time: item.publish_time, img: item.img, - grade: item.favorcnt / 10, + grade: item.favorcnt && item.favorcnt / 10, desc: item.desc, + source: 'kw', }))) }) return list }, - // 获取歌曲列表内的音乐 - getListDetail(id, page, tryNum = 0) { + getListDetailDigest8(id, page, tryNum = 0) { if (this._requestObj_listDetail) { this._requestObj_listDetail.cancelHttp() } if (tryNum > 2) return Promise.reject(new Error('try max num')) - if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1') - this._requestObj_listDetail = httpFetch(this.getListDetailUrl(id, page)) return this._requestObj_listDetail.promise.then(({ body }) => { if (body.result !== 'ok') return this.getListDetail(id, page, ++tryNum) @@ -189,6 +189,66 @@ export default { } }) }, + getListDetailDigest5Info(id, tryNum = 0) { + if (this._requestObj_listDetail) { + this._requestObj_listDetail.cancelHttp() + } + if (tryNum > 2) return Promise.reject(new Error('try max num')) + this._requestObj_listDetail = httpFetch(`http://qukudata.kuwo.cn/q.k?op=query&cont=ninfo&node=${id}&pn=0&rn=1&fmt=json&src=mbox&level=2`) + return this._requestObj_listDetail.promise.then(({ statusCode, body }) => { + if (statusCode != 200 || !body.child) return this.getListDetail(id, ++tryNum) + // console.log(body) + return body.child.length ? body.child[0].sourceid : null + }) + }, + getListDetailDigest5Music(id, page, tryNum = 0) { + if (this._requestObj_listDetail) { + this._requestObj_listDetail.cancelHttp() + } + if (tryNum > 2) return Promise.reject(new Error('try max num')) + this._requestObj_listDetail = 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 this._requestObj_listDetail.promise.then(({ body }) => { + // console.log(body) + if (body.result !== 'ok') return this.getListDetail(id, page, ++tryNum) + return { + list: this.filterListDetail(body.musiclist), + page, + limit: body.rn, + total: body.total, + source: 'kw', + info: { + name: body.title, + img: body.pic, + desc: body.info, + author: body.uname, + play_count: this.formatPlayCount(body.playnum), + }, + } + }) + }, + async getListDetailDigest5(id, page) { + const detailId = await this.getListDetailDigest5Info(id) + return this.getListDetailDigest5Music(detailId, page) + }, + + // 获取歌曲列表内的音乐 + getListDetail(id, page) { + // console.log(id) + if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1') + else if (/^digest-/.test(id)) { + let [digest, _id] = id.split('__') + digest = digest.replace('digest-', '') + id = _id + switch (digest) { + case '8': + break + case '13': return album.getAlbumListDetail(id, page) + case '5': + default: return this.getListDetailDigest5(id, page) + } + } + return this.getListDetailDigest8(id, page) + }, filterListDetail(rawData) { // console.log(rawData) return rawData.map(item => { diff --git a/src/renderer/utils/music/kw/tempSearch.js b/src/renderer/utils/music/kw/tempSearch.js index df9efe36..f1f958f3 100644 --- a/src/renderer/utils/music/kw/tempSearch.js +++ b/src/renderer/utils/music/kw/tempSearch.js @@ -1,6 +1,6 @@ -import { httpFetch } from '../../request' +// import { httpFetch } from '../../request' import { decodeName } from '../../index' -import { getToken, matchToken } from './util' +import { tokenRequest } from './util' export default { @@ -8,18 +8,11 @@ export default { relWord: /RELWORD=(.+)/, }, requestObj: null, - tempSearch(str, token) { + async tempSearch(str) { this.cancelTempSearch() - this.requestObj = httpFetch(`http://www.kuwo.cn/api/www/search/searchKey?key=${encodeURIComponent(str)}`, { - headers: { - Referer: 'http://www.kuwo.cn/', - csrf: token, - cookie: 'kw_token=' + token, - }, - }) - return this.requestObj.promise.then(({ statusCode, body, headers }) => { - if (statusCode != 200) return Promise.reject(new Error('请求失败')) - window.kw_token.token = matchToken(headers) + this.requestObj = await tokenRequest(`http://www.kuwo.cn/api/www/search/searchKey?key=${encodeURIComponent(str)}`) + return this.requestObj.promise.then(({ body }) => { + // console.log(body) if (body.code !== 200) return Promise.reject(new Error('请求失败')) return body }) @@ -34,8 +27,6 @@ export default { if (this.requestObj && this.requestObj.cancelHttp) this.requestObj.cancelHttp() }, async search(str) { - let token = window.kw_token.token - if (!token) token = await getToken() - return this.tempSearch(str, token).then(result => this.handleResult(result.data)) + return this.tempSearch(str).then(result => this.handleResult(result.data)) }, } diff --git a/src/renderer/utils/music/kw/util.js b/src/renderer/utils/music/kw/util.js index 068f4236..6f283dcf 100644 --- a/src/renderer/utils/music/kw/util.js +++ b/src/renderer/utils/music/kw/util.js @@ -1,11 +1,29 @@ -import { httpGet } from '../../request' +import { httpGet, httpFetch } from '../../request' import { rendererInvoke, NAMES } from '../../../../common/ipc' -if (!window.kw_token) { - window.kw_token = { - token: null, - isGetingToken: false, - } +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 formatSinger = rawData => rawData.replace(/&/g, '、') @@ -21,17 +39,40 @@ export const matchToken = headers => { const wait = time => new Promise(resolve => setTimeout(() => resolve(), time)) -export const getToken = () => new Promise((resolve, reject) => { - if (window.kw_token.isGetingToken) return wait(1000).then(() => getToken().then(token => resolve(token))) - if (window.kw_token.token) return resolve(window.kw_token.token) - window.kw_token.isGetingToken = true +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) => { - window.kw_token.isGetingToken = false - if (err) return reject(err) + kw_token.isGetingToken = false + if (err) return getToken(retryNum) if (resp.statusCode != 200) return reject(new Error('获取失败')) - const token = window.kw_token.token = matchToken(resp.headers) + const token = kw_token.token = matchToken(resp.headers) resolve(token) }) }) export const decodeLyric = base64Data => rendererInvoke(NAMES.mainWindow.handle_kw_decode_lyric, base64Data) + +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 +}