From 4c18f8ab7131dd6f4e615dfbc069f49d996af96b Mon Sep 17 00:00:00 2001 From: lyswhut Date: Sat, 29 Apr 2023 15:04:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/layout/Toolbar/SearchInput.vue | 4 +- src/renderer/utils/index.ts | 8 +- src/renderer/utils/musicSdk/kg/album.js | 4 +- src/renderer/utils/musicSdk/kg/index.js | 4 +- src/renderer/utils/musicSdk/kg/musicSearch.js | 133 ++- src/renderer/utils/musicSdk/kg/songList.js | 811 ++++++++++------- .../utils/musicSdk/kg/temp/musicSearch-new.js | 5 +- .../temp/{songList-old.js => songList-new.js} | 838 ++++++++---------- src/renderer/utils/musicSdk/kg/util.js | 28 +- src/renderer/utils/musicSdk/mg/album.js | 6 +- src/renderer/utils/musicSdk/mg/index.js | 4 +- src/renderer/utils/musicSdk/mg/musicInfo.js | 10 +- src/renderer/utils/musicSdk/mg/musicSearch.js | 264 ++++-- src/renderer/utils/musicSdk/tx/index.js | 4 +- src/renderer/utils/musicSdk/tx/tipSearch.js | 20 +- src/renderer/utils/musicSdk/utils.js | 27 +- src/renderer/utils/musicSdk/wy/index.js | 4 +- 17 files changed, 1126 insertions(+), 1048 deletions(-) rename src/renderer/utils/musicSdk/kg/temp/{songList-old.js => songList-new.js} (51%) diff --git a/src/renderer/components/layout/Toolbar/SearchInput.vue b/src/renderer/components/layout/Toolbar/SearchInput.vue index 24da7200..ac2ef4e7 100644 --- a/src/renderer/components/layout/Toolbar/SearchInput.vue +++ b/src/renderer/components/layout/Toolbar/SearchInput.vue @@ -48,12 +48,12 @@ export default { const tipSearch = debounce(async() => { if (searchText.value === '' && prevTempSearchSource) { tipList.value = [] - music[prevTempSearchSource].tempSearch.cancelTempSearch() + music[prevTempSearchSource].tipSearch.cancelTipSearch() return } const { temp_source } = await getSearchSetting() prevTempSearchSource = temp_source - music[prevTempSearchSource].tempSearch.search(searchText.value).then(list => { + music[prevTempSearchSource].tipSearch.search(searchText.value).then(list => { tipList.value = list }).catch(() => {}) }, 50) diff --git a/src/renderer/utils/index.ts b/src/renderer/utils/index.ts index 4332dc18..fe4adf76 100644 --- a/src/renderer/utils/index.ts +++ b/src/renderer/utils/index.ts @@ -9,10 +9,10 @@ export * from '@common/utils/tools' * 格式化播放数量 * @param {*} num 数字 */ -export const formatPlayCount = (num: number): { count: number, lang: string } => { - if (num > 100000000) return {count: parseInt((num / 10000000).toString()) / 10, lang: 'play_count_billion'} - if (num > 10000) return {count: parseInt((num / 1000).toString()) / 10, lang: 'play_count_million'} - return { count: num, lang: 'play_count_none' } +export const formatPlayCount = (num: number): string => { + if (num > 100000000) return `${Math.trunc(num / 10000000) / 10}亿` + if (num > 10000) return `${Math.trunc(num / 1000) / 10}万` + return String(num) } diff --git a/src/renderer/utils/musicSdk/kg/album.js b/src/renderer/utils/musicSdk/kg/album.js index 2250c08e..1d9642f8 100644 --- a/src/renderer/utils/musicSdk/kg/album.js +++ b/src/renderer/utils/musicSdk/kg/album.js @@ -38,13 +38,13 @@ export default { * @param {*} page */ async getAlbumDetail(id, page = 1, limit = 200) { - const info = await this.getAlbumInfo(id) - const albumList = await createHttpFetch(`http://mobiles.kugou.com/api/v3/album/song?version=9108&albumid=${id}&plat=0&pagesize=${limit}&area_code=0&page=${page}&with_res_tag=0`) if (!albumList.info) return Promise.reject(new Error('Get album list failed.')) let result = await getMusicInfosByList(albumList.info) + const info = await this.getAlbumInfo(id) + return { list: result || [], page, diff --git a/src/renderer/utils/musicSdk/kg/index.js b/src/renderer/utils/musicSdk/kg/index.js index 142dd9b1..5d41846c 100644 --- a/src/renderer/utils/musicSdk/kg/index.js +++ b/src/renderer/utils/musicSdk/kg/index.js @@ -6,10 +6,10 @@ import pic from './pic' import lyric from './lyric' import hotSearch from './hotSearch' import comment from './comment' -import tipSearch from './tipSearch' +// import tipSearch from './tipSearch' const kg = { - tipSearch, + // tipSearch, leaderboard, songList, musicSearch, diff --git a/src/renderer/utils/musicSdk/kg/musicSearch.js b/src/renderer/utils/musicSdk/kg/musicSearch.js index 2e62267d..ee60a694 100644 --- a/src/renderer/utils/musicSdk/kg/musicSearch.js +++ b/src/renderer/utils/musicSdk/kg/musicSearch.js @@ -1,5 +1,6 @@ import { httpFetch } from '../../request' import { decodeName, formatPlayTime, sizeFormate } from '../../index' +import { formatSingerName } from '@renderer/utils/musicSdk/utils' export default { limit: 30, @@ -10,80 +11,76 @@ export default { const searchRequest = httpFetch(`https://songsearch.kugou.com/song_search_v2?keyword=${encodeURIComponent(str)}&page=${page}&pagesize=${limit}&userid=0&clientver=&platform=WebFilter&filter=2&iscorrection=1&privilege_filter=0`) return searchRequest.promise.then(({ body }) => body) }, - filterList(raw) { + filterData(rawData) { + const types = [] + const _types = {} + if (rawData.FileSize !== 0) { + let size = sizeFormate(rawData.FileSize) + types.push({ type: '128k', size, hash: rawData.FileHash }) + _types['128k'] = { + size, + hash: rawData.FileHash, + } + } + if (rawData.HQFileSize !== 0) { + let size = sizeFormate(rawData.HQFileSize) + types.push({ type: '320k', size, hash: rawData.HQFileHash }) + _types['320k'] = { + size, + hash: rawData.HQFileHash, + } + } + if (rawData.SQFileSize !== 0) { + let size = sizeFormate(rawData.SQFileSize) + types.push({ type: 'flac', size, hash: rawData.SQFileHash }) + _types.flac = { + size, + hash: rawData.SQFileHash, + } + } + if (rawData.ResFileSize !== 0) { + let size = sizeFormate(rawData.ResFileSize) + types.push({ type: 'flac24bit', size, hash: rawData.ResFileHash }) + _types.flac24bit = { + size, + hash: rawData.ResFileHash, + } + } + return { + singer: decodeName(formatSingerName(rawData.Singers, 'name')), + name: decodeName(rawData.SongName), + albumName: decodeName(rawData.AlbumName), + albumId: rawData.AlbumID, + songmid: rawData.Audioid, + source: 'kg', + interval: formatPlayTime(rawData.Duration), + _interval: rawData.Duration, + img: null, + lrc: null, + otherSource: null, + hash: rawData.FileHash, + types, + _types, + typeUrl: {}, + } + }, + handleResult(rawData) { let ids = new Set() const list = [] - - raw.forEach(item => { - if (ids.has(item.Audioid) || !item.Audioid) return - ids.add(item.Audioid) - - const types = [] - const _types = {} - if (item.FileSize !== 0) { - let size = sizeFormate(item.FileSize) - types.push({ type: '128k', size, hash: item.FileHash }) - _types['128k'] = { - size, - hash: item.FileHash, - } + rawData.forEach(item => { + const key = item.Audioid + item.FileHash + if (ids.has(key)) return + ids.add(key) + list.push(this.filterData(item)) + for (const childItem of item.Grp) { + const key = item.Audioid + item.FileHash + if (ids.has(key)) continue + ids.add(key) + list.push(this.filterData(childItem)) } - if (item.HQFileSize !== 0) { - let size = sizeFormate(item.HQFileSize) - types.push({ type: '320k', size, hash: item.HQFileHash }) - _types['320k'] = { - size, - hash: item.HQFileHash, - } - } - if (item.SQFileSize !== 0) { - let size = sizeFormate(item.SQFileSize) - types.push({ type: 'flac', size, hash: item.SQFileHash }) - _types.flac = { - size, - hash: item.SQFileHash, - } - } - if (item.ResFileSize !== 0) { - let size = sizeFormate(item.ResFileSize) - types.push({ type: 'flac24bit', size, hash: item.ResFileHash }) - _types.flac24bit = { - size, - hash: item.ResFileHash, - } - } - list.push({ - singer: decodeName(item.SingerName), - name: decodeName(item.SongName), - albumName: decodeName(item.AlbumName), - albumId: item.AlbumID, - songmid: item.Audioid, - source: 'kg', - interval: formatPlayTime(item.Duration), - _interval: item.Duration, - img: null, - lrc: null, - otherSource: null, - hash: item.FileHash, - types, - _types, - typeUrl: {}, - }) }) return list }, - handleResult(raw) { - const handleList = [] - - raw.forEach(item => { - handleList.push(item) - if (item.Grp.length === 0) return - for (const e in item.Grp) { - handleList.push(e) - } - }) - return this.filterList(handleList) - }, search(str, page = 1, limit, retryNum = 0) { if (++retryNum > 3) return Promise.reject(new Error('try max num')) if (limit == null) limit = this.limit diff --git a/src/renderer/utils/musicSdk/kg/songList.js b/src/renderer/utils/musicSdk/kg/songList.js index 65dc7f05..f06163dd 100644 --- a/src/renderer/utils/musicSdk/kg/songList.js +++ b/src/renderer/utils/musicSdk/kg/songList.js @@ -1,9 +1,17 @@ import { httpFetch } from '../../request' -import { formatSingerName } from '../utils' import { decodeName, formatPlayTime, sizeFormate, dateFormat, formatPlayCount } from '../../index' -import { signatureParams, createHttpFetch } from './util' -import { getMusicInfosByList } from './musicInfo' -import album from './album' +import infSign from './vendors/infSign.min' +import { signatureParams } from './util' + +const handleSignature = (id, page, limit) => new Promise((resolve, reject) => { + infSign({ appid: 1058, type: 0, module: 'playlist', page, pagesize: limit, specialid: id }, null, { + useH5: !0, + isCDN: !0, + callback(i) { + resolve(i.signature) + }, + }) +}) export default { _requestObj_tags: null, @@ -38,59 +46,75 @@ export default { }, ], cache: new Map(), - CollectionIdListInfoCache: new Map(), regExps: { listData: /global\.data = (\[.+\]);/, listInfo: /global = {[\s\S]+?name: "(.+)"[\s\S]+?pic: "(.+)"[\s\S]+?};/, // https://www.kugou.com/yy/special/single/1067062.html listDetailLink: /^.+\/(\d+)\.html(?:\?.*|&.*$|#.*$|$)/, }, - - /** - * 获取歌曲列表内的音乐 - * @param {*} id - * @param {*} page - */ - async getListDetail(id, page) { - id = id.toString() - - if (id.includes('special/single/')) id = id.replace(this.regExps.listDetailLink, '$1') - // fix https://www.kugou.com/songlist/xxx/?uid=xxx&chl=qq_client&cover=http%3A%2F%2Fimge.kugou.com%xxx.jpg&iszlist=1 - if (/https?:/.test(id)) { - if (id.includes('#')) id = id.replace(/#.*$/, '') - if (id.includes('global_collection_id')) return this.getUserListDetailByCollectionId(id.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1'), page) - if (id.includes('chain=')) return this.getUserListDetail3(id.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'), page) - if (id.includes('.html')) { - if (id.includes('zlist.html')) { - id = id.replace(/^(.*)zlist\.html/, 'https://m3ws.kugou.com/zlist/list') - if (id.includes('pagesize')) { - id = id.replace('pagesize=30', 'pagesize=' + this.listDetailLimit).replace('page=1', 'page=' + page) - } else { - id += `&pagesize=${this.listDetailLimit}&page=${page}` - } - } else if (!id.includes('song.html')) return this.getUserListDetail3(id.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page) - } - return this.getUserListDetail(id.replace(/^.*?http/, 'http'), page) - } - if (/^\d+$/.test(id)) return this.getUserListDetailByCode(id, page) - if (id.startsWith('gid_')) return this.getUserListDetailByCollectionId(id.replace('gid_', ''), page) - if (id.startsWith('id_')) return this.getUserListDetailBySpecialId(id.replace('id_', ''), page) - - return new Error('Failed.') + // async getGlobalSpecialId(specialId) { + // return httpFetch(`http://mobilecdnbj.kugou.com/api/v5/special/info?specialid=${specialId}`, { + // headers: { + // 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; HLK-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Mobile Safari/537.36 EdgA/104.0.1293.70', + // }, + // }).promise.then(({ body }) => { + // // console.log(body) + // if (!body.data.global_specialid) Promise.reject(new Error('Failed to get global collection id.')) + // return body.data.global_specialid + // }) + // }, + // async getListInfoBySpecialId(special_id, retry = 0) { + // if (++retry > 2) throw new Error('failed') + // return httpFetch(`https://m.kugou.com/plist/list/${special_id}/?json=true`, { + // headers: { + // 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; HLK-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Mobile Safari/537.36 EdgA/104.0.1293.70', + // }, + // follow_max: 2, + // }).promise.then(({ body }) => { + // // console.log(body) + // if (!body.info.list) return this.getListInfoBySpecialId(special_id, retry) + // let listinfo = body.info.list + // return { + // listInfo: { + // name: listinfo.specialname, + // image: listinfo.imgurl.replace('{size}', '150'), + // intro: listinfo.intro, + // author: listinfo.nickname, + // playcount: listinfo.playcount, + // total: listinfo.songcount, + // }, + // globalSpecialId: listinfo.global_specialid, + // } + // }) + // }, + // async getSongListDetailByGlobalSpecialId(id, page, limit = 100, retry = 0) { + // if (++retry > 2) throw new Error('failed') + // console.log(id) + // const params = `specialid=0&need_sort=1&module=CloudMusic&clientver=11409&pagesize=${limit}&global_collection_id=${id}&userid=0&page=${page}&type=1&area_code=1&appid=1005` + // return httpFetch(`http://pubsongscdn.tx.kugou.com/v2/get_other_list_file?${params}&signature=${signatureParams(params)}`).promise.then(({ body }) => { + // // console.log(body) + // if (body.data?.info == null) return this.getSongListDetailByGlobalSpecialId(id, page, limit, retry) + // return body.data.info + // }) + // }, + parseHtmlDesc(html) { + const prefix = '
' + let index = html.indexOf(prefix) + if (index < 0) return null + const afterStr = html.substring(index + prefix.length) + index = afterStr.indexOf('
') + if (index < 0) return null + return decodeName(afterStr.substring(0, index)) }, - - /** - * 获取SpecialId歌单 - * @param {*} id - */ - async getUserListDetailBySpecialId(id, page, tryNum = 0) { + async getListDetailBySpecialId(id, page, tryNum = 0) { if (tryNum > 2) throw new Error('try max num') const { body } = await httpFetch(this.getSongListDetailUrl(id)).promise let listData = body.match(this.regExps.listData) let listInfo = body.match(this.regExps.listInfo) if (!listData) return this.getListDetailBySpecialId(id, page, ++tryNum) - let list = await getMusicInfosByList(JSON.parse(listData[1])) + let list = await this.getMusicInfos(JSON.parse(listData[1])) + // listData = this.filterData(JSON.parse(listData[1])) let name let pic if (listInfo) { @@ -114,48 +138,36 @@ export default { // play_count: formatPlayCount(body.result.listen_num), }, } - }, - parseHtmlDesc(html) { - const prefix = '
' - let index = html.indexOf(prefix) - if (index < 0) return null - const afterStr = html.substring(index + prefix.length) - index = afterStr.indexOf('
') - if (index < 0) return null - return decodeName(afterStr.substring(0, index)) - }, - /** - * 使用SpecialId获取CollectionId - * @param {*} specialId - */ - async getCollectionIdBySpecialId(specialId) { - return httpFetch(`http://mobilecdnbj.kugou.com/api/v5/special/info?specialid=${specialId}`, { - headers: { - 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; HLK-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Mobile Safari/537.36 EdgA/104.0.1293.70', - }, - }).promise.then(({ body }) => { - // console.log('getCollectionIdBySpecialId', body) - if (!body.data.global_specialid) return Promise.reject(new Error('Failed to get global collection id.')) - return body.data.global_specialid - }) - }, - - /** - * 获取歌单URL - * @param {*} sortId - * @param {*} tagId - * @param {*} page - */ - getSongListUrl(sortId, tagId, page) { - if (tagId == null) tagId = '' - return `http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_ajax=1&cdn=cdn&t=${sortId}&c=${tagId}&p=${page}` + // const globalSpecialId = await this.getGlobalSpecialId(id) + // const limit = 100 + // const listData = await this.getSongListDetailByGlobalSpecialId(globalSpecialId, page, limit) + // if (!Array.isArray(listData)) + // return this.getUserListDetail2(globalSpecialId) + // return { + // list: this.filterDatav9(listData), + // page, + // limit, + // total: listInfo.total, + // source: 'kg', + // info: { + // name: listInfo.name, + // img: listInfo.image, + // desc: listInfo.intro, + // author: listInfo.author, + // play_count: formatPlayCount(listInfo.playcount), + // }, + // } }, getInfoUrl(tagId) { return tagId ? `http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_smarty=1&cdn=cdn&t=5&c=${tagId}` : 'http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_smarty=1&' }, + getSongListUrl(sortId, tagId, page) { + if (tagId == null) tagId = '' + return `http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_ajax=1&cdn=cdn&t=${sortId}&c=${tagId}&p=${page}` + }, getSongListDetailUrl(id) { return `http://www2.kugou.kugou.com/yueku/v9/special/single/${id}-5-9999.html` }, @@ -173,7 +185,6 @@ export default { } return result }, - filterTagInfo(rawData) { const result = [] for (const name of Object.keys(rawData)) { @@ -190,20 +201,6 @@ export default { } return result }, - filterSongList(rawData) { - return rawData.map(item => ({ - play_count: item.total_play_count || formatPlayCount(item.play_count), - id: 'id_' + item.specialid, - author: item.nickname, - name: item.specialname, - time: dateFormat(item.publish_time || item.publishtime, 'Y-M-D'), - img: item.img || item.imgurl, - total: item.songcount, - grade: item.grade, - desc: item.intro, - source: 'kg', - })) - }, getSongList(sortId, tagId, page, tryNum = 0) { if (this._requestObj_list) this._requestObj_list.cancelHttp() @@ -213,7 +210,7 @@ export default { ) return this._requestObj_list.promise.then(({ body }) => { if (!body || body.status !== 1) return this.getSongList(sortId, tagId, page, ++tryNum) - return this.filterSongList(body.special_db) + return this.filterList(body.special_db) }) }, getSongListRecommend(tryNum = 0) { @@ -241,185 +238,88 @@ export default { ) return this._requestObj_listRecommend.promise.then(({ body }) => { if (body.status !== 1) return this.getSongListRecommend(++tryNum) - return this.filterSongList(body.data.special_list) + return this.filterList(body.data.special_list) }) }, + filterList(rawData) { + return rawData.map(item => ({ + play_count: item.total_play_count || formatPlayCount(item.play_count), + id: 'id_' + item.specialid, + author: item.nickname, + name: item.specialname, + time: dateFormat(item.publish_time || item.publishtime, 'Y-M-D'), + img: item.img || item.imgurl, + total: item.songcount, + grade: item.grade, + desc: item.intro, + source: 'kg', + })) + }, - /** - * 通过CollectionId获取歌单详情 - * @param {*} id - */ - async getUserListInfoByCollectionId(id) { - if (!id || id.length > 1000) return Promise.reject(new Error('get list error')) - if (this.CollectionIdListInfoCache.has(id)) return this.CollectionIdListInfoCache.get(id) + async createHttp(url, options, retryNum = 0) { + if (retryNum > 2) throw new Error('try max num') + let result + try { + result = await httpFetch(url, options).promise + } catch (err) { + console.log(err) + return this.createHttp(url, options, ++retryNum) + } + // console.log(result.statusCode, result.body) + if (result.statusCode !== 200 || + ( + (result.body.error_code !== undefined + ? result.body.error_code + : result.body.errcode !== undefined + ? result.body.errcode + : result.body.err_code + ) !== 0) + ) return this.createHttp(url, options, ++retryNum) + if (result.body.data) return result.body.data + if (Array.isArray(result.body.info)) return result.body + return result.body.info + }, - const params = `appid=1058&specialid=0&global_specialid=${id}&format=jsonp&srcappid=2919&clientver=20000&clienttime=1586163242519&mid=1586163242519&uuid=1586163242519&dfid=-` - return createHttpFetch(`https://mobiles.kugou.com/api/v5/special/info_v2?${params}&signature=${signatureParams(params, 5)}`, { + createTask(hashs) { + let data = { + appid: 1001, + clienttime: 639437935, + clientver: 9020, + fields: + 'album_info,author_name,audio_info,ori_audio_name', + is_publish: '1', + key: '0475af1457cd3363c7b45b871e94428a', + mid: '21511157a05844bd085308bc76ef3342', + show_privilege: 1, + } + let list = hashs + let tasks = [] + while (list.length) { + tasks.push(Object.assign({ data: list.slice(0, 100) }, data)) + if (list.length < 100) break + list = list.slice(100) + } + let url = 'http://kmr.service.kugou.com/v2/album_audio/audio' + return tasks.map(task => this.createHttp(url, { + method: 'POST', + body: task, headers: { - mid: '1586163242519', - Referer: 'https://m3ws.kugou.com/share/index.php', - 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1', - dfid: '-', - clienttime: '1586163242519', + 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1', }, - }).then(body => { - let info = { - type: body.type, - userName: body.nickname, - userAvatar: body.user_avatar, - imageUrl: body.imgurl, - desc: body.intro, - name: body.specialname, - globalSpecialid: body.global_specialid, - total: body.songcount, - playCount: body.playcount, - } - - this.CollectionIdListInfoCache.set(id, info) - return info - }) + }).then(data => data.map(s => s[0]))) }, - /** - * 通过SpecialId获取歌单 - * @param {*} id - */ - // async getUserListDetailBySpecialId(id, page = 1, limit = 300) { - // if (!id || id.length > 1000) return Promise.reject(new Error('get list error.')) - // const listInfo = await this.getListInfoBySpecialId(id) - - // const params = `specialid=${id}&need_sort=1&module=CloudMusic&clientver=11589&pagesize=${limit}&userid=0&page=${page}&type=0&area_code=1&appid=1005` - // return createHttpFetch(`http://pubsongs.kugou.com/v2/get_other_list_file?${params}&signature=${signatureParams(params, 2)}`, { - // headers: { - // 'User-Agent': 'Android10-AndroidPhone-11589-201-0-playlist-wifi', - // }, - // }).then(body => { - // if (!body.info) return Promise.reject(new Error('Get list failed.')) - // const songList = this.filterListByCollectionId(body.info) - - // return { - // list: songList || [], - // page, - // limit, - // total: body.count, - // source: 'kg', - // info: { - // name: listInfo.name, - // img: listInfo.image, - // desc: listInfo.desc, - // // author: listInfo.userName, - // // play_count: formatPlayCount(listInfo.playCount), - // }, - // } - // }) - // }, - /** - * 通过CollectionId获取歌单 - * @param {*} id - */ - async getUserListDetailByCollectionId(id, page = 1, limit = 300) { - if (!id || id.length > 1000) return Promise.reject(new Error('ID error.')) - const listInfo = await this.getUserListInfoByCollectionId(id) - - const params = `need_sort=1&module=CloudMusic&clientver=11589&pagesize=${limit}&global_collection_id=${id}&userid=0&page=${page}&type=0&area_code=1&appid=1005` - return createHttpFetch(`http://pubsongs.kugou.com/v2/get_other_list_file?${params}&signature=${signatureParams(params, 2)}`, { - headers: { - 'User-Agent': 'Android10-AndroidPhone-11589-201-0-playlist-wifi', - }, - }).then(body => { - if (!body.info) return Promise.reject(new Error('Get list failed.')) - const songList = this.filterListByCollectionId(body.info) - - return { - list: songList || [], - page, - limit, - total: listInfo.total, - source: 'kg', - info: { - name: listInfo.name, - img: listInfo.imageUrl && listInfo.imageUrl.replace('{size}', 240), - desc: listInfo.desc, - author: listInfo.userName, - play_count: formatPlayCount(listInfo.playCount), - }, - } - }) + async getMusicInfos(list) { + return this.filterData2( + await Promise.all( + this.createTask( + this.deDuplication(list) + .map(item => ({ hash: item.hash })), + )) + .then(([...datas]) => datas.flat())) }, - /** - * 过滤GlobalSpecialId歌单数据 - * @param {*} rawData - */ - filterListByCollectionId(rawData) { - let ids = new Set() - let list = [] - rawData.forEach(item => { - if (!item) return - if (ids.has(item.hash)) return - ids.add(item.hash) - const types = [] - const _types = {} - item.relate_goods.forEach(data => { - let size = sizeFormate(data.size) - switch (data.level) { - case 2: - types.push({ type: '128k', size, hash: data.hash }) - _types['128k'] = { - size, - hash: data.hash, - } - break - case 4: - types.push({ type: '320k', size, hash: data.hash }) - _types['320k'] = { - size, - hash: data.hash, - } - break - case 5: - types.push({ type: 'flac', size, hash: data.hash }) - _types.flac = { - size, - hash: data.hash, - } - break - case 6: - types.push({ type: 'flac24bit', size, hash: data.hash }) - _types.flac24bit = { - size, - hash: data.hash, - } - break - } - }) - - list.push({ - singer: formatSingerName(item.singerinfo, 'name') || decodeName(item.name).split(' - ')[0].replace(/&/g, '、'), - name: decodeName(item.name).split(' - ')[1], - albumName: decodeName(item.albuminfo.name), - albumId: item.albuminfo.id, - songmid: item.audio_id, - source: 'kg', - interval: formatPlayTime(parseInt(item.timelen) / 1000), - img: null, - lrc: null, - hash: item.hash, - otherSource: null, - types, - _types, - typeUrl: {}, - }) - }) - return list - }, - /** - * 通过酷狗码获取歌单 - * @param {*} id - * @param {*} page - */ - async getUserListDetailByCode(id, page = 1) { - // type 1单曲,2歌单,3电台,4酷狗码,5别人的播放队列 - const codeData = await createHttpFetch('http://t.kugou.com/command/', { + async getUserListDetailByCode(id) { + const songInfo = await this.createHttp('http://t.kugou.com/command/', { method: 'POST', headers: { 'KG-RC': 1, @@ -428,58 +328,59 @@ export default { }, body: { appid: 1001, clientver: 9020, mid: '21511157a05844bd085308bc76ef3343', clienttime: 640612895, key: '36164c4015e704673c588ee202b9ecb8', data: id }, }) - if (!codeData) return Promise.reject(new Error('Get list failed.')) - const codeInfo = codeData.info - - switch (codeInfo.type) { + // console.log(songInfo) + // type 1单曲,2歌单,3电台,4酷狗码,5别人的播放队列 + let songList + let info = songInfo.info + switch (info.type) { case 2: - if (!codeInfo.global_collection_id) return this.getUserListDetailBySpecialId(codeInfo.id, page) + if (!info.global_collection_id) return this.getListDetailBySpecialId(info.id) break - case 3: - return album.getAlbumDetail(codeInfo.id, page) - } - if (codeInfo.global_collection_id) return this.getUserListDetailByCollectionId(codeInfo.global_collection_id, page) - if (codeInfo.userid != null) { - const songList = await createHttpFetch('http://www2.kugou.kugou.com/apps/kucodeAndShare/app/', { + default: + break + } + if (info.global_collection_id) return this.getUserListDetail2(info.global_collection_id) + if (info.userid != null) { + songList = await this.createHttp('http://www2.kugou.kugou.com/apps/kucodeAndShare/app/', { method: 'POST', headers: { 'KG-RC': 1, 'KG-THash': 'network_super_call.cpp:3676261689:379', 'User-Agent': '', }, - body: { appid: 1001, clientver: 9020, mid: '21511157a05844bd085308bc76ef3343', clienttime: 640612895, key: '36164c4015e704673c588ee202b9ecb8', data: { id: codeInfo.id, type: 3, userid: codeInfo.userid, collect_type: 0, page: 1, pagesize: codeInfo.count } }, + body: { appid: 1001, clientver: 9020, mid: '21511157a05844bd085308bc76ef3343', clienttime: 640612895, key: '36164c4015e704673c588ee202b9ecb8', data: { id: info.id, type: 3, userid: info.userid, collect_type: 0, page: 1, pagesize: info.count } }, }) // console.log(songList) - let list = await getMusicInfosByList(songList || codeInfo.list) - return { - list, - page: 1, - limit: codeInfo.count, - total: list.length, - source: 'kg', - info: { - name: codeInfo.name, - img: (codeInfo.img_size && codeInfo.img_size.replace('{size}', 240)) || codeInfo.img, - // desc: body.result.info.list_desc, - author: codeInfo.username, - // play_count: formatPlayCount(info.count), - }, - } + } + let list = await this.getMusicInfos(songList || songInfo.list) + return { + list, + page: 1, + limit: info.count, + total: list.length, + source: 'kg', + info: { + name: info.name, + img: (info.img_size && info.img_size.replace('{size}', 240)) || info.img, + // desc: body.result.info.list_desc, + author: info.username, + // play_count: formatPlayCount(info.count), + }, } }, async getUserListDetail3(chain, page) { - const songInfo = await createHttpFetch(`http://m.kugou.com/schain/transfer?pagesize=${this.listDetailLimit}&chain=${chain}&su=1&page=${page}&n=0.7928855356604456`, { + const songInfo = await this.createHttp(`http://m.kugou.com/schain/transfer?pagesize=${this.listDetailLimit}&chain=${chain}&su=1&page=${page}&n=0.7928855356604456`, { headers: { 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1', }, }) if (!songInfo.list) { - if (songInfo.global_collection_id) return this.getUserListDetailByCollectionId(songInfo.global_collection_id, page) + if (songInfo.global_collection_id) return this.getUserListDetail2(songInfo.global_collection_id) else return this.getUserListDetail4(songInfo, chain, page).catch(() => this.getUserListDetail5(chain)) } - let list = await getMusicInfosByList(songInfo.list) + let list = await this.getMusicInfos(songInfo.list) // console.log(info, songInfo) return { list, @@ -497,6 +398,15 @@ export default { } }, + deDuplication(datas) { + let ids = new Set() + return datas.filter(({ hash }) => { + if (ids.has(hash)) return false + ids.add(hash) + return true + }) + }, + async getUserListDetailByLink({ info }, link) { let listInfo = info['0'] let total = listInfo.count @@ -506,7 +416,7 @@ export default { const limit = total > 90 ? 90 : total total -= limit page += 1 - tasks.push(createHttpFetch(link.replace(/pagesize=\d+/, 'pagesize=' + limit).replace(/page=\d+/, 'page=' + page), { + tasks.push(this.createHttp(link.replace(/pagesize=\d+/, 'pagesize=' + limit).replace(/page=\d+/, 'page=' + page), { headers: { 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1', Referer: link, @@ -514,7 +424,7 @@ export default { }).then(data => data.list.info)) } let result = await Promise.all(tasks).then(([...datas]) => datas.flat()) - result = await getMusicInfosByList(result) + result = await this.getMusicInfos(result) // console.log(result) return { list: result, @@ -539,7 +449,7 @@ export default { total -= limit page += 1 const params = 'appid=1058&global_specialid=' + id + '&specialid=0&plat=0&version=8000&page=' + page + '&pagesize=' + limit + '&srcappid=2919&clientver=20000&clienttime=1586163263991&mid=1586163263991&uuid=1586163263991&dfid=-' - tasks.push(createHttpFetch(`https://mobiles.kugou.com/api/v5/special/song_v2?${params}&signature=${signatureParams(params, 5)}`, { + tasks.push(this.createHttp(`https://mobiles.kugou.com/api/v5/special/song_v2?${params}&signature=${signatureParams(params, 5)}`, { headers: { mid: '1586163263991', Referer: 'https://m3ws.kugou.com/share/index.php', @@ -555,7 +465,7 @@ export default { let id = global_collection_id if (id.length > 1000) throw new Error('get list error') const params = 'appid=1058&specialid=0&global_specialid=' + id + '&format=jsonp&srcappid=2919&clientver=20000&clienttime=1586163242519&mid=1586163242519&uuid=1586163242519&dfid=-' - let info = await createHttpFetch(`https://mobiles.kugou.com/api/v5/special/info_v2?${params}&signature=${signatureParams(params, 5)}`, { + let info = await this.createHttp(`https://mobiles.kugou.com/api/v5/special/info_v2?${params}&signature=${signatureParams(params, 5)}`, { headers: { mid: '1586163242519', Referer: 'https://m3ws.kugou.com/share/index.php', @@ -565,7 +475,7 @@ export default { }, }) const songInfo = await this.createGetListDetail2Task(id, info.songcount) - let list = await getMusicInfosByList(songInfo) + let list = await this.getMusicInfos(songInfo) // console.log(info, songInfo, list) return { list, @@ -590,7 +500,6 @@ export default { 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1', }, }).promise - // console.log(body) let result = body.match(/var\sphpParam\s=\s({.+?});/) if (result) result = JSON.parse(result[1]) this.cache.set(chain, result) @@ -608,7 +517,7 @@ export default { let result = body.match(/var\sdataFromSmarty\s=\s(\[.+?\])/) if (result) result = JSON.parse(result[1]) this.cache.set(chain, result) - result = await getMusicInfosByList(result) + result = await this.getMusicInfos(result) // console.log(info, songInfo) return result }, @@ -617,7 +526,7 @@ export default { const limit = 100 const [listInfo, list] = await Promise.all([ this.getListInfoByChain(chain), - this.getUserListDetailBySpecialId(songInfo.id, page, limit), + this.getUserListDetailById(songInfo.id, page, limit), ]) return { list: list || [], @@ -656,29 +565,50 @@ export default { } }, + async getUserListDetailById(id, page, limit) { + const signature = await handleSignature(id, page, limit) + let info = await this.createHttp(`https://pubsongscdn.kugou.com/v2/get_other_list_file?srcappid=2919&clientver=20000&appid=1058&type=0&module=playlist&page=${page}&pagesize=${limit}&specialid=${id}&signature=${signature}`, { + headers: { + Referer: 'https://m3ws.kugou.com/share/index.php', + 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1', + dfid: '-', + }, + }) + + // console.log(info) + let result = await this.getMusicInfos(info.info) + // console.log(info, songInfo) + return result + }, + async getUserListDetail(link, page, retryNum = 0) { if (retryNum > 3) return Promise.reject(new Error('link try max num')) + if (link.includes('#')) link = link.replace(/#.*$/, '') + if (link.includes('global_collection_id')) return this.getUserListDetail2(link.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1')) + if (link.includes('chain=')) return this.getUserListDetail3(link.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'), page) + if (link.includes('.html')) { + if (link.includes('zlist.html')) { + link = link.replace(/^(.*)zlist\.html/, 'https://m3ws.kugou.com/zlist/list') + if (link.includes('pagesize')) { + link = link.replace('pagesize=30', 'pagesize=' + this.listDetailLimit).replace('page=1', 'page=' + page) + } else { + link += `&pagesize=${this.listDetailLimit}&page=${page}` + } + } else if (!link.includes('song.html')) return this.getUserListDetail3(link.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page) + } - const requestLink = httpFetch(link, { + const requestObj_listDetailLink = httpFetch(link, { headers: { 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1', Referer: link, }, - follow_max: 2, }) - const { headers: { location }, statusCode, body } = await requestLink.promise - // console.log(body, location, statusCode) + const { headers: { location }, statusCode, body } = await requestObj_listDetailLink.promise + // console.log(body, location) if (statusCode > 400) return this.getUserListDetail(link, page, ++retryNum) - if (typeof body == 'string') { - if (body.includes('"global_collection_id":')) return this.getUserListDetailByCollectionId(body.replace(/^[\s\S]+?"global_collection_id":"(\w+)"[\s\S]+?$/, '$1'), page) - if (body.includes('"albumid":')) return album.getAlbumDetail(body.replace(/^[\s\S]+?"albumid":(\w+)[\s\S]+?$/, '$1'), page) - if (body.includes('"album_id":') && link.includes('album/info')) return album.getAlbumDetail(body.replace(/^[\s\S]+?"album_id":(\w+)[\s\S]+?$/, '$1'), page) - if (body.includes('list_id = "') && link.includes('album/info')) return album.getAlbumDetail(body.replace(/^[\s\S]+?list_id = "(\w+)"[\s\S]+?$/, '$1'), page) - } if (location) { - // 概念版分享链接 https://t1.kugou.com/xxx - if (location.includes('global_specialid')) return this.getUserListDetailByCollectionId(location.replace(/^.*?global_specialid=(\w+)(?:&.*$|#.*$|$)/, '$1'), page) - if (location.includes('global_collection_id')) return this.getUserListDetailByCollectionId(location.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1'), page) + // console.log(location) + if (location.includes('global_collection_id')) return this.getUserListDetail2(location.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1')) if (location.includes('chain=')) return this.getUserListDetail3(location.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'), page) if (location.includes('.html')) { if (location.includes('zlist.html')) { @@ -691,12 +621,210 @@ export default { return this.getUserListDetail(link, page, ++retryNum) } else return this.getUserListDetail3(location.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page) } + // console.log('location', location) return this.getUserListDetail(location, page, ++retryNum) } + if (typeof body == 'string') return this.getUserListDetail2(body.replace(/^[\s\S]+?"global_collection_id":"(\w+)"[\s\S]+?$/, '$1')) if (body.errcode !== 0) return this.getUserListDetail(link, page, ++retryNum) return this.getUserListDetailByLink(body, link) }, + async getListDetail(id, page) { // 获取歌曲列表内的音乐 + id = id.toString() + if (id.includes('special/single/')) { + id = id.replace(this.regExps.listDetailLink, '$1') + } else if (/https?:/.test(id)) { + // fix https://www.kugou.com/songlist/xxx/?uid=xxx&chl=qq_client&cover=http%3A%2F%2Fimge.kugou.com%xxx.jpg&iszlist=1 + return this.getUserListDetail(id.replace(/^.*?http/, 'http'), page) + } else if (/^\d+$/.test(id)) { + return this.getUserListDetailByCode(id) + } else if (id.startsWith('id_')) { + id = id.replace('id_', '') + } + // if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1') + + return this.getListDetailBySpecialId(id, page) + }, + filterData(rawList) { + // console.log(rawList) + return rawList.map(item => { + const types = [] + const _types = {} + if (item.filesize !== 0) { + let size = sizeFormate(item.filesize) + types.push({ type: '128k', size, hash: item.hash }) + _types['128k'] = { + size, + hash: item.hash, + } + } + if (item.filesize_320 !== 0) { + let size = sizeFormate(item.filesize_320) + types.push({ type: '320k', size, hash: item.hash_320 }) + _types['320k'] = { + size, + hash: item.hash_320, + } + } + if (item.filesize_ape !== 0) { + let size = sizeFormate(item.filesize_ape) + types.push({ type: 'ape', size, hash: item.hash_ape }) + _types.ape = { + size, + hash: item.hash_ape, + } + } + if (item.filesize_flac !== 0) { + let size = sizeFormate(item.filesize_flac) + types.push({ type: 'flac', size, hash: item.hash_flac }) + _types.flac = { + size, + hash: item.hash_flac, + } + } + return { + singer: decodeName(item.singername), + name: decodeName(item.songname), + albumName: decodeName(item.album_name), + albumId: item.album_id, + songmid: item.audio_id, + source: 'kg', + interval: formatPlayTime(item.duration / 1000), + img: null, + lrc: null, + hash: item.hash, + types, + _types, + typeUrl: {}, + } + }) + }, + // getSinger(singers) { + // let arr = [] + // singers?.forEach(singer => { + // arr.push(singer.name) + // }) + // return arr.join('、') + // }, + // v9 API + // filterDatav9(rawList) { + // console.log(rawList) + // return rawList.map(item => { + // const types = [] + // const _types = {} + // item.relate_goods.forEach(qualityObj => { + // if (qualityObj.level === 2) { + // let size = sizeFormate(qualityObj.size) + // types.push({ type: '128k', size, hash: qualityObj.hash }) + // _types['128k'] = { + // size, + // hash: qualityObj.hash, + // } + // } else if (qualityObj.level === 4) { + // let size = sizeFormate(qualityObj.size) + // types.push({ type: '320k', size, hash: qualityObj.hash }) + // _types['320k'] = { + // size, + // hash: qualityObj.hash, + // } + // } else if (qualityObj.level === 5) { + // let size = sizeFormate(qualityObj.size) + // types.push({ type: 'flac', size, hash: qualityObj.hash }) + // _types.flac = { + // size, + // hash: qualityObj.hash, + // } + // } else if (qualityObj.level === 6) { + // let size = sizeFormate(qualityObj.size) + // types.push({ type: 'flac24bit', size, hash: qualityObj.hash }) + // _types.flac24bit = { + // size, + // hash: qualityObj.hash, + // } + // } + // }) + // const nameInfo = item.name.split(' - ') + // return { + // singer: this.getSinger(item.singerinfo), + // name: decodeName((nameInfo[1] ?? nameInfo[0]).trim()), + // albumName: decodeName(item.albuminfo.name), + // albumId: item.albuminfo.id, + // songmid: item.audio_id, + // source: 'kg', + // interval: formatPlayTime(item.timelen / 1000), + // img: null, + // lrc: null, + // hash: item.hash, + // types, + // _types, + // typeUrl: {}, + // } + // }) + // }, + + // hash list filter + filterData2(rawList) { + // console.log(rawList) + let ids = new Set() + let list = [] + rawList.forEach(item => { + if (!item) return + if (ids.has(item.audio_info.audio_id)) return + ids.add(item.audio_info.audio_id) + const types = [] + const _types = {} + if (item.audio_info.filesize !== '0') { + let size = sizeFormate(parseInt(item.audio_info.filesize)) + types.push({ type: '128k', size, hash: item.audio_info.hash }) + _types['128k'] = { + size, + hash: item.audio_info.hash, + } + } + if (item.audio_info.filesize_320 !== '0') { + let size = sizeFormate(parseInt(item.audio_info.filesize_320)) + types.push({ type: '320k', size, hash: item.audio_info.hash_320 }) + _types['320k'] = { + size, + hash: item.audio_info.hash_320, + } + } + if (item.audio_info.filesize_flac !== '0') { + let size = sizeFormate(parseInt(item.audio_info.filesize_flac)) + types.push({ type: 'flac', size, hash: item.audio_info.hash_flac }) + _types.flac = { + size, + hash: item.audio_info.hash_flac, + } + } + if (item.audio_info.filesize_high !== '0') { + let size = sizeFormate(parseInt(item.audio_info.filesize_high)) + types.push({ type: 'flac24bit', size, hash: item.audio_info.hash_high }) + _types.flac24bit = { + size, + hash: item.audio_info.hash_high, + } + } + list.push({ + singer: decodeName(item.author_name), + name: decodeName(item.ori_audio_name), + albumName: decodeName(item.album_info.album_name), + albumId: item.album_info.album_id, + songmid: item.audio_info.audio_id, + source: 'kg', + interval: formatPlayTime(parseInt(item.audio_info.timelength) / 1000), + img: null, + lrc: null, + hash: item.audio_info.hash, + otherSource: null, + types, + _types, + typeUrl: {}, + }) + }) + return list + }, + // 获取列表信息 getListInfo(tagId, tryNum = 0) { if (this._requestObj_listInfo) this._requestObj_listInfo.cancelHttp() @@ -759,33 +887,32 @@ export default { }, search(text, page, limit = 20) { - const params = `userid=1384394652&req_custom=1&appid=1005&req_multi=1&version=11589&page=${page}&filter=0&pagesize=${limit}&order=0&clienttime=1681779443&iscorrection=1&searchsong=0&keyword=${text}&mid=288799920684148686226285199951543865551&dfid=3eSBsO1u97EY1zeIZd40hH4p&clientver=11589&platform=AndroidFilter` - const url = encodeURI(`http://complexsearchretry.kugou.com/v1/search/special?${params}&signature=${signatureParams(params, 1)}`) - return createHttpFetch(url).then(body => { - // console.log(body) - return { - list: body.lists.map(item => { - return { - play_count: formatPlayCount(item.total_play_count), - id: item.gid ? `gid_${item.gid}` : `id_${item.specialid}`, - author: item.nickname, - name: item.specialname, - time: dateFormat(item.publish_time, 'Y-M-D'), - img: item.img, - grade: item.grade, - desc: item.intro, - total: item.song_count, - source: 'kg', - } - }), - limit, - total: body.total, - source: 'kg', - } - }) // http://msearchretry.kugou.com/api/v3/search/special?version=9209&keyword=%E5%91%A8%E6%9D%B0%E4%BC%A6&pagesize=20&filter=0&page=1&sver=2&with_res_tag=0 - // http://ioscdn.kugou.com/api/v3/search/special?keyword=${encodeURIComponent(text)}&page=${page}&pagesize=${limit}&showtype=10&plat=2&version=7910&correct=1&sver=5 - // http://msearchretry.kugou.com/api/v3/search/special?keyword=${encodeURIComponent(text)}&page=${page}&pagesize=${limit}&showtype=10&filter=0&version=7910&sver=2 + // return httpFetch(`http://ioscdn.kugou.com/api/v3/search/special?keyword=${encodeURIComponent(text)}&page=${page}&pagesize=${limit}&showtype=10&plat=2&version=7910&correct=1&sver=5`) + return httpFetch(`http://msearchretry.kugou.com/api/v3/search/special?keyword=${encodeURIComponent(text)}&page=${page}&pagesize=${limit}&showtype=10&filter=0&version=7910&sver=2`) + .promise.then(({ body }) => { + if (body.errcode != 0) throw new Error('filed') + // console.log(body.data.info) + return { + list: body.data.info.map(item => { + return { + play_count: formatPlayCount(item.playcount), + id: 'id_' + item.specialid, + author: item.nickname, + name: item.specialname, + time: dateFormat(item.publishtime, 'Y-M-D'), + img: item.imgurl, + grade: item.grade, + desc: item.intro, + total: item.songcount, + source: 'kg', + } + }), + limit, + total: body.data.total, + source: 'kg', + } + }) }, } diff --git a/src/renderer/utils/musicSdk/kg/temp/musicSearch-new.js b/src/renderer/utils/musicSdk/kg/temp/musicSearch-new.js index d293ca24..535fc790 100644 --- a/src/renderer/utils/musicSdk/kg/temp/musicSearch-new.js +++ b/src/renderer/utils/musicSdk/kg/temp/musicSearch-new.js @@ -1,3 +1,4 @@ +import { formatSingerName } from '@renderer/utils/musicSdk/utils' import { decodeName, formatPlayTime, sizeFormate } from '../../index' import { signatureParams, createHttpFetch } from './util' @@ -58,8 +59,8 @@ export default { } } list.push({ - singer: decodeName(item.SingerName), - name: decodeName(item.OriSongName), + singer: decodeName(formatSingerName(item.Singers)), + name: decodeName(item.SongName), albumName: decodeName(item.AlbumName), albumId: item.AlbumID, songmid: item.Audioid, diff --git a/src/renderer/utils/musicSdk/kg/temp/songList-old.js b/src/renderer/utils/musicSdk/kg/temp/songList-new.js similarity index 51% rename from src/renderer/utils/musicSdk/kg/temp/songList-old.js rename to src/renderer/utils/musicSdk/kg/temp/songList-new.js index 6557a578..389e93c1 100644 --- a/src/renderer/utils/musicSdk/kg/temp/songList-old.js +++ b/src/renderer/utils/musicSdk/kg/temp/songList-new.js @@ -1,17 +1,9 @@ import { httpFetch } from '../../../request' -import { decodeName, formatPlayTime, sizeFormate, dateFormat } from '../../../index' -import infSign from '../vendors/infSign.min' -import { signatureParams } from '../util' - -const handleSignature = (id, page, limit) => new Promise((resolve, reject) => { - infSign({ appid: 1058, type: 0, module: 'playlist', page, pagesize: limit, specialid: id }, null, { - useH5: !0, - isCDN: !0, - callback(i) { - resolve(i.signature) - }, - }) -}) +import { formatSingerName } from '../../utils' +import { decodeName, formatPlayTime, sizeFormate, dateFormat, formatPlayCount } from '../../../index' +import { signatureParams, createHttpFetch } from './../util' +import { getMusicInfosByList } from '../musicInfo' +import album from '../album' export default { _requestObj_tags: null, @@ -46,75 +38,59 @@ export default { }, ], cache: new Map(), + collectionIdListInfoCache: new Map(), regExps: { listData: /global\.data = (\[.+\]);/, listInfo: /global = {[\s\S]+?name: "(.+)"[\s\S]+?pic: "(.+)"[\s\S]+?};/, // https://www.kugou.com/yy/special/single/1067062.html listDetailLink: /^.+\/(\d+)\.html(?:\?.*|&.*$|#.*$|$)/, }, - async getGlobalSpecialId(specialId) { - return httpFetch(`http://mobilecdnbj.kugou.com/api/v5/special/info?specialid=${specialId}`, { - headers: { - 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; HLK-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Mobile Safari/537.36 EdgA/104.0.1293.70', - }, - }).promise.then(({ body }) => { - // console.log(body) - if (!body.data.global_specialid) Promise.reject(new Error('Failed to get global collection id.')) - return body.data.global_specialid - }) + + /** + * 获取歌曲列表内的音乐 + * @param {*} id + * @param {*} page + */ + async getListDetail(id, page) { + id = id.toString() + + if (id.includes('special/single/')) id = id.replace(this.regExps.listDetailLink, '$1') + // fix https://www.kugou.com/songlist/xxx/?uid=xxx&chl=qq_client&cover=http%3A%2F%2Fimge.kugou.com%xxx.jpg&iszlist=1 + if (/https?:/.test(id)) { + if (id.includes('#')) id = id.replace(/#.*$/, '') + if (id.includes('global_collection_id')) return this.getUserListDetailByCollectionId(id.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1'), page) + if (id.includes('chain=')) return this.getUserListDetail3(id.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'), page) + if (id.includes('.html')) { + if (id.includes('zlist.html')) { + id = id.replace(/^(.*)zlist\.html/, 'https://m3ws.kugou.com/zlist/list') + if (id.includes('pagesize')) { + id = id.replace('pagesize=30', 'pagesize=' + this.listDetailLimit).replace('page=1', 'page=' + page) + } else { + id += `&pagesize=${this.listDetailLimit}&page=${page}` + } + } else if (!id.includes('song.html')) return this.getUserListDetail3(id.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page) + } + return this.getUserListDetail(id.replace(/^.*?http/, 'http'), page) + } + if (/^\d+$/.test(id)) return this.getUserListDetailByCode(id, page) + if (id.startsWith('gid_')) return this.getUserListDetailByCollectionId(id.replace('gid_', ''), page) + if (id.startsWith('id_')) return this.getUserListDetailBySpecialId(id.replace('id_', ''), page) + + return new Error('Failed.') }, - // async getListInfoBySpecialId(special_id, retry = 0) { - // if (++retry > 2) throw new Error('failed') - // return httpFetch(`https://m.kugou.com/plist/list/${special_id}/?json=true`, { - // headers: { - // 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; HLK-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Mobile Safari/537.36 EdgA/104.0.1293.70', - // }, - // follow_max: 2, - // }).promise.then(({ body }) => { - // // console.log(body) - // if (!body.info.list) return this.getListInfoBySpecialId(special_id, retry) - // let listinfo = body.info.list - // return { - // listInfo: { - // name: listinfo.specialname, - // image: listinfo.imgurl.replace('{size}', '150'), - // intro: listinfo.intro, - // author: listinfo.nickname, - // playcount: listinfo.playcount, - // total: listinfo.songcount, - // }, - // globalSpecialId: listinfo.global_specialid, - // } - // }) - // }, - // async getSongListDetailByGlobalSpecialId(id, page, limit = 100, retry = 0) { - // if (++retry > 2) throw new Error('failed') - // console.log(id) - // const params = `specialid=0&need_sort=1&module=CloudMusic&clientver=11409&pagesize=${limit}&global_collection_id=${id}&userid=0&page=${page}&type=1&area_code=1&appid=1005` - // return httpFetch(`http://pubsongscdn.tx.kugou.com/v2/get_other_list_file?${params}&signature=${signatureParams(params)}`).promise.then(({ body }) => { - // // console.log(body) - // if (body.data?.info == null) return this.getSongListDetailByGlobalSpecialId(id, page, limit, retry) - // return body.data.info - // }) - // }, - parseHtmlDesc(html) { - const prefix = '
' - let index = html.indexOf(prefix) - if (index < 0) return null - const afterStr = html.substring(index + prefix.length) - index = afterStr.indexOf('
') - if (index < 0) return null - return decodeName(afterStr.substring(0, index)) - }, - async getListDetailBySpecialId(id, page, tryNum = 0) { + + /** + * 获取SpecialId歌单 + * @param {*} id + */ + async getUserListDetailBySpecialId(id, page, tryNum = 0) { if (tryNum > 2) throw new Error('try max num') const { body } = await httpFetch(this.getSongListDetailUrl(id)).promise let listData = body.match(this.regExps.listData) let listInfo = body.match(this.regExps.listInfo) if (!listData) return this.getListDetailBySpecialId(id, page, ++tryNum) - let list = await this.getMusicInfos(JSON.parse(listData[1])) - // listData = this.filterData(JSON.parse(listData[1])) + let list = await getMusicInfosByList(JSON.parse(listData[1])) let name let pic if (listInfo) { @@ -135,52 +111,55 @@ export default { img: pic, desc, // author: body.result.info.userinfo.username, - // play_count: this.formatPlayCount(body.result.listen_num), + // play_count: formatPlayCount(body.result.listen_num), }, } + }, + parseHtmlDesc(html) { + const prefix = '
' + let index = html.indexOf(prefix) + if (index < 0) return null + const afterStr = html.substring(index + prefix.length) + index = afterStr.indexOf('
') + if (index < 0) return null + return decodeName(afterStr.substring(0, index)) + }, - // const globalSpecialId = await this.getGlobalSpecialId(id) - // const limit = 100 - // const listData = await this.getSongListDetailByGlobalSpecialId(globalSpecialId, page, limit) - // if (!Array.isArray(listData)) - // return this.getUserListDetail2(globalSpecialId) - // return { - // list: this.filterDatav9(listData), - // page, - // limit, - // total: listInfo.total, - // source: 'kg', - // info: { - // name: listInfo.name, - // img: listInfo.image, - // desc: listInfo.intro, - // author: listInfo.author, - // play_count: this.formatPlayCount(listInfo.playcount), - // }, - // } + /** + * 使用SpecialId获取CollectionId + * @param {*} specialId + */ + async getCollectionIdBySpecialId(specialId) { + return httpFetch(`http://mobilecdnbj.kugou.com/api/v5/special/info?specialid=${specialId}`, { + headers: { + 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; HLK-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Mobile Safari/537.36 EdgA/104.0.1293.70', + }, + }).promise.then(({ body }) => { + // console.log('getCollectionIdBySpecialId', body) + if (!body.data.global_specialid) return Promise.reject(new Error('Failed to get global collection id.')) + return body.data.global_specialid + }) + }, + + /** + * 获取歌单URL + * @param {*} sortId + * @param {*} tagId + * @param {*} page + */ + getSongListUrl(sortId, tagId, page) { + if (tagId == null) tagId = '' + return `http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_ajax=1&cdn=cdn&t=${sortId}&c=${tagId}&p=${page}` }, getInfoUrl(tagId) { return tagId ? `http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_smarty=1&cdn=cdn&t=5&c=${tagId}` : 'http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_smarty=1&' }, - getSongListUrl(sortId, tagId, page) { - if (tagId == null) tagId = '' - return `http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_ajax=1&cdn=cdn&t=${sortId}&c=${tagId}&p=${page}` - }, getSongListDetailUrl(id) { return `http://www2.kugou.kugou.com/yueku/v9/special/single/${id}-5-9999.html` }, - /** - * 格式化播放数量 - * @param {*} num - */ - formatPlayCount(num) { - if (num > 100000000) return parseInt(num / 10000000) / 10 + '亿' - if (num > 10000) return parseInt(num / 1000) / 10 + '万' - return num - }, filterInfoHotTag(rawData) { const result = [] if (rawData.status !== 1) return result @@ -194,6 +173,7 @@ export default { } return result }, + filterTagInfo(rawData) { const result = [] for (const name of Object.keys(rawData)) { @@ -210,6 +190,20 @@ export default { } return result }, + filterSongList(rawData) { + return rawData.map(item => ({ + play_count: item.total_play_count || formatPlayCount(item.play_count), + id: 'id_' + item.specialid, + author: item.nickname, + name: item.specialname, + time: dateFormat(item.publish_time || item.publishtime, 'Y-M-D'), + img: item.img || item.imgurl, + total: item.songcount, + grade: item.grade, + desc: item.intro, + source: 'kg', + })) + }, getSongList(sortId, tagId, page, tryNum = 0) { if (this._requestObj_list) this._requestObj_list.cancelHttp() @@ -219,7 +213,7 @@ export default { ) return this._requestObj_list.promise.then(({ body }) => { if (!body || body.status !== 1) return this.getSongList(sortId, tagId, page, ++tryNum) - return this.filterList(body.special_db) + return this.filterSongList(body.special_db) }) }, getSongListRecommend(tryNum = 0) { @@ -247,88 +241,185 @@ export default { ) return this._requestObj_listRecommend.promise.then(({ body }) => { if (body.status !== 1) return this.getSongListRecommend(++tryNum) - return this.filterList(body.data.special_list) + return this.filterSongList(body.data.special_list) }) }, - filterList(rawData) { - return rawData.map(item => ({ - play_count: item.total_play_count || this.formatPlayCount(item.play_count), - id: 'id_' + item.specialid, - author: item.nickname, - name: item.specialname, - time: dateFormat(item.publish_time || item.publishtime, 'Y-M-D'), - img: item.img || item.imgurl, - total: item.songcount, - grade: item.grade, - desc: item.intro, - source: 'kg', - })) - }, - async createHttp(url, options, retryNum = 0) { - if (retryNum > 2) throw new Error('try max num') - let result - try { - result = await httpFetch(url, options).promise - } catch (err) { - console.log(err) - return this.createHttp(url, options, ++retryNum) - } - // console.log(result.statusCode, result.body) - if (result.statusCode !== 200 || - ( - (result.body.error_code !== undefined - ? result.body.error_code - : result.body.errcode !== undefined - ? result.body.errcode - : result.body.err_code - ) !== 0) - ) return this.createHttp(url, options, ++retryNum) - if (result.body.data) return result.body.data - if (Array.isArray(result.body.info)) return result.body - return result.body.info - }, + /** + * 通过CollectionId获取歌单详情 + * @param {*} id + */ + async getUserListInfoByCollectionId(id) { + if (!id || id.length > 1000) return Promise.reject(new Error('get list error')) + if (this.collectionIdListInfoCache.has(id)) return this.collectionIdListInfoCache.get(id) - createTask(hashs) { - let data = { - appid: 1001, - clienttime: 639437935, - clientver: 9020, - fields: - 'album_info,author_name,audio_info,ori_audio_name', - is_publish: '1', - key: '0475af1457cd3363c7b45b871e94428a', - mid: '21511157a05844bd085308bc76ef3342', - show_privilege: 1, - } - let list = hashs - let tasks = [] - while (list.length) { - tasks.push(Object.assign({ data: list.slice(0, 100) }, data)) - if (list.length < 100) break - list = list.slice(100) - } - let url = 'http://kmr.service.kugou.com/v2/album_audio/audio' - return tasks.map(task => this.createHttp(url, { - method: 'POST', - body: task, + const params = `appid=1058&specialid=0&global_specialid=${id}&format=jsonp&srcappid=2919&clientver=20000&clienttime=1586163242519&mid=1586163242519&uuid=1586163242519&dfid=-` + return createHttpFetch(`https://mobiles.kugou.com/api/v5/special/info_v2?${params}&signature=${signatureParams(params, 5)}`, { headers: { - 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1', + mid: '1586163242519', + Referer: 'https://m3ws.kugou.com/share/index.php', + 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1', + dfid: '-', + clienttime: '1586163242519', }, - }).then(data => data.map(s => s[0]))) - }, - async getMusicInfos(list) { - return this.filterData2( - await Promise.all( - this.createTask( - this.deDuplication(list) - .map(item => ({ hash: item.hash })), - )) - .then(([...datas]) => datas.flat())) - }, + }).then(body => { + let info = { + type: body.type, + userName: body.nickname, + userAvatar: body.user_avatar, + imageUrl: body.imgurl, + desc: body.intro, + name: body.specialname, + globalSpecialid: body.global_specialid, + total: body.songcount, + playCount: body.playcount, + } - async getUserListDetailByCode(id) { - const songInfo = await this.createHttp('http://t.kugou.com/command/', { + this.collectionIdListInfoCache.set(id, info) + return info + }) + }, + /** + * 通过SpecialId获取歌单 + * @param {*} id + */ + // async getUserListDetailBySpecialId(id, page = 1, limit = 300) { + // if (!id || id.length > 1000) return Promise.reject(new Error('get list error.')) + // const listInfo = await this.getListInfoBySpecialId(id) + + // const params = `specialid=${id}&need_sort=1&module=CloudMusic&clientver=11589&pagesize=${limit}&userid=0&page=${page}&type=0&area_code=1&appid=1005` + // return createHttpFetch(`http://pubsongs.kugou.com/v2/get_other_list_file?${params}&signature=${signatureParams(params, 2)}`, { + // headers: { + // 'User-Agent': 'Android10-AndroidPhone-11589-201-0-playlist-wifi', + // }, + // }).then(body => { + // if (!body.info) return Promise.reject(new Error('Get list failed.')) + // const songList = this.filterListByCollectionId(body.info) + + // return { + // list: songList || [], + // page, + // limit, + // total: body.count, + // source: 'kg', + // info: { + // name: listInfo.name, + // img: listInfo.image, + // desc: listInfo.desc, + // // author: listInfo.userName, + // // play_count: formatPlayCount(listInfo.playCount), + // }, + // } + // }) + // }, + /** + * 通过CollectionId获取歌单 + * @param {*} id + */ + async getUserListDetailByCollectionId(id, page = 1, limit = 300) { + if (!id || id.length > 1000) return Promise.reject(new Error('ID error.')) + const listInfo = await this.getUserListInfoByCollectionId(id) + + const params = `need_sort=1&module=CloudMusic&clientver=11589&pagesize=${limit}&global_collection_id=${id}&userid=0&page=${page}&type=0&area_code=1&appid=1005` + return createHttpFetch(`http://pubsongs.kugou.com/v2/get_other_list_file?${params}&signature=${signatureParams(params, 2)}`, { + headers: { + 'User-Agent': 'Android10-AndroidPhone-11589-201-0-playlist-wifi', + }, + }).then(body => { + if (!body.info) return Promise.reject(new Error('Get list failed.')) + const songList = this.filterListByCollectionId(body.info) + + return { + list: songList || [], + page, + limit, + total: listInfo.total, + source: 'kg', + info: { + name: listInfo.name, + img: listInfo.imageUrl && listInfo.imageUrl.replace('{size}', 240), + desc: listInfo.desc, + author: listInfo.userName, + play_count: formatPlayCount(listInfo.playCount), + }, + } + }) + }, + /** + * 过滤GlobalSpecialId歌单数据 + * @param {*} rawData + */ + filterListByCollectionId(rawData) { + let ids = new Set() + let list = [] + rawData.forEach(item => { + if (!item) return + if (ids.has(item.hash)) return + ids.add(item.hash) + const types = [] + const _types = {} + + item.relate_goods.forEach(data => { + let size = sizeFormate(data.size) + switch (data.level) { + case 2: + types.push({ type: '128k', size, hash: data.hash }) + _types['128k'] = { + size, + hash: data.hash, + } + break + case 4: + types.push({ type: '320k', size, hash: data.hash }) + _types['320k'] = { + size, + hash: data.hash, + } + break + case 5: + types.push({ type: 'flac', size, hash: data.hash }) + _types.flac = { + size, + hash: data.hash, + } + break + case 6: + types.push({ type: 'flac24bit', size, hash: data.hash }) + _types.flac24bit = { + size, + hash: data.hash, + } + break + } + }) + + list.push({ + singer: formatSingerName(item.singerinfo, 'name') || decodeName(item.name).split(' - ')[0].replace(/&/g, '、'), + name: decodeName(item.name).split(' - ')[1], + albumName: decodeName(item.albuminfo.name), + albumId: item.albuminfo.id, + songmid: item.audio_id, + source: 'kg', + interval: formatPlayTime(parseInt(item.timelen) / 1000), + img: null, + lrc: null, + hash: item.hash, + otherSource: null, + types, + _types, + typeUrl: {}, + }) + }) + return list + }, + /** + * 通过酷狗码获取歌单 + * @param {*} id + * @param {*} page + */ + async getUserListDetailByCode(id, page = 1) { + // type 1单曲,2歌单,3电台,4酷狗码,5别人的播放队列 + const codeData = await createHttpFetch('http://t.kugou.com/command/', { method: 'POST', headers: { 'KG-RC': 1, @@ -337,59 +428,58 @@ export default { }, body: { appid: 1001, clientver: 9020, mid: '21511157a05844bd085308bc76ef3343', clienttime: 640612895, key: '36164c4015e704673c588ee202b9ecb8', data: id }, }) - // console.log(songInfo) - // type 1单曲,2歌单,3电台,4酷狗码,5别人的播放队列 - let songList - let info = songInfo.info - switch (info.type) { - case 2: - if (!info.global_collection_id) return this.getListDetailBySpecialId(info.id) - break + if (!codeData) return Promise.reject(new Error('Get list failed.')) + const codeInfo = codeData.info - default: + switch (codeInfo.type) { + case 2: + if (!codeInfo.global_collection_id) return this.getUserListDetailBySpecialId(codeInfo.id, page) break + case 3: + return album.getAlbumDetail(codeInfo.id, page) } - if (info.global_collection_id) return this.getUserListDetail2(info.global_collection_id) - if (info.userid != null) { - songList = await this.createHttp('http://www2.kugou.kugou.com/apps/kucodeAndShare/app/', { + if (codeInfo.global_collection_id) return this.getUserListDetailByCollectionId(codeInfo.global_collection_id, page) + + if (codeInfo.userid != null) { + const songList = await createHttpFetch('http://www2.kugou.kugou.com/apps/kucodeAndShare/app/', { method: 'POST', headers: { 'KG-RC': 1, 'KG-THash': 'network_super_call.cpp:3676261689:379', 'User-Agent': '', }, - body: { appid: 1001, clientver: 9020, mid: '21511157a05844bd085308bc76ef3343', clienttime: 640612895, key: '36164c4015e704673c588ee202b9ecb8', data: { id: info.id, type: 3, userid: info.userid, collect_type: 0, page: 1, pagesize: info.count } }, + body: { appid: 1001, clientver: 9020, mid: '21511157a05844bd085308bc76ef3343', clienttime: 640612895, key: '36164c4015e704673c588ee202b9ecb8', data: { id: codeInfo.id, type: 3, userid: codeInfo.userid, collect_type: 0, page: 1, pagesize: codeInfo.count } }, }) // console.log(songList) - } - let list = await this.getMusicInfos(songList || songInfo.list) - return { - list, - page: 1, - limit: info.count, - total: list.length, - source: 'kg', - info: { - name: info.name, - img: (info.img_size && info.img_size.replace('{size}', 240)) || info.img, - // desc: body.result.info.list_desc, - author: info.username, - // play_count: this.formatPlayCount(info.count), - }, + let list = await getMusicInfosByList(songList || codeInfo.list) + return { + list, + page: 1, + limit: codeInfo.count, + total: list.length, + source: 'kg', + info: { + name: codeInfo.name, + img: (codeInfo.img_size && codeInfo.img_size.replace('{size}', 240)) || codeInfo.img, + // desc: body.result.info.list_desc, + author: codeInfo.username, + // play_count: formatPlayCount(info.count), + }, + } } }, async getUserListDetail3(chain, page) { - const songInfo = await this.createHttp(`http://m.kugou.com/schain/transfer?pagesize=${this.listDetailLimit}&chain=${chain}&su=1&page=${page}&n=0.7928855356604456`, { + const songInfo = await createHttpFetch(`http://m.kugou.com/schain/transfer?pagesize=${this.listDetailLimit}&chain=${chain}&su=1&page=${page}&n=0.7928855356604456`, { headers: { 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1', }, }) if (!songInfo.list) { - if (songInfo.global_collection_id) return this.getUserListDetail2(songInfo.global_collection_id) + if (songInfo.global_collection_id) return this.getUserListDetailByCollectionId(songInfo.global_collection_id, page) else return this.getUserListDetail4(songInfo, chain, page).catch(() => this.getUserListDetail5(chain)) } - let list = await this.getMusicInfos(songInfo.list) + let list = await getMusicInfosByList(songInfo.list) // console.log(info, songInfo) return { list, @@ -402,20 +492,11 @@ export default { img: songInfo.info.img, // desc: body.result.info.list_desc, author: songInfo.info.username, - // play_count: this.formatPlayCount(info.count), + // play_count: formatPlayCount(info.count), }, } }, - deDuplication(datas) { - let ids = new Set() - return datas.filter(({ hash }) => { - if (ids.has(hash)) return false - ids.add(hash) - return true - }) - }, - async getUserListDetailByLink({ info }, link) { let listInfo = info['0'] let total = listInfo.count @@ -425,7 +506,7 @@ export default { const limit = total > 90 ? 90 : total total -= limit page += 1 - tasks.push(this.createHttp(link.replace(/pagesize=\d+/, 'pagesize=' + limit).replace(/page=\d+/, 'page=' + page), { + tasks.push(createHttpFetch(link.replace(/pagesize=\d+/, 'pagesize=' + limit).replace(/page=\d+/, 'page=' + page), { headers: { 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1', Referer: link, @@ -433,7 +514,7 @@ export default { }).then(data => data.list.info)) } let result = await Promise.all(tasks).then(([...datas]) => datas.flat()) - result = await this.getMusicInfos(result) + result = await getMusicInfosByList(result) // console.log(result) return { list: result, @@ -446,7 +527,7 @@ export default { img: listInfo.pic && listInfo.pic.replace('{size}', 240), // desc: body.result.info.list_desc, author: listInfo.list_create_username, - // play_count: this.formatPlayCount(listInfo.count), + // play_count: formatPlayCount(listInfo.count), }, } }, @@ -458,7 +539,7 @@ export default { total -= limit page += 1 const params = 'appid=1058&global_specialid=' + id + '&specialid=0&plat=0&version=8000&page=' + page + '&pagesize=' + limit + '&srcappid=2919&clientver=20000&clienttime=1586163263991&mid=1586163263991&uuid=1586163263991&dfid=-' - tasks.push(this.createHttp(`https://mobiles.kugou.com/api/v5/special/song_v2?${params}&signature=${signatureParams(params, 5)}`, { + tasks.push(createHttpFetch(`https://mobiles.kugou.com/api/v5/special/song_v2?${params}&signature=${signatureParams(params, 5)}`, { headers: { mid: '1586163263991', Referer: 'https://m3ws.kugou.com/share/index.php', @@ -474,7 +555,7 @@ export default { let id = global_collection_id if (id.length > 1000) throw new Error('get list error') const params = 'appid=1058&specialid=0&global_specialid=' + id + '&format=jsonp&srcappid=2919&clientver=20000&clienttime=1586163242519&mid=1586163242519&uuid=1586163242519&dfid=-' - let info = await this.createHttp(`https://mobiles.kugou.com/api/v5/special/info_v2?${params}&signature=${signatureParams(params, 5)}`, { + let info = await createHttpFetch(`https://mobiles.kugou.com/api/v5/special/info_v2?${params}&signature=${signatureParams(params, 5)}`, { headers: { mid: '1586163242519', Referer: 'https://m3ws.kugou.com/share/index.php', @@ -484,7 +565,7 @@ export default { }, }) const songInfo = await this.createGetListDetail2Task(id, info.songcount) - let list = await this.getMusicInfos(songInfo) + let list = await getMusicInfosByList(songInfo) // console.log(info, songInfo, list) return { list, @@ -497,7 +578,7 @@ export default { img: info.imgurl && info.imgurl.replace('{size}', 240), desc: info.intro, author: info.nickname, - play_count: this.formatPlayCount(info.playcount), + play_count: formatPlayCount(info.playcount), }, } }, @@ -509,6 +590,7 @@ export default { 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1', }, }).promise + // console.log(body) let result = body.match(/var\sphpParam\s=\s({.+?});/) if (result) result = JSON.parse(result[1]) this.cache.set(chain, result) @@ -526,7 +608,7 @@ export default { let result = body.match(/var\sdataFromSmarty\s=\s(\[.+?\])/) if (result) result = JSON.parse(result[1]) this.cache.set(chain, result) - result = await this.getMusicInfos(result) + result = await getMusicInfosByList(result) // console.log(info, songInfo) return result }, @@ -535,7 +617,7 @@ export default { const limit = 100 const [listInfo, list] = await Promise.all([ this.getListInfoByChain(chain), - this.getUserListDetailById(songInfo.id, page, limit), + this.getUserListDetailBySpecialId(songInfo.id, page, limit), ]) return { list: list || [], @@ -548,7 +630,7 @@ export default { img: listInfo.imgurl && listInfo.imgurl.replace('{size}', 240), // desc: body.result.info.list_desc, author: listInfo.nickname, - // play_count: this.formatPlayCount(info.count), + // play_count: formatPlayCount(info.count), }, } }, @@ -569,55 +651,34 @@ export default { img: listInfo.imgurl && listInfo.imgurl.replace('{size}', 240), // desc: body.result.info.list_desc, author: listInfo.nickname, - // play_count: this.formatPlayCount(info.count), + // play_count: formatPlayCount(info.count), }, } }, - async getUserListDetailById(id, page, limit) { - const signature = await handleSignature(id, page, limit) - let info = await this.createHttp(`https://pubsongscdn.kugou.com/v2/get_other_list_file?srcappid=2919&clientver=20000&appid=1058&type=0&module=playlist&page=${page}&pagesize=${limit}&specialid=${id}&signature=${signature}`, { - headers: { - Referer: 'https://m3ws.kugou.com/share/index.php', - 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1', - dfid: '-', - }, - }) - - // console.log(info) - let result = await this.getMusicInfos(info.info) - // console.log(info, songInfo) - return result - }, - async getUserListDetail(link, page, retryNum = 0) { if (retryNum > 3) return Promise.reject(new Error('link try max num')) - if (link.includes('#')) link = link.replace(/#.*$/, '') - if (link.includes('global_collection_id')) return this.getUserListDetail2(link.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1')) - if (link.includes('chain=')) return this.getUserListDetail3(link.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'), page) - if (link.includes('.html')) { - if (link.includes('zlist.html')) { - link = link.replace(/^(.*)zlist\.html/, 'https://m3ws.kugou.com/zlist/list') - if (link.includes('pagesize')) { - link = link.replace('pagesize=30', 'pagesize=' + this.listDetailLimit).replace('page=1', 'page=' + page) - } else { - link += `&pagesize=${this.listDetailLimit}&page=${page}` - } - } else if (!link.includes('song.html')) return this.getUserListDetail3(link.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page) - } - const requestObj_listDetailLink = httpFetch(link, { + const requestLink = httpFetch(link, { headers: { 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1', Referer: link, }, + follow_max: 2, }) - const { headers: { location }, statusCode, body } = await requestObj_listDetailLink.promise - // console.log(body, location) + const { headers: { location }, statusCode, body } = await requestLink.promise + // console.log(body, location, statusCode) if (statusCode > 400) return this.getUserListDetail(link, page, ++retryNum) + if (typeof body == 'string') { + if (body.includes('"global_collection_id":')) return this.getUserListDetailByCollectionId(body.replace(/^[\s\S]+?"global_collection_id":"(\w+)"[\s\S]+?$/, '$1'), page) + if (body.includes('"albumid":')) return album.getAlbumDetail(body.replace(/^[\s\S]+?"albumid":(\w+)[\s\S]+?$/, '$1'), page) + if (body.includes('"album_id":') && link.includes('album/info')) return album.getAlbumDetail(body.replace(/^[\s\S]+?"album_id":(\w+)[\s\S]+?$/, '$1'), page) + if (body.includes('list_id = "') && link.includes('album/info')) return album.getAlbumDetail(body.replace(/^[\s\S]+?list_id = "(\w+)"[\s\S]+?$/, '$1'), page) + } if (location) { - // console.log(location) - if (location.includes('global_collection_id')) return this.getUserListDetail2(location.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1')) + // 概念版分享链接 https://t1.kugou.com/xxx + if (location.includes('global_specialid')) return this.getUserListDetailByCollectionId(location.replace(/^.*?global_specialid=(\w+)(?:&.*$|#.*$|$)/, '$1'), page) + if (location.includes('global_collection_id')) return this.getUserListDetailByCollectionId(location.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1'), page) if (location.includes('chain=')) return this.getUserListDetail3(location.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'), page) if (location.includes('.html')) { if (location.includes('zlist.html')) { @@ -630,210 +691,12 @@ export default { return this.getUserListDetail(link, page, ++retryNum) } else return this.getUserListDetail3(location.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page) } - // console.log('location', location) return this.getUserListDetail(location, page, ++retryNum) } - if (typeof body == 'string') return this.getUserListDetail2(body.replace(/^[\s\S]+?"global_collection_id":"(\w+)"[\s\S]+?$/, '$1')) if (body.errcode !== 0) return this.getUserListDetail(link, page, ++retryNum) return this.getUserListDetailByLink(body, link) }, - async getListDetail(id, page) { // 获取歌曲列表内的音乐 - id = id.toString() - if (id.includes('special/single/')) { - id = id.replace(this.regExps.listDetailLink, '$1') - } else if (/https?:/.test(id)) { - // fix https://www.kugou.com/songlist/xxx/?uid=xxx&chl=qq_client&cover=http%3A%2F%2Fimge.kugou.com%xxx.jpg&iszlist=1 - return this.getUserListDetail(id.replace(/^.*?http/, 'http'), page) - } else if (/^\d+$/.test(id)) { - return this.getUserListDetailByCode(id) - } else if (id.startsWith('id_')) { - id = id.replace('id_', '') - } - // if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1') - - return this.getListDetailBySpecialId(id, page) - }, - filterData(rawList) { - // console.log(rawList) - return rawList.map(item => { - const types = [] - const _types = {} - if (item.filesize !== 0) { - let size = sizeFormate(item.filesize) - types.push({ type: '128k', size, hash: item.hash }) - _types['128k'] = { - size, - hash: item.hash, - } - } - if (item.filesize_320 !== 0) { - let size = sizeFormate(item.filesize_320) - types.push({ type: '320k', size, hash: item.hash_320 }) - _types['320k'] = { - size, - hash: item.hash_320, - } - } - if (item.filesize_ape !== 0) { - let size = sizeFormate(item.filesize_ape) - types.push({ type: 'ape', size, hash: item.hash_ape }) - _types.ape = { - size, - hash: item.hash_ape, - } - } - if (item.filesize_flac !== 0) { - let size = sizeFormate(item.filesize_flac) - types.push({ type: 'flac', size, hash: item.hash_flac }) - _types.flac = { - size, - hash: item.hash_flac, - } - } - return { - singer: decodeName(item.singername), - name: decodeName(item.songname), - albumName: decodeName(item.album_name), - albumId: item.album_id, - songmid: item.audio_id, - source: 'kg', - interval: formatPlayTime(item.duration / 1000), - img: null, - lrc: null, - hash: item.hash, - types, - _types, - typeUrl: {}, - } - }) - }, - // getSinger(singers) { - // let arr = [] - // singers?.forEach(singer => { - // arr.push(singer.name) - // }) - // return arr.join('、') - // }, - // v9 API - // filterDatav9(rawList) { - // console.log(rawList) - // return rawList.map(item => { - // const types = [] - // const _types = {} - // item.relate_goods.forEach(qualityObj => { - // if (qualityObj.level === 2) { - // let size = sizeFormate(qualityObj.size) - // types.push({ type: '128k', size, hash: qualityObj.hash }) - // _types['128k'] = { - // size, - // hash: qualityObj.hash, - // } - // } else if (qualityObj.level === 4) { - // let size = sizeFormate(qualityObj.size) - // types.push({ type: '320k', size, hash: qualityObj.hash }) - // _types['320k'] = { - // size, - // hash: qualityObj.hash, - // } - // } else if (qualityObj.level === 5) { - // let size = sizeFormate(qualityObj.size) - // types.push({ type: 'flac', size, hash: qualityObj.hash }) - // _types.flac = { - // size, - // hash: qualityObj.hash, - // } - // } else if (qualityObj.level === 6) { - // let size = sizeFormate(qualityObj.size) - // types.push({ type: 'flac24bit', size, hash: qualityObj.hash }) - // _types.flac24bit = { - // size, - // hash: qualityObj.hash, - // } - // } - // }) - // const nameInfo = item.name.split(' - ') - // return { - // singer: this.getSinger(item.singerinfo), - // name: decodeName((nameInfo[1] ?? nameInfo[0]).trim()), - // albumName: decodeName(item.albuminfo.name), - // albumId: item.albuminfo.id, - // songmid: item.audio_id, - // source: 'kg', - // interval: formatPlayTime(item.timelen / 1000), - // img: null, - // lrc: null, - // hash: item.hash, - // types, - // _types, - // typeUrl: {}, - // } - // }) - // }, - - // hash list filter - filterData2(rawList) { - // console.log(rawList) - let ids = new Set() - let list = [] - rawList.forEach(item => { - if (!item) return - if (ids.has(item.audio_info.audio_id)) return - ids.add(item.audio_info.audio_id) - const types = [] - const _types = {} - if (item.audio_info.filesize !== '0') { - let size = sizeFormate(parseInt(item.audio_info.filesize)) - types.push({ type: '128k', size, hash: item.audio_info.hash }) - _types['128k'] = { - size, - hash: item.audio_info.hash, - } - } - if (item.audio_info.filesize_320 !== '0') { - let size = sizeFormate(parseInt(item.audio_info.filesize_320)) - types.push({ type: '320k', size, hash: item.audio_info.hash_320 }) - _types['320k'] = { - size, - hash: item.audio_info.hash_320, - } - } - if (item.audio_info.filesize_flac !== '0') { - let size = sizeFormate(parseInt(item.audio_info.filesize_flac)) - types.push({ type: 'flac', size, hash: item.audio_info.hash_flac }) - _types.flac = { - size, - hash: item.audio_info.hash_flac, - } - } - if (item.audio_info.filesize_high !== '0') { - let size = sizeFormate(parseInt(item.audio_info.filesize_high)) - types.push({ type: 'flac24bit', size, hash: item.audio_info.hash_high }) - _types.flac24bit = { - size, - hash: item.audio_info.hash_high, - } - } - list.push({ - singer: decodeName(item.author_name), - name: decodeName(item.ori_audio_name), - albumName: decodeName(item.album_info.album_name), - albumId: item.album_info.album_id, - songmid: item.audio_info.audio_id, - source: 'kg', - interval: formatPlayTime(parseInt(item.audio_info.timelength) / 1000), - img: null, - lrc: null, - hash: item.audio_info.hash, - otherSource: null, - types, - _types, - typeUrl: {}, - }) - }) - return list - }, - // 获取列表信息 getListInfo(tagId, tryNum = 0) { if (this._requestObj_listInfo) this._requestObj_listInfo.cancelHttp() @@ -896,32 +759,33 @@ export default { }, search(text, page, limit = 20) { + const params = `userid=1384394652&req_custom=1&appid=1005&req_multi=1&version=11589&page=${page}&filter=0&pagesize=${limit}&order=0&clienttime=1681779443&iscorrection=1&searchsong=0&keyword=${text}&mid=288799920684148686226285199951543865551&dfid=3eSBsO1u97EY1zeIZd40hH4p&clientver=11589&platform=AndroidFilter` + const url = encodeURI(`http://complexsearchretry.kugou.com/v1/search/special?${params}&signature=${signatureParams(params, 1)}`) + return createHttpFetch(url).then(body => { + // console.log(body) + return { + list: body.lists.map(item => { + return { + play_count: formatPlayCount(item.total_play_count), + id: item.gid ? `gid_${item.gid}` : `id_${item.specialid}`, + author: item.nickname, + name: item.specialname, + time: dateFormat(item.publish_time, 'Y-M-D'), + img: item.img, + grade: item.grade, + desc: item.intro, + total: item.song_count, + source: 'kg', + } + }), + limit, + total: body.total, + source: 'kg', + } + }) // http://msearchretry.kugou.com/api/v3/search/special?version=9209&keyword=%E5%91%A8%E6%9D%B0%E4%BC%A6&pagesize=20&filter=0&page=1&sver=2&with_res_tag=0 - // return httpFetch(`http://ioscdn.kugou.com/api/v3/search/special?keyword=${encodeURIComponent(text)}&page=${page}&pagesize=${limit}&showtype=10&plat=2&version=7910&correct=1&sver=5`) - return httpFetch(`http://msearchretry.kugou.com/api/v3/search/special?keyword=${encodeURIComponent(text)}&page=${page}&pagesize=${limit}&showtype=10&filter=0&version=7910&sver=2`) - .promise.then(({ body }) => { - if (body.errcode != 0) throw new Error('filed') - // console.log(body.data.info) - return { - list: body.data.info.map(item => { - return { - play_count: this.formatPlayCount(item.playcount), - id: 'id_' + item.specialid, - author: item.nickname, - name: item.specialname, - time: dateFormat(item.publishtime, 'Y-M-D'), - img: item.imgurl, - grade: item.grade, - desc: item.intro, - total: item.songcount, - source: 'kg', - } - }), - limit, - total: body.data.total, - source: 'kg', - } - }) + // http://ioscdn.kugou.com/api/v3/search/special?keyword=${encodeURIComponent(text)}&page=${page}&pagesize=${limit}&showtype=10&plat=2&version=7910&correct=1&sver=5 + // http://msearchretry.kugou.com/api/v3/search/special?keyword=${encodeURIComponent(text)}&page=${page}&pagesize=${limit}&showtype=10&filter=0&version=7910&sver=2 }, } diff --git a/src/renderer/utils/musicSdk/kg/util.js b/src/renderer/utils/musicSdk/kg/util.js index 5bfbe2ff..955bac98 100644 --- a/src/renderer/utils/musicSdk/kg/util.js +++ b/src/renderer/utils/musicSdk/kg/util.js @@ -20,25 +20,18 @@ export const decodeLyric = str => new Promise((resolve, reject) => { // console.log(str) // }) - -const signatureKey = { app: 'OIlwieks28dk2k092lksi2UIkp', web: 'NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt' } /** * 签名 * @param {*} params * @param {*} apiver */ export const signatureParams = (params, apiver = 9) => { - let key = signatureKey.app - if (apiver === 5) key = signatureKey.web - - if (typeof params === 'object') { - if (!Array.isArray(params)) throw new Error('params error.') - params = params.sort() - } else if (typeof params === 'string') { - params = params.split('&').sort() - } else throw new Error('params error.') - - return toMD5(`${key}${params.join('')}${key}`) + let keyparam = 'OIlwieks28dk2k092lksi2UIkp' + if (apiver === 5) keyparam = 'NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt' + let param_list = params.split('&') + param_list.sort() + let sign_params = `${keyparam}${param_list.join('')}${keyparam}` + return toMD5(sign_params) } /** @@ -59,12 +52,9 @@ export const createHttpFetch = async(url, options, retryNum = 0) => { // console.log(result.statusCode, result.body) if (result.statusCode !== 200 || ( - (result.body.error_code !== undefined - ? result.body.error_code - : result.body.errcode !== undefined - ? result.body.errcode - : result.body.err_code - ) !== 0) + result.body.error_code ?? + result.body.errcode ?? + result.body.err_code) != 0 ) return createHttpFetch(url, options, ++retryNum) if (result.body.data) return result.body.data if (Array.isArray(result.body.info)) return result.body diff --git a/src/renderer/utils/musicSdk/mg/album.js b/src/renderer/utils/musicSdk/mg/album.js index 4223ecbb..b9b4435a 100644 --- a/src/renderer/utils/musicSdk/mg/album.js +++ b/src/renderer/utils/musicSdk/mg/album.js @@ -27,7 +27,7 @@ export default { desc: listInfo.desc, author: listInfo.author, play_count: listInfo.play_count, - } + }, } }, /** @@ -46,6 +46,6 @@ export default { author: info.singer, play_count: formatPlayCount(info.opNumItem.playNum), total: info.totalCount, - } + } }, -} \ No newline at end of file +} diff --git a/src/renderer/utils/musicSdk/mg/index.js b/src/renderer/utils/musicSdk/mg/index.js index 8cd3aa63..6fec7987 100644 --- a/src/renderer/utils/musicSdk/mg/index.js +++ b/src/renderer/utils/musicSdk/mg/index.js @@ -6,10 +6,10 @@ import pic from './pic' import lyric from './lyric' import hotSearch from './hotSearch' import comment from './comment' -import tipSearch from './tipSearch' +// import tipSearch from './tipSearch' const mg = { - tipSearch, + // tipSearch, songList, musicSearch, leaderboard, diff --git a/src/renderer/utils/musicSdk/mg/musicInfo.js b/src/renderer/utils/musicSdk/mg/musicInfo.js index 01109c16..99aeeebc 100644 --- a/src/renderer/utils/musicSdk/mg/musicInfo.js +++ b/src/renderer/utils/musicSdk/mg/musicInfo.js @@ -2,7 +2,7 @@ import { sizeFormate } from '../../index' import { createHttpFetch } from './utils' import { formatSingerName } from '../utils' -const createGetMusicInfosTask = (ids, resType = 2) => { +const createGetMusicInfosTask = (ids) => { let list = ids let tasks = [] while (list.length) { @@ -10,13 +10,13 @@ const createGetMusicInfosTask = (ids, resType = 2) => { if (list.length < 100) break list = list.slice(100) } - let url = `https://c.musicapp.migu.cn/MIGUM2.0/v1.0/content/resourceinfo.do?resourceType=${resType}` - return tasks.map(task => createHttpFetch(url, { + let url = 'https://c.musicapp.migu.cn/MIGUM2.0/v1.0/content/resourceinfo.do?resourceType=2' + return Promise.all(tasks.map(task => createHttpFetch(url, { method: 'POST', form: { resourceId: task.join('|'), }, - }).then(data => data.resource)) + }).then(data => data.resource))) } export const filterMusicInfoList = (rawList) => { @@ -92,5 +92,5 @@ export const getMusicInfo = async(copyrightId) => { } export const getMusicInfos = async(copyrightIds) => { - return filterMusicInfoList(await Promise.all(createGetMusicInfosTask(copyrightIds)).then(data => data)) + return filterMusicInfoList(await Promise.all(createGetMusicInfosTask(copyrightIds)).then(data => data.flat())) } diff --git a/src/renderer/utils/musicSdk/mg/musicSearch.js b/src/renderer/utils/musicSdk/mg/musicSearch.js index 635f6b56..3cb1b7df 100644 --- a/src/renderer/utils/musicSdk/mg/musicSearch.js +++ b/src/renderer/utils/musicSdk/mg/musicSearch.js @@ -1,10 +1,13 @@ +import { httpFetch } from '../../request' import { sizeFormate, formatPlayTime } from '../../index' -import { createHttpFetch } from './utils' -import { formatSingerName, toMD5 } from '../utils' +import { toMD5 } from '../utils' +import { formatSingerName } from '@renderer/utils/musicSdk/utils' -const searchSign = (timeStr, str, deviceId) => { - const key = '6cdc72a439cef99a3418d2a78aa28c73' - return toMD5(`${str}${key}yyapp2d16148780a1dcc7408e06336b98cfd50${deviceId}${timeStr}`) +const sign = (time, str) => { + const deviceId = '963B7AA0D21511ED807EE5846EC87D20' + const signatureMd5 = '6cdc72a439cef99a3418d2a78aa28c73' + const sign = toMD5(`${str}${signatureMd5}yyapp2d16148780a1dcc7408e06336b98cfd50${deviceId}${time}`) + return { sign, deviceId } } export default { @@ -12,115 +15,210 @@ export default { total: 0, page: 0, allPage: 1, - deviceId: '963B7AA0D21511ED807EE5846EC87D20', + + // 旧版API + // musicSearch(str, page, limit) { + // const searchRequest = httpFetch(`http://pd.musicapp.migu.cn/MIGUM2.0/v1.0/content/search_all.do?ua=Android_migu&version=5.0.1&text=${encodeURIComponent(str)}&pageNo=${page}&pageSize=${limit}&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A0%2C%22mvSong%22%3A0%2C%22songlist%22%3A0%2C%22bestShow%22%3A1%7D`, { + // searchRequest = httpFetch(`http://pd.musicapp.migu.cn/MIGUM2.0/v1.0/content/search_all.do?ua=Android_migu&version=5.0.1&text=${encodeURIComponent(str)}&pageNo=${page}&pageSize=${limit}&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A0%2C%22mvSong%22%3A0%2C%22songlist%22%3A0%2C%22bestShow%22%3A1%7D`, { + // searchRequest = httpFetch(`http://jadeite.migu.cn:7090/music_search/v2/search/searchAll?sid=4f87090d01c84984a11976b828e2b02c18946be88a6b4c47bcdc92fbd40762db&isCorrect=1&isCopyright=1&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A1%2C%22mvSong%22%3A0%2C%22bestShow%22%3A1%2C%22songlist%22%3A0%2C%22lyricSong%22%3A0%7D&pageSize=${limit}&text=${encodeURIComponent(str)}&pageNo=${page}&sort=0`, { + // searchRequest = httpFetch(`https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/search_all.do?isCopyright=1&isCorrect=1&pageNo=${page}&pageSize=${limit}&searchSwitch={%22song%22:1,%22album%22:0,%22singer%22:0,%22tagSong%22:0,%22mvSong%22:0,%22songlist%22:0,%22bestShow%22:0}&sort=0&text=${encodeURIComponent(str)}`) + // // searchRequest = httpFetch(`http://jadeite.migu.cn:7090/music_search/v2/search/searchAll?sid=4f87090d01c84984a11976b828e2b02c18946be88a6b4c47bcdc92fbd40762db&isCorrect=1&isCopyright=1&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A1%2C%22mvSong%22%3A0%2C%22bestShow%22%3A1%2C%22songlist%22%3A0%2C%22lyricSong%22%3A0%7D&pageSize=${limit}&text=${encodeURIComponent(str)}&pageNo=${page}&sort=0`, { + // headers: { + // // sign: 'c3b7ae985e2206e97f1b2de8f88691e2', + // // timestamp: 1578225871982, + // // appId: 'yyapp2', + // // mode: 'android', + // // ua: 'Android_migu', + // // version: '6.9.4', + // osVersion: 'android 7.0', + // 'User-Agent': 'okhttp/3.9.1', + // }, + // }) + // // searchRequest = httpFetch(`https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/search_all.do?isCopyright=1&isCorrect=1&pageNo=${page}&pageSize=${limit}&searchSwitch={%22song%22:1,%22album%22:0,%22singer%22:0,%22tagSong%22:0,%22mvSong%22:0,%22songlist%22:0,%22bestShow%22:0}&sort=0&text=${encodeURIComponent(str)}`) + // return searchRequest.promise.then(({ body }) => body) + // }, + // handleResult(rawData) { + // // console.log(rawData) + // let ids = new Set() + // const list = [] + // rawData.forEach(item => { + // if (ids.has(item.id)) return + // ids.add(item.id) + // const types = [] + // const _types = {} + // item.newRateFormats && item.newRateFormats.forEach(type => { + // let size + // switch (type.formatType) { + // case 'PQ': + // size = sizeFormate(type.size ?? type.androidSize) + // types.push({ type: '128k', size }) + // _types['128k'] = { + // size, + // } + // break + // case 'HQ': + // size = sizeFormate(type.size ?? type.androidSize) + // types.push({ type: '320k', size }) + // _types['320k'] = { + // size, + // } + // break + // case 'SQ': + // size = sizeFormate(type.size ?? type.androidSize) + // types.push({ type: 'flac', size }) + // _types.flac = { + // size, + // } + // break + // case 'ZQ': + // size = sizeFormate(type.size ?? type.androidSize) + // types.push({ type: 'flac24bit', size }) + // _types.flac24bit = { + // size, + // } + // break + // } + // }) + + // const albumNInfo = item.albums && item.albums.length + // ? { + // id: item.albums[0].id, + // name: item.albums[0].name, + // } + // : {} + + // list.push({ + // singer: this.getSinger(item.singers), + // name: item.name, + // albumName: albumNInfo.name, + // albumId: albumNInfo.id, + // songmid: item.songId, + // copyrightId: item.copyrightId, + // source: 'mg', + // interval: null, + // img: item.imgItems && item.imgItems.length ? item.imgItems[0].img : null, + // lrc: null, + // lrcUrl: item.lyricUrl, + // mrcUrl: item.mrcurl, + // trcUrl: item.trcUrl, + // otherSource: null, + // types, + // _types, + // typeUrl: {}, + // }) + // }) + // return list + // }, + musicSearch(str, page, limit) { - // searchRequest = httpFetch(`https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/search_all.do?isCopyright=1&isCorrect=1&pageNo=${page}&pageSize=${limit}&searchSwitch={%22song%22:1,%22album%22:0,%22singer%22:0,%22tagSong%22:0,%22mvSong%22:0,%22songlist%22:0,%22bestShow%22:0}&sort=0&text=${encodeURIComponent(str)}`) - const timeStr = Date.now().toString() - const signResult = searchSign(timeStr, str, this.deviceId) - return createHttpFetch(`https://jadeite.migu.cn/music_search/v3/search/searchAll?isCorrect=1&isCopyright=1&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A1%2C%22mvSong%22%3A0%2C%22bestShow%22%3A1%2C%22songlist%22%3A0%2C%22lyricSong%22%3A0%7D&pageSize=${limit}&text=${encodeURIComponent(str)}&pageNo=${page}&sort=0`, { + const time = Date.now().toString() + const signData = sign(time, str) + const searchRequest = httpFetch(`https://jadeite.migu.cn/music_search/v3/search/searchAll?isCorrect=1&isCopyright=1&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A1%2C%22mvSong%22%3A0%2C%22bestShow%22%3A1%2C%22songlist%22%3A0%2C%22lyricSong%22%3A0%7D&pageSize=${limit}&text=${encodeURIComponent(str)}&pageNo=${page}&sort=0`, { headers: { uiVersion: 'A_music_3.6.1', - deviceId: this.deviceId, - timestamp: timeStr, - sign: signResult, + deviceId: signData.deviceId, + timestamp: time, + sign: signData.sign, channel: '0146921', 'User-Agent': 'Mozilla/5.0 (Linux; U; Android 11.0.0; zh-cn; MI 11 Build/OPR1.170623.032) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', }, - }).then(body => body) + }) + return searchRequest.promise.then(({ body }) => body) }, - filterList(raw) { - const ids = new Set() + filterData(rawData) { + // console.log(rawData) const list = [] + const ids = new Set() - raw.forEach(item => { - if (!item.songId || !item.copyrightId || ids.has(item.copyrightId)) return - ids.add(item.copyrightId) + rawData.forEach(item => { + item.forEach(data => { + if (!data.songId || !data.copyrightId || ids.has(data.copyrightId)) return + ids.add(data.copyrightId) - const types = [] - const _types = {} - item.audioFormats && item.audioFormats.forEach(type => { - let size - switch (type.formatType) { - case 'PQ': - size = sizeFormate(type.asize ?? type.isize) - types.push({ type: '128k', size }) - _types['128k'] = { - size, - } - break - case 'HQ': - size = sizeFormate(type.asize ?? type.isize) - types.push({ type: '320k', size }) - _types['320k'] = { - size, - } - break - case 'SQ': - size = sizeFormate(type.asize ?? type.isize) - types.push({ type: 'flac', size }) - _types.flac = { - size, - } - break - case 'ZQ24': - size = sizeFormate(type.asize ?? type.isize) - types.push({ type: 'flac24bit', size }) - _types.flac24bit = { - size, - } - break - } - }) + const types = [] + const _types = {} + data.audioFormats && data.audioFormats.forEach(type => { + let size + switch (type.formatType) { + case 'PQ': + size = sizeFormate(type.asize ?? type.isize) + types.push({ type: '128k', size }) + _types['128k'] = { + size, + } + break + case 'HQ': + size = sizeFormate(type.asize ?? type.isize) + types.push({ type: '320k', size }) + _types['320k'] = { + size, + } + break + case 'SQ': + size = sizeFormate(type.asize ?? type.isize) + types.push({ type: 'flac', size }) + _types.flac = { + size, + } + break + case 'ZQ24': + size = sizeFormate(type.asize ?? type.isize) + types.push({ type: 'flac24bit', size }) + _types.flac24bit = { + size, + } + break + } + }) - let img = item.img3 || item.img2 || item.img1 || null - if (img && !/https?:/.test(item.img3)) img = 'http://d.musicapp.migu.cn' + img + let img = data.img3 || data.img2 || data.img1 || null + if (img && !/https?:/.test(data.img3)) img = 'http://d.musicapp.migu.cn' + img - list.push({ - singer: formatSingerName(item.singerList, 'name'), - name: item.name, - albumName: item.album, - albumId: item.albumId, - songmid: item.songId, - copyrightId: item.copyrightId, - source: 'mg', - interval: formatPlayTime(item.duration), - img, - lrc: null, - lrcUrl: item.lrcUrl, - mrcUrl: item.mrcurl, - trcUrl: item.trcUrl, - types, - _types, - typeUrl: {}, + list.push({ + singer: formatSingerName(data.singerList), + name: data.name, + albumName: data.album, + albumId: data.albumId, + songmid: data.songId, + copyrightId: data.copyrightId, + source: 'mg', + interval: formatPlayTime(data.duration), + img, + lrc: null, + lrcUrl: data.lrcUrl, + mrcUrl: data.mrcurl, + trcUrl: data.trcUrl, + types, + _types, + typeUrl: {}, + }) }) }) - return list }, - handleResult(rawData) { - return this.filterList(rawData.flat()) - }, search(str, page = 1, limit, retryNum = 0) { if (++retryNum > 3) return Promise.reject(new Error('try max num')) if (limit == null) limit = this.limit + // http://newlyric.kuwo.cn/newlyric.lrc?62355680 + return this.musicSearch(str, page, limit).then(result => { + // console.log(result) + if (!result || result.code !== '000000') return Promise.reject(new Error(result ? result.info : '搜索失败')) + const songResultData = result.songResultData || { resultList: [], totalCount: 0 } - return this.musicSearch(str, page, limit).then(data => { - const songResultData = data.songResultData || { resultList: [], totalCount: 0 } - - let list = this.handleResult(songResultData.resultList) + let list = this.filterData(songResultData.resultList) if (list == null) return this.search(str, page, limit, retryNum) this.total = parseInt(songResultData.totalCount) this.page = page this.allPage = Math.ceil(this.total / limit) - return Promise.resolve({ + return { list, allPage: this.allPage, limit, total: this.total, source: 'mg', - }) + } }) }, } diff --git a/src/renderer/utils/musicSdk/tx/index.js b/src/renderer/utils/musicSdk/tx/index.js index 1a5bcc1d..beb52af0 100644 --- a/src/renderer/utils/musicSdk/tx/index.js +++ b/src/renderer/utils/musicSdk/tx/index.js @@ -5,10 +5,10 @@ import musicSearch from './musicSearch' import { apis } from '../api-source' import hotSearch from './hotSearch' import comment from './comment' -import tipSearch from './tipSearch' +// import tipSearch from './tipSearch' const tx = { - tipSearch, + // tipSearch, leaderboard, songList, musicSearch, diff --git a/src/renderer/utils/musicSdk/tx/tipSearch.js b/src/renderer/utils/musicSdk/tx/tipSearch.js index bd3506f8..a844da9e 100644 --- a/src/renderer/utils/musicSdk/tx/tipSearch.js +++ b/src/renderer/utils/musicSdk/tx/tipSearch.js @@ -1,27 +1,29 @@ import { httpFetch } from '../../request' export default { + // regExps: { + // relWord: /RELWORD=(.+)/, + // }, requestObj: null, - cancelTipSearch() { - if (this.requestObj && this.requestObj.cancelHttp) this.requestObj.cancelHttp() - }, - tipSearchBySong(str) { + tipSearch(str) { this.cancelTipSearch() - this.requestObj = httpFetch(`https://c.y.qq.com/splcloud/fcgi-bin/smartbox_new.fcg?&format=json&inCharset=utf-8&outCharset=utf-8¬ice=0&platform=yqq.json&needNewCode=1&uin=0&hostUin=0&is_xml=0&key=${encodeURIComponent(str)}`, { + this.requestObj = httpFetch(`https://c.y.qq.com/splcloud/fcgi-bin/smartbox_new.fcg?is_xml=0&format=json&key=${encodeURIComponent(str)}&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0`, { headers: { - origin: 'https://y.qq.com/', - referer: 'https://y.qq.com/', + Referer: 'https://y.qq.com/portal/player.html', }, }) return this.requestObj.promise.then(({ statusCode, body }) => { if (statusCode != 200 || body.code != 0) return Promise.reject(new Error('请求失败')) - return body.data.song.itemlist + return body.data }) }, handleResult(rawData) { return rawData.map(info => `${info.name} - ${info.singer}`) }, + cancelTipSearch() { + if (this.requestObj && this.requestObj.cancelHttp) this.requestObj.cancelHttp() + }, async search(str) { - return this.tipSearchBySong(str).then(result => this.handleResult(result)) + return this.tipSearch(str).then(result => this.handleResult(result.song.itemlist)) }, } diff --git a/src/renderer/utils/musicSdk/utils.js b/src/renderer/utils/musicSdk/utils.js index 1b90b593..756cf880 100644 --- a/src/renderer/utils/musicSdk/utils.js +++ b/src/renderer/utils/musicSdk/utils.js @@ -22,7 +22,6 @@ export const getHostIp = hostname => { }) } - export const dnsLookup = (hostname, options, callback) => { const result = getHostIp(hostname) if (result) return callback(null, result.address, result.family) @@ -34,24 +33,24 @@ export const dnsLookup = (hostname, options, callback) => { /** * 格式化歌手 * @param singers 歌手数组 - * @param obj 读取的数据 - * @param join 插入的字符 + * @param nameKey 歌手名键值 + * @param join 歌手分割字符 */ -export const formatSingerName = (singers, obj = 'name', join = '、') => { - if (typeof singers === 'string') { +export const formatSingerName = (singers, nameKey = 'name', join = '、') => { + if (typeof singers == 'string') { try { singers = JSON.parse(singers) } catch (err) { return decodeName(singers) } + } else if (Array.isArray(singers)) { + const singer = [] + singers.forEach(item => { + let name = item[nameKey] + if (!name) return + singer.push(name) + }) + return decodeName(singer.join(join)) } - if (!Array.isArray(singers)) return decodeName(singers) - - const singer = [] - singers.forEach(item => { - let name = item[obj] - if (!name) return - singer.push(name) - }) - return decodeName(singer.join(join)) + return decodeName(String(singers ?? '')) } diff --git a/src/renderer/utils/musicSdk/wy/index.js b/src/renderer/utils/musicSdk/wy/index.js index 95217c9c..a28295f5 100644 --- a/src/renderer/utils/musicSdk/wy/index.js +++ b/src/renderer/utils/musicSdk/wy/index.js @@ -6,10 +6,10 @@ import musicSearch from './musicSearch' import songList from './songList' import hotSearch from './hotSearch' import comment from './comment' -import tipSearch from './tipSearch' +// import tipSearch from './tipSearch' const wy = { - tipSearch, + // tipSearch, leaderboard, musicSearch, songList,