From be4b1a372b1fda7cd5cb1cb639c28bbfada0c10d Mon Sep 17 00:00:00 2001 From: ikun0014 Date: Wed, 9 Oct 2024 22:44:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20wyy=E6=AF=8D=E5=B8=A6=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E6=97=A0=E5=A4=A7=E5=B0=8F=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/musicSdk/wy/musicDetail.js | 228 +++++++++++++++------------ src/utils/musicSdk/wy/musicSearch.js | 155 +++++++++--------- src/utils/musicSdk/wy/songList.js | 90 ++--------- 3 files changed, 216 insertions(+), 257 deletions(-) diff --git a/src/utils/musicSdk/wy/musicDetail.js b/src/utils/musicSdk/wy/musicDetail.js index 4fd4e9b..588e9c7 100644 --- a/src/utils/musicSdk/wy/musicDetail.js +++ b/src/utils/musicSdk/wy/musicDetail.js @@ -1,118 +1,142 @@ import { httpFetch } from '../../request' import { weapi } from './utils/crypto' import { formatPlayTime, sizeFormate } from '../../index' -// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/song_detail.js export default { getSinger(singers) { - let arr = [] - singers?.forEach(singer => { - arr.push(singer.name) - }) - return arr.join('、') + try { + const arr = singers.map(singer => singer.name) + return arr.join('、') + } catch (err) { + console.error('getSinger error:', err) + return '' + } }, - filterList({ songs, privileges }) { - // console.log(songs, privileges) - const list = [] - songs.forEach((item, index) => { - const types = [] - const _types = {} - let size - let privilege = privileges[index] - if (privilege.id !== item.id) privilege = privileges.find(p => p.id === item.id) - if (!privilege) return - if (privilege.chargeInfoList.length > 4 && privilege.chargeInfoList[4].rate == 1999000 && privilege.chargeInfoList[4].chargeType == 1) { - types.push({ type: 'master' }) - _types.master = { - size: null, - } - } - if (privilege.maxBrLevel == 'hires') { - size = item.hr ? sizeFormate(item.hr.size) : null - types.push({ type: 'flac24bit', size }) - _types.flac24bit = { - size, - } - } - switch (privilege.maxbr) { - case 999000: - size = item.sq ? sizeFormate(item.sq.size) : null - types.push({ type: 'flac', size }) - _types.flac = { - size, - } - case 320000: - size = item.h ? sizeFormate(item.h.size) : null - types.push({ type: '320k', size }) - _types['320k'] = { - size, - } - case 192000: - case 128000: - size = item.l ? sizeFormate(item.l.size) : null - types.push({ type: '128k', size }) - _types['128k'] = { - size, - } - } + async fetchSongDetails(songId) { + try { + const requestObj = httpFetch(`https://music.163.com/api/song/music/detail/get?songId=${songId}`, { + method: 'get', + headers: { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64)', + origin: 'https://music.163.com', + }, + }) - types.reverse() + const { body, statusCode } = await requestObj.promise + if (statusCode !== 200 || body.code !== 200) throw new Error('获取歌曲音质信息失败') - if (item.pc) { - list.push({ - singer: item.pc.ar ?? '', - name: item.pc.sn ?? '', - albumName: item.pc.alb ?? '', - albumId: item.al?.id, - source: 'wy', - interval: formatPlayTime(item.dt / 1000), - songmid: item.id, - img: item.al?.picUrl ?? '', - lrc: null, - otherSource: null, - types, - _types, - typeUrl: {}, - }) - } else { - list.push({ - singer: this.getSinger(item.ar), - name: item.name ?? '', - albumName: item.al?.name, - albumId: item.al?.id, - source: 'wy', - interval: formatPlayTime(item.dt / 1000), - songmid: item.id, - img: item.al?.picUrl, - lrc: null, - otherSource: null, - types, - _types, - typeUrl: {}, - }) - } - }) - // console.log(list) - return list + return body + } catch (err) { + console.error('Error fetching song details:', err) + return null + } }, + + async filterList({ songs, privileges }) { + try { + const list = await Promise.all( + songs.map(async(item, index) => { + const types = [] + const _types = {} + let size + let privilege = privileges[index] + + if (privilege.id !== item.id) { + privilege = privileges.find(p => p.id === item.id) + } + + if (!privilege) return + + const songDetails = await this.fetchSongDetails(privilege.id) + if (songDetails) { + const jmSize = songDetails.data.jm?.size + if (jmSize) { + size = sizeFormate(jmSize) + types.push({ type: 'master', size }) + _types.master = { size } + } + + if (privilege.maxBrLevel === 'hires') { + size = item.hr ? sizeFormate(item.hr.size) : null + types.push({ type: 'flac24bit', size }) + _types.flac24bit = { size } + } + + switch (privilege.maxbr) { + case 999000: + size = item.sq ? sizeFormate(item.sq.size) : null + types.push({ type: 'flac', size }) + _types.flac = { size } + case 320000: + size = item.h ? sizeFormate(item.h.size) : null + types.push({ type: '320k', size }) + _types['320k'] = { size } + case 192000: + case 128000: + size = item.l ? sizeFormate(item.l.size) : null + types.push({ type: '128k', size }) + _types['128k'] = { size } + } + + types.reverse() + } + + const song = { + singer: this.getSinger(item.ar), + name: item.name ?? '', + albumName: item.al?.name, + albumId: item.al?.id, + source: 'wy', + interval: formatPlayTime(item.dt / 1000), + songmid: item.id, + img: item.al?.picUrl, + lrc: null, + otherSource: null, + types, + _types, + typeUrl: {}, + } + + return song + }), + ) + + return list.filter(item => item !== undefined) + } catch (err) { + console.error('filterList error:', err) + return [] + } + }, + async getList(ids = [], retryNum = 0) { if (retryNum > 2) return Promise.reject(new Error('try max num')) - const requestObj = httpFetch('https://music.163.com/weapi/v3/song/detail', { - method: 'post', - headers: { - 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36', - origin: 'https://music.163.com', - }, - form: weapi({ - c: '[' + ids.map(id => ('{"id":' + id + '}')).join(',') + ']', - ids: '[' + ids.join(',') + ']', - }), - }) - const { body, statusCode } = await requestObj.promise - if (statusCode != 200 || body.code !== 200) throw new Error('获取歌曲详情失败') - // console.log(body) - return { source: 'wy', list: this.filterList(body) } + try { + const requestObj = httpFetch('https://music.163.com/weapi/v3/song/detail', { + method: 'post', + headers: { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36', + origin: 'https://music.163.com', + }, + form: weapi({ + c: '[' + ids.map(id => `{"id":${id}}`).join(',') + ']', + ids: '[' + ids.join(',') + ']', + }), + }) + + const { body, statusCode } = await requestObj.promise + if (statusCode !== 200 || body.code !== 200) throw new Error('获取歌曲详情失败') + + const result = await this.filterList(body) + return { source: 'wy', list: result } + } catch (err) { + console.error('getList error:', err) + if (retryNum < 2) { + return this.getList(ids, retryNum + 1) + } else { + throw new Error('Failed after retrying') + } + } }, } diff --git a/src/utils/musicSdk/wy/musicSearch.js b/src/utils/musicSdk/wy/musicSearch.js index fc42caa..ac9a534 100644 --- a/src/utils/musicSdk/wy/musicSearch.js +++ b/src/utils/musicSdk/wy/musicSearch.js @@ -1,7 +1,5 @@ -// import { httpFetch } from '../../request' -// import { weapi } from './utils/crypto' +import { httpFetch } from '../../request' import { sizeFormate, formatPlayTime } from '../../index' -// import musicDetailApi from './musicDetail' import { eapiRequest } from './utils/index' export default { @@ -9,6 +7,7 @@ export default { total: 0, page: 0, allPage: 1, + musicSearch(str, page, limit) { const searchRequest = eapiRequest('/api/cloudsearch/pc', { s: str, @@ -19,97 +18,103 @@ export default { }) return searchRequest.promise.then(({ body }) => body) }, + getSinger(singers) { - let arr = [] - singers.forEach(singer => { - arr.push(singer.name) - }) - return arr.join('、') + return singers.map(singer => singer.name).join('、') }, + handleResult(rawList) { - // console.log(rawList) if (!rawList) return [] - return rawList.map(item => { + + return Promise.all(rawList.map(async item => { const types = [] const _types = {} let size - if (item.privilege.chargeInfoList.length > 4 && item.privilege.chargeInfoList[4].rate == 1999000 && item.privilege.chargeInfoList[4].chargeType == 1) { - types.push({ type: 'master' }) - _types.master = { - size: null, - } - } - if (item.privilege.maxBrLevel == 'hires') { - size = item.hr ? sizeFormate(item.hr.size) : null - types.push({ type: 'flac24bit', size }) - _types.flac24bit = { - size, - } - } - switch (item.privilege.maxbr) { - case 999000: - size = item.sq ? sizeFormate(item.sq.size) : null - types.push({ type: 'flac', size }) - _types.flac = { - size, - } - case 320000: - size = item.h ? sizeFormate(item.h.size) : null - types.push({ type: '320k', size }) - _types['320k'] = { - size, - } - case 192000: - case 128000: - size = item.l ? sizeFormate(item.l.size) : null - types.push({ type: '128k', size }) - _types['128k'] = { - size, - } - } + try { + const requestObj = httpFetch(`https://music.163.com/api/song/music/detail/get?songId=${item.id}`, { + method: 'get', + headers: { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36', + origin: 'https://music.163.com', + }, + }) - types.reverse() + const { body, statusCode } = await requestObj.promise - return { - singer: this.getSinger(item.ar), - name: item.name, - albumName: item.al.name, - albumId: item.al.id, - source: 'wy', - interval: formatPlayTime(item.dt / 1000), - songmid: item.id, - img: item.al.picUrl, - lrc: null, - types, - _types, - typeUrl: {}, + if (statusCode !== 200 || !body || body.code !== 200) { + throw new Error('Failed to get song quality information') + } + + if (body.data.jm && body.data.jm.size) { + size = sizeFormate(body.data.jm.size) + types.push({ type: 'master', size }) + _types.master = { size } + } + if (item.privilege.maxBrLevel == 'hires') { + size = item.hr ? sizeFormate(item.hr.size) : null + types.push({ type: 'flac24bit', size }) + _types.flac24bit = { size } + } + switch (item.privilege.maxbr) { + case 999000: + size = item.sq ? sizeFormate(item.sq.size) : null + types.push({ type: 'flac', size }) + _types.flac = { size } + case 320000: + size = item.h ? sizeFormate(item.h.size) : null + types.push({ type: '320k', size }) + _types['320k'] = { size } + case 192000: + case 128000: + size = item.l ? sizeFormate(item.l.size) : null + types.push({ type: '128k', size }) + _types['128k'] = { size } + } + + types.reverse() + + return { + singer: this.getSinger(item.ar), + name: item.name, + albumName: item.al.name, + albumId: item.al.id, + source: 'wy', + interval: formatPlayTime(item.dt / 1000), + songmid: item.id, + img: item.al.picUrl, + lrc: null, + types, + _types, + typeUrl: {}, + } + } catch (error) { + console.error(error.message) + return null } - }) + })) }, + search(str, page = 1, limit, retryNum = 0) { if (++retryNum > 3) return Promise.reject(new Error('try max num')) if (limit == null) limit = this.limit return this.musicSearch(str, page, limit).then(result => { - // console.log(result) if (!result || result.code !== 200) return this.search(str, page, limit, retryNum) - let list = this.handleResult(result.result.songs || []) - // console.log(list) + return this.handleResult(result.result.songs || []).then(list => { + if (!list || list.length === 0) return this.search(str, page, limit, retryNum) - if (list == null) return this.search(str, page, limit, retryNum) + this.total = result.result.songCount || 0 + this.page = page + this.allPage = Math.ceil(this.total / this.limit) - this.total = result.result.songCount || 0 - this.page = page - this.allPage = Math.ceil(this.total / this.limit) - - return { - list, - allPage: this.allPage, - limit: this.limit, - total: this.total, - source: 'wy', - } - // return result.data + return { + list, + allPage: this.allPage, + limit: this.limit, + total: this.total, + source: 'wy', + } + }) }) }, } diff --git a/src/utils/musicSdk/wy/songList.js b/src/utils/musicSdk/wy/songList.js index f6ee0cf..d02ee4d 100644 --- a/src/utils/musicSdk/wy/songList.js +++ b/src/utils/musicSdk/wy/songList.js @@ -1,11 +1,6 @@ -// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/playlist_catlist.js -// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/playlist_hot.js -// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/top_playlist.js -// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/playlist_detail.js - import { weapi, linuxapi } from './utils/crypto' import { httpFetch } from '../../request' -import { formatPlayTime, sizeFormate, dateFormat, formatPlayCount } from '../../index' +import { formatPlayTime, dateFormat, formatPlayCount } from '../../index' import musicDetailApi from './musicDetail' import { eapiRequest } from './utils/index' import { formatSingerName } from '../utils' @@ -21,14 +16,8 @@ export default { sortList: [ { name: '最热', - tid: 'hot', id: 'hot', }, - // { - // name: '最新', - // tid: 'new', - // id: 'new', - // }, ], regExps: { listDetailLink: /^.+(?:\?|&)id=(\d+)(?:&.*$|#.*$|$)/, @@ -39,9 +28,9 @@ export default { if (retryNum > 2) throw new Error('link try max num') const requestObj_listDetailLink = httpFetch(link) - const { url, statusCode } = await requestObj_listDetailLink.promise - // console.log(headers) + const { headers: { location }, statusCode } = await requestObj_listDetailLink.promise if (statusCode > 400) return this.handleParseId(link, ++retryNum) + const url = location == null ? link : location return this.regExps.listDetailLink.test(url) ? url.replace(this.regExps.listDetailLink, '$1') : url.replace(this.regExps.listDetailLink2, '$1') @@ -62,12 +51,11 @@ export default { } else { id = await this.handleParseId(id) } - // console.log(id) } return { id, cookie } }, - async getListDetail(rawId, page, tryNum = 0) { // 获取歌曲列表内的音乐 - if (tryNum > 2) return Promise.reject(new Error('try max num')) + async getListDetail(rawId, page, tryNum = 0) { + if (tryNum > 1000) return Promise.reject(new Error('try max num')) const { id, cookie } = await this.getListId(rawId) if (cookie) this.cookie = cookie @@ -78,8 +66,6 @@ export default { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36', Cookie: this.cookie, }, - credentials: 'omit', - cache: 'default', form: linuxapi({ method: 'POST', url: 'https://music.163.com/api/v3/playlist/detail', @@ -94,7 +80,6 @@ export default { if (statusCode !== 200 || body.code !== this.successCode) return this.getListDetail(id, page, ++tryNum) let limit = 1000 let rangeStart = (page - 1) * limit - // console.log(body) let list if (body.playlist.trackIds.length == body.privileges.length) { list = this.filterListDetail(body) @@ -110,7 +95,6 @@ export default { } } } - // console.log(list) return { list, page, @@ -126,53 +110,11 @@ export default { }, } }, - filterListDetail({ playlist: { tracks }, privileges }) { - // console.log(tracks, privileges) + filterListDetail({ playlist: { tracks } }) { const list = [] - tracks.forEach((item, index) => { + tracks.forEach((item) => { const types = [] const _types = {} - let size - let privilege = privileges[index] - if (privilege.id !== item.id) privilege = privileges.find(p => p.id === item.id) - if (!privilege) return - - if (privilege.chargeInfoList.length > 4 && privilege.chargeInfoList[4].rate == 1999000 && privilege.chargeInfoList[4].chargeType == 1) { - types.push({ type: 'master' }) - _types.master = { - size: null, - } - } - if (privilege.maxBrLevel == 'hires') { - size = item.hr ? sizeFormate(item.hr.size) : null - types.push({ type: 'flac24bit', size }) - _types.flac24bit = { - size, - } - } - switch (privilege.maxbr) { - case 999000: - size = null - types.push({ type: 'flac', size }) - _types.flac = { - size, - } - case 320000: - size = item.h ? sizeFormate(item.h.size) : null - types.push({ type: '320k', size }) - _types['320k'] = { - size, - } - case 192000: - case 128000: - size = item.l ? sizeFormate(item.l.size) : null - types.push({ type: '128k', size }) - _types['128k'] = { - size, - } - } - - types.reverse() if (item.pc) { list.push({ @@ -211,22 +153,20 @@ export default { return list }, - // 获取列表数据 getList(sortId, tagId, page, tryNum = 0) { if (tryNum > 2) return Promise.reject(new Error('try max num')) if (this._requestObj_list) this._requestObj_list.cancelHttp() this._requestObj_list = httpFetch('https://music.163.com/weapi/playlist/list', { method: 'post', form: weapi({ - cat: tagId || '全部', // 全部,华语,欧美,日语,韩语,粤语,小语种,流行,摇滚,民谣,电子,舞曲,说唱,轻音乐,爵士,乡村,R&B/Soul,古典,民族,英伦,金属,朋克,蓝调,雷鬼,世界音乐,拉丁,另类/独立,New Age,古风,后摇,Bossa Nova,清晨,夜晚,学习,工作,午休,下午茶,地铁,驾车,运动,旅行,散步,酒吧,怀旧,清新,浪漫,性感,伤感,治愈,放松,孤独,感动,兴奋,快乐,安静,思念,影视原声,ACG,儿童,校园,游戏,70后,80后,90后,网络歌曲,KTV,经典,翻唱,吉他,钢琴,器乐,榜单,00后 - order: sortId, // hot,new + cat: tagId || '全部', + order: sortId, limit: this.limit_list, offset: this.limit_list * (page - 1), total: true, }), }) return this._requestObj_list.promise.then(({ body }) => { - // console.log(body) if (body.code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum) return { list: this.filterList(body.playlists), @@ -238,7 +178,6 @@ export default { }) }, filterList(rawData) { - // console.log(rawData) return rawData.map(item => ({ play_count: formatPlayCount(item.playCount), id: String(item.id), @@ -253,7 +192,6 @@ export default { })) }, - // 获取标签 getTag(tryNum = 0) { if (this._requestObj_tags) this._requestObj_tags.cancelHttp() if (tryNum > 2) return Promise.reject(new Error('try max num')) @@ -262,7 +200,6 @@ export default { form: weapi({}), }) return this._requestObj_tags.promise.then(({ body }) => { - // console.log(JSON.stringify(body)) if (body.code !== this.successCode) return this.getTag(++tryNum) return this.filterTagInfo(body) }) @@ -291,7 +228,6 @@ export default { return list }, - // 获取热门标签 getHotTag(tryNum = 0) { if (this._requestObj_hotTags) this._requestObj_hotTags.cancelHttp() if (tryNum > 2) return Promise.reject(new Error('try max num')) @@ -300,7 +236,6 @@ export default { form: weapi({}), }) return this._requestObj_hotTags.promise.then(({ body }) => { - // console.log(JSON.stringify(body)) if (body.code !== this.successCode) return this.getTag(++tryNum) return this.filterHotTagInfo(body.tags) }) @@ -325,14 +260,13 @@ export default { search(text, page, limit = 20) { return eapiRequest('/api/cloudsearch/pc', { s: text, - type: 1000, // 1: 单曲, 10: 专辑, 100: 歌手, 1000: 歌单, 1002: 用户, 1004: MV, 1006: 歌词, 1009: 电台, 1014: 视频 + type: 1000, limit, total: page == 1, offset: limit * (page - 1), }) .promise.then(({ body }) => { if (body.code != this.successCode) throw new Error('filed') - // console.log(body) return { list: this.filterList(body.result.playlists), limit, @@ -342,7 +276,3 @@ export default { }) }, } - -// getList -// getTags -// getListDetail