更新kw源API

This commit is contained in:
Folltoshe 2023-07-10 00:36:38 +08:00
parent 6423066cb2
commit fc97c76be5
10 changed files with 219 additions and 153 deletions

View File

@ -1,6 +1,6 @@
import { httpFetch } from '../../request'
import { decodeName } from '../../index'
import { formatSinger, objStr2JSON } from './util'
import { formatSinger, objStr2JSON } from './utils'
// let requestObj_list
export default {

View File

@ -1,7 +1,7 @@
import { httpFetch } from '../../request'
import tipSearch from './tipSearch'
import musicSearch from './musicSearch'
import { formatSinger } from './util'
import { formatSinger } from './utils'
import leaderboard from './leaderboard'
import lyric from './lyric'
import pic from './pic'

View File

@ -1,6 +1,8 @@
import { httpFetch } from '../../request'
import { formatPlayTime, decodeName } from '../../index'
import { formatSinger } from './util'
import { sortQualityArray } from '../utils'
import { formatSinger } from './utils'
import { wbdCrypto } from './utils/crypto'
const boardList = [{ id: 'kw__93', name: '飙升榜', bangid: '93' }, { id: 'kw__17', name: '新歌榜', bangid: '17' }, { id: 'kw__16', name: '热歌榜', bangid: '16' }, { id: 'kw__158', name: '抖音热歌榜', bangid: '158' }, { id: 'kw__292', name: '铃声榜', bangid: '292' }, { id: 'kw__284', name: '热评榜', bangid: '284' }, { id: 'kw__290', name: 'ACG新歌榜', bangid: '290' }, { id: 'kw__286', name: '台湾KKBOX榜', bangid: '286' }, { id: 'kw__279', name: '冬日暖心榜', bangid: '279' }, { id: 'kw__281', name: '巴士随身听榜', bangid: '281' }, { id: 'kw__255', name: 'KTV点唱榜', bangid: '255' }, { id: 'kw__280', name: '家务进行曲榜', bangid: '280' }, { id: 'kw__282', name: '熬夜修仙榜', bangid: '282' }, { id: 'kw__283', name: '枕边轻音乐榜', bangid: '283' }, { id: 'kw__278', name: '古风音乐榜', bangid: '278' }, { id: 'kw__264', name: 'Vlog音乐榜', bangid: '264' }, { id: 'kw__242', name: '电音榜', bangid: '242' }, { id: 'kw__187', name: '流行趋势榜', bangid: '187' }, { id: 'kw__204', name: '现场音乐榜', bangid: '204' }, { id: 'kw__186', name: 'ACG神曲榜', bangid: '186' }, { id: 'kw__185', name: '最强翻唱榜', bangid: '185' }, { id: 'kw__26', name: '经典怀旧榜', bangid: '26' }, { id: 'kw__104', name: '华语榜', bangid: '104' }, { id: 'kw__182', name: '粤语榜', bangid: '182' }, { id: 'kw__22', name: '欧美榜', bangid: '22' }, { id: 'kw__184', name: '韩语榜', bangid: '184' }, { id: 'kw__183', name: '日语榜', bangid: '183' }, { id: 'kw__145', name: '会员畅听榜', bangid: '145' }, { id: 'kw__153', name: '网红新歌榜', bangid: '153' }, { id: 'kw__64', name: '影视金曲榜', bangid: '64' }, { id: 'kw__176', name: 'DJ嗨歌榜', bangid: '176' }, { id: 'kw__106', name: '真声音', bangid: '106' }, { id: 'kw__12', name: 'Billboard榜', bangid: '12' }, { id: 'kw__49', name: 'iTunes音乐榜', bangid: '49' }, { id: 'kw__180', name: 'beatport电音榜', bangid: '180' }, { id: 'kw__13', name: '英国UK榜', bangid: '13' }, { id: 'kw__164', name: '百大DJ榜', bangid: '164' }, { id: 'kw__246', name: 'YouTube音乐排行榜', bangid: '246' }, { id: 'kw__265', name: '韩国Genie榜', bangid: '265' }, { id: 'kw__14', name: '韩国M-net榜', bangid: '14' }, { id: 'kw__8', name: '香港电台榜', bangid: '8' }, { id: 'kw__15', name: '日本公信榜', bangid: '15' }, { id: 'kw__151', name: '腾讯音乐人原创榜', bangid: '151' }]
@ -62,9 +64,9 @@ export default {
bangid: 183,
},
],
getUrl: (p, l, id) => `http://kbangserver.kuwo.cn/ksong.s?from=pc&fmt=json&pn=${p - 1}&rn=${l}&type=bang&data=content&id=${id}&show_copyright_off=0&pcmp4=1&isbang=1`,
// getUrl: (p, l, id) => `http://kbangserver.kuwo.cn/ksong.s?from=pc&fmt=json&pn=${p - 1}&rn=${l}&type=bang&data=content&id=${id}&show_copyright_off=0&pcmp4=1&isbang=1`,
regExps: {
mInfo: /level:(\w+),bitrate:(\d+),format:(\w+),size:([\w.]+)/,
},
limit: 100,
_requestBoardsObj: null,
@ -74,62 +76,51 @@ export default {
this._requestBoardsObj = httpFetch('http://qukudata.kuwo.cn/q.k?op=query&cont=tree&node=2&pn=0&rn=1000&fmt=json&level=2')
return this._requestBoardsObj.promise
},
getData(url) {
const requestDataObj = httpFetch(url)
return requestDataObj.promise
},
filterData(rawList) {
// console.log(rawList)
// console.log(rawList.length, rawList2.length)
return rawList.map((item, inedx) => {
let formats = item.formats.split('|')
return rawList.map(item => {
let types = []
let _types = {}
if (formats.includes('MP3128')) {
types.push({ type: '128k', size: null })
_types['128k'] = {
size: null,
const _types = {}
const qualitys = new Set()
item.n_minfo.split(';').forEach(i => {
const info = i.match(this.regExps.mInfo)
if (!info) return
const quality = info[2]
const size = info[4].toLocaleUpperCase()
if (qualitys.has(quality)) return
qualitys.add(quality)
switch (quality) {
case '4000':
types.push({ type: 'flac24bit', size })
_types.flac24bit = { size }
break
case '2000':
types.push({ type: 'flac', size })
_types.flac = { size }
break
case '320':
types.push({ type: '320k', size })
_types['320k'] = { size }
break
case '128':
types.push({ type: '128k', size })
_types['128k'] = { size }
break
}
}
// 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,
}
}
if (formats.includes('HIRFLAC')) {
types.push({ type: 'flac24bit', size: null })
_types.flac24bit = {
size: null,
}
}
// types.reverse()
})
types = sortQualityArray(types)
return {
singer: formatSinger(decodeName(item.artist)),
name: decodeName(item.name),
albumName: decodeName(item.album),
albumId: item.albumid,
albumId: item.albumId,
songmid: item.id,
source: 'kw',
interval: formatPlayTime(parseInt(item.song_duration)),
interval: formatPlayTime(parseInt(item.duration)),
img: item.pic,
lrc: null,
otherSource: null,
@ -180,12 +171,19 @@ export default {
getList(id, page, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
return this.getData(this.getUrl(page, this.limit, id)).then(({ statusCode, body }) => {
// console.log(body)
if (statusCode !== 200 || !body.musiclist) return this.getList(id, page, retryNum)
// console.log(data1.musiclist, data2.data)
let total = parseInt(body.num)
let list = this.filterData(body.musiclist)
const requestBody = { uid: '', devId: '', sFrom: 'kuwo_sdk', user_type: 'AP', carSource: 'kwplayercar_ar_6.0.1.0_apk_keluze.apk', id, pn: page - 1, rn: this.limit }
const requestUrl = `https://wbd.kuwo.cn/api/bd/bang/bang_info?${wbdCrypto.buildParam(requestBody)}`
const request = httpFetch(requestUrl).promise
return request.then(({ statusCode, body }) => {
const rawData = wbdCrypto.decodeData(body)
const data = rawData.data
if (statusCode !== 200 || rawData.code != 200 || !data.musiclist) return this.getList(id, page, retryNum)
const total = parseInt(data.total)
const list = this.filterData(data.musiclist)
return {
total,
list,

View File

@ -1,5 +1,5 @@
import { httpFetch } from '../../request'
import { decodeLyric, lrcTools } from './util'
import lyricTools from './utils/lrc'
import { decodeName } from '../../index'
/*
@ -198,7 +198,7 @@ export default {
time,
text,
})
} else if (lrcTools.rxps.tagLine.test(line)) {
} else if (lyricTools.rxps.tagLine.test(line)) {
tags.push(line)
}
}
@ -212,7 +212,7 @@ export default {
// 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 => {
// return lyricTools.decodeLyric({ lrcBase64: raw.toString('base64'), isGetLyricx }).then(base64Data => {
// let lrcInfo
// console.log(Buffer.from(base64Data, 'base64').toString())
// try {
@ -220,8 +220,8 @@ export default {
// } 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)
// if (lrcInfo.tlyric) lrcInfo.tlyric = lrcInfo.tlyric.replace(lyricTools.rxps.wordTimeAll, '')
// lrcInfo.lxlyric = lyricTools.parse(lrcInfo.lyric)
// // console.log(lrcInfo.lyric)
// // console.log(lrcInfo.tlyric)
// // console.log(lrcInfo.lxlyric)
@ -235,7 +235,7 @@ export default {
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 => {
return lyricTools.decodeLyric({ lrcBase64: raw.toString('base64'), isGetLyricx }).then(base64Data => {
// let lrcInfo
// try {
// lrcInfo = this.parseLrc(Buffer.from(base64Data, 'base64').toString())
@ -250,13 +250,13 @@ export default {
return Promise.reject(new Error('Get lyric failed'))
}
// console.log(lrcInfo)
if (lrcInfo.tlyric) lrcInfo.tlyric = lrcInfo.tlyric.replace(lrcTools.rxps.wordTimeAll, '')
if (lrcInfo.tlyric) lrcInfo.tlyric = lrcInfo.tlyric.replace(lyricTools.rxps.wordTimeAll, '')
try {
lrcInfo.lxlyric = lrcTools.parse(lrcInfo.lyric)
lrcInfo.lxlyric = lyricTools.parse(lrcInfo.lyric)
} catch {
lrcInfo.lxlyric = ''
}
lrcInfo.lyric = lrcInfo.lyric.replace(lrcTools.rxps.wordTimeAll, '')
lrcInfo.lyric = lrcInfo.lyric.replace(lyricTools.rxps.wordTimeAll, '')
if (!existTimeExp.test(lrcInfo.lyric)) return Promise.reject(new Error('Get lyric failed'))
// console.log(lrcInfo)
return lrcInfo

View File

@ -3,7 +3,7 @@
import { httpFetch } from '../../request'
import { formatPlayTime, decodeName } from '../../index'
// import { debug } from '../../utils/env'
import { formatSinger } from './util'
import { formatSinger } from './utils'
export default {
regExps: {

View File

@ -1,6 +1,6 @@
import { httpFetch } from '../../request'
import { formatPlayTime, decodeName } from '../../index'
import { formatSinger, objStr2JSON } from './util'
import { formatSinger, objStr2JSON } from './utils'
import album from './album'
export default {

View File

@ -0,0 +1,48 @@
import { createCipheriv, createDecipheriv, createHash } from 'crypto'
const createAesEncrypt = (buffer, mode, key, iv) => {
const cipher = createCipheriv(mode, key, iv)
return Buffer.concat([cipher.update(buffer), cipher.final()])
}
const createAesDecrypt = (buffer, mode, key, iv) => {
const cipher = createDecipheriv(mode, key, iv)
return Buffer.concat([cipher.update(buffer), cipher.final()])
}
const createMD5 = str => createHash('md5').update(str).digest('hex')
const strToUint8Array = str => {
const length = Math.floor(str.length / 2)
const bArr = new Uint8Array(length)
for (let i = 0; i < length; i++) {
const i2 = i * 2
bArr[i] = parseInt(str.substring(i2, i2 + 2), 16)
}
return bArr
}
export const wbdCrypto = {
aesMode: 'aes-128-ecb',
aesKey: strToUint8Array('7057273DC7FA29BF39442D72DD5E8CE4'),
aesIv: '',
appId: 'y67sprxhhpws',
decodeData(base64Result) {
const data = Buffer.from(decodeURIComponent(base64Result), 'base64')
return JSON.parse(createAesDecrypt(data, this.aesMode, this.aesKey, this.aesIv).toString())
},
createSign(data, time) {
const str = `${this.appId}${data}${time}`
return createMD5(str).toUpperCase()
},
buildParam(jsonData) {
const data = Buffer.from(JSON.stringify(jsonData))
const time = Date.now()
const encodeData = createAesEncrypt(data, this.aesMode, this.aesKey, this.aesIv).toString('base64')
const sign = this.createSign(encodeData, time)
return `data=${encodeURIComponent(encodeData)}&time=${time}&appId=${this.appId}&sign=${sign}`
},
}

View File

@ -0,0 +1,73 @@
export const objStr2JSON = str => JSON.parse(str.replace(/('(?=(,\s*')))|('(?=:))|((?<=([:,]\s*))')|((?<={)')|('(?=}))/g, '"'))
export const formatSinger = rawData => rawData.replace(/&/g, '、')
export const matchToken = headers => {
try {
return headers['set-cookie'][0].match(/kw_token=(\w+)/)[1]
} catch (err) {
return null
}
}
// const kw_token = {
// token: null,
// isGetingToken: false,
// }
// export const getToken = (retryNum = 0) => new Promise((resolve, reject) => {
// if (retryNum > 2) return Promise.reject(new Error('try max num'))
// if (kw_token.isGetingToken) return wait(1000).then(() => getToken(retryNum).then(token => resolve(token)))
// if (kw_token.token) return resolve(kw_token.token)
// kw_token.isGetingToken = true
// httpGet('http://www.kuwo.cn/', (err, resp) => {
// kw_token.isGetingToken = false
// if (err) return getToken(++retryNum)
// if (resp.statusCode != 200) return reject(new Error('获取失败'))
// const token = kw_token.token = matchToken(resp.headers)
// resolve(token)
// })
// })
// export const 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
// }
// const translationMap = {
// "{'": '{"',
// "'}\n": '"}',
// "'}": '"}',
// "':'": '":"',
// "','": '","',
// "':{'": '":{"',
// "':['": '":["',
// "'}],'": '"}],"',
// "':[{'": '":[{"',
// "'},'": '"},"',
// "'},{'": '"},{"',
// "':[],'": '":[],"',
// "':{},'": '":{},"',
// "'}]}": '"}]}',
// }
// export const objStr2JSON = str => {
// return JSON.parse(str.replace(/(^{'|'}\n$|'}$|':'|','|':\[{'|'}\],'|':{'|'},'|'},{'|':\['|':\[\],'|':{},'|'}]})/g, s => translationMap[s]))
// }

View File

@ -1,90 +1,7 @@
// import { httpGet, httpFetch } from '../../request'
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
import { rendererInvoke } from '@common/rendererIpc'
// const kw_token = {
// token: null,
// isGetingToken: false,
// }
// const translationMap = {
// "{'": '{"',
// "'}\n": '"}',
// "'}": '"}',
// "':'": '":"',
// "','": '","',
// "':{'": '":{"',
// "':['": '":["',
// "'}],'": '"}],"',
// "':[{'": '":[{"',
// "'},'": '"},"',
// "'},{'": '"},{"',
// "':[],'": '":[],"',
// "':{},'": '":{},"',
// "'}]}": '"}]}',
// }
// export const objStr2JSON = str => {
// return JSON.parse(str.replace(/(^{'|'}\n$|'}$|':'|','|':\[{'|'}\],'|':{'|'},'|'},{'|':\['|':\[\],'|':{},'|'}]})/g, s => translationMap[s]))
// }
export const objStr2JSON = str => {
return JSON.parse(str.replace(/('(?=(,\s*')))|('(?=:))|((?<=([:,]\s*))')|((?<={)')|('(?=}))/g, '"'))
}
export const formatSinger = rawData => rawData.replace(/&/g, '、')
export const matchToken = headers => {
try {
return headers['set-cookie'][0].match(/kw_token=(\w+)/)[1]
} catch (err) {
return null
}
}
// const wait = time => new Promise(resolve => setTimeout(() => resolve(), time))
// export const getToken = (retryNum = 0) => new Promise((resolve, reject) => {
// if (retryNum > 2) return Promise.reject(new Error('try max num'))
// if (kw_token.isGetingToken) return wait(1000).then(() => getToken(retryNum).then(token => resolve(token)))
// if (kw_token.token) return resolve(kw_token.token)
// kw_token.isGetingToken = true
// httpGet('http://www.kuwo.cn/', (err, resp) => {
// kw_token.isGetingToken = false
// if (err) return getToken(++retryNum)
// if (resp.statusCode != 200) return reject(new Error('获取失败'))
// const token = kw_token.token = matchToken(resp.headers)
// resolve(token)
// })
// })
export const decodeLyric = base64Data => rendererInvoke(WIN_MAIN_RENDERER_EVENT_NAME.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
// }
export const lrcTools = {
export default {
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*\]/,
@ -96,6 +13,7 @@ export const lrcTools = {
isOK: false,
lines: [],
tags: [],
decodeLyric: base64Data => rendererInvoke(WIN_MAIN_RENDERER_EVENT_NAME.handle_kw_decode_lyric, base64Data),
getWordInfo(str, str2, prevWord) {
const offset = parseInt(str)
const offset2 = parseInt(str2)

View File

@ -48,3 +48,32 @@ export const formatSingerName = (singers, nameKey = 'name', join = '、') => {
}
return decodeName(String(singers ?? ''))
}
/**
* 音质排序
* @param {*} array
*/
export const sortQualityArray = array => {
const qualityMap = {
flac24bit: 4,
flac: 3,
'320k': 2,
'128k': 1,
}
const rawQualityArray = []
const newQualityArray = []
array.forEach((item, index) => {
const type = qualityMap[item.type]
if (!type) return
rawQualityArray.push({ type, index })
})
rawQualityArray.sort((a, b) => a.type - b.type)
rawQualityArray.forEach(item => {
newQualityArray.push(array[item.index])
})
return newQualityArray
}