存档:TS 重构前基线(lx 聚合源 = isolated-vm 加载器,签名方案已移除)
This commit is contained in:
commit
f289df4cd7
124 files changed
+14110
No files matched your search
@@ -0,0 +1,51 @@
|
||||
import { createHttpFetch } from './utils/index.js'
|
||||
import { filterMusicInfoList } from './musicInfo.js'
|
||||
import { formatPlayCount } from '../index.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 通过AlbumId获取专辑
|
||||
* @param {*} id
|
||||
* @param {*} page
|
||||
*/
|
||||
async getAlbumDetail(id, page = 1) {
|
||||
const list = await createHttpFetch(`http://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/queryAlbumSong?albumId=${id}&pageNo=${page}`)
|
||||
if (!list.songList) return Promise.reject(new Error('Get album list error.'))
|
||||
|
||||
const songList = filterMusicInfoList(list.songList)
|
||||
const listInfo = await this.getAlbumInfo(id)
|
||||
|
||||
return {
|
||||
list: songList || [],
|
||||
page,
|
||||
limit: listInfo.total,
|
||||
total: listInfo.total,
|
||||
source: 'mg',
|
||||
info: {
|
||||
name: listInfo.name,
|
||||
img: listInfo.image,
|
||||
desc: listInfo.desc,
|
||||
author: listInfo.author,
|
||||
play_count: listInfo.play_count,
|
||||
},
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 通过AlbumId获取专辑信息
|
||||
* @param {*} id
|
||||
* @param {*} page
|
||||
*/
|
||||
async getAlbumInfo(id) {
|
||||
const info = await createHttpFetch(`https://app.c.nf.migu.cn/MIGUM3.0/resource/album/v2.0?albumId=${id}`)
|
||||
if (!info) return Promise.reject(new Error('Get album info error.'))
|
||||
|
||||
return {
|
||||
name: info.title,
|
||||
image: info.imgItems.length ? info.imgItems[0].img : null,
|
||||
desc: info.summary,
|
||||
author: info.singer,
|
||||
play_count: formatPlayCount(info.opNumItem.playNum),
|
||||
total: info.totalCount,
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { httpFetch } from '../request.js'
|
||||
import { requestMsg } from '../message.js'
|
||||
import { headers, timeout } from '../options.js'
|
||||
import { dnsLookup } from '../utils.js'
|
||||
|
||||
const api_test = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFetch(`http://ts.tempmusics.tk/url/mg/${songInfo.copyrightId}/${type}`, {
|
||||
method: 'get',
|
||||
timeout,
|
||||
headers,
|
||||
lookup: dnsLookup,
|
||||
family: 4,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ statusCode, body }) => {
|
||||
if (statusCode == 429) return Promise.reject(new Error(requestMsg.tooManyRequests))
|
||||
switch (body.code) {
|
||||
case 0: return Promise.resolve({ type, url: body.data })
|
||||
default: return Promise.reject(new Error(requestMsg.fail))
|
||||
}
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
|
||||
export default api_test
|
||||
@@ -0,0 +1,96 @@
|
||||
import { httpFetch } from '../request.js'
|
||||
import getSongId from './songId.js'
|
||||
import { dateFormat2 } from '../index.js'
|
||||
|
||||
export default {
|
||||
_requestObj: null,
|
||||
_requestObj2: null,
|
||||
_requestObj3: null,
|
||||
lastCommentIds: new Map(),
|
||||
async getComment(musicInfo, page = 1, limit = 20) {
|
||||
if (this._requestObj) this._requestObj.cancelHttp()
|
||||
if (!musicInfo.songId) {
|
||||
let id = await getSongId(musicInfo)
|
||||
if (!id) throw new Error('获取评论失败')
|
||||
musicInfo.songId = id
|
||||
}
|
||||
if (page === 1) this.lastCommentIds.clear()
|
||||
const lastCommentId = this.lastCommentIds.get(String(page)) || ''
|
||||
if (!lastCommentId && page > 1) throw new Error('获取评论失败')
|
||||
// const _requestObj = httpFetch(`https://music.migu.cn/v3/api/comment/listComments?targetId=${musicInfo.songId}&pageSize=${limit}&pageNo=${page}`, {
|
||||
const _requestObj = httpFetch(`https://app.c.nf.migu.cn/MIGUM3.0/user/comment/stack/v1.0?pageSize=${limit}&queryType=1&resourceId=${musicInfo.songId}&resourceType=2&commentId=${lastCommentId}`, {
|
||||
headers: {
|
||||
'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',
|
||||
// Referer: 'https://music.migu.cn',
|
||||
},
|
||||
})
|
||||
const { body, statusCode } = await _requestObj.promise
|
||||
// console.log(body)
|
||||
if (statusCode != 200 || body.code !== '000000') throw new Error('获取评论失败')
|
||||
const total = parseInt(body.data.commentNums)
|
||||
const list = this.filterComment(body.data.comments)
|
||||
this.lastCommentIds.set(String(page + 1), list.length ? list[list.length - 1].id : '')
|
||||
return { source: 'mg', comments: list, total, page, limit, maxPage: Math.ceil(total / limit) || 1 }
|
||||
},
|
||||
async getHotComment(musicInfo, page = 1, limit = 20) {
|
||||
if (this._requestObj2) this._requestObj2.cancelHttp()
|
||||
|
||||
if (!musicInfo.songId) {
|
||||
let id = await getSongId(musicInfo)
|
||||
if (!id) throw new Error('获取评论失败')
|
||||
musicInfo.songId = id
|
||||
}
|
||||
|
||||
// const _requestObj2 = httpFetch(`https://music.migu.cn/v3/api/comment/listTopComments?targetId=${musicInfo.songId}&pageSize=${limit}&pageNo=${page}`, {
|
||||
const _requestObj2 = httpFetch(`https://app.c.nf.migu.cn/MIGUM3.0/user/comment/stack/v1.0?pageSize=${limit}&queryType=2&resourceId=${musicInfo.songId}&resourceType=2&hotCommentStart=${(page - 1) * limit}`, {
|
||||
headers: {
|
||||
'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',
|
||||
// Referer: 'https://music.migu.cn',
|
||||
},
|
||||
})
|
||||
const { body, statusCode } = await _requestObj2.promise
|
||||
// console.log(body)
|
||||
if (statusCode != 200 || body.code !== '000000') throw new Error('获取热门评论失败')
|
||||
const total = parseInt(body.data.cfgHotCount)
|
||||
return { source: 'mg', comments: this.filterComment(body.data.hotComments), total, page, limit, maxPage: Math.ceil(total / limit) || 1 }
|
||||
},
|
||||
async getReplyComment(musicInfo, replyId, page = 1, limit = 10) {
|
||||
if (this._requestObj2) this._requestObj2.cancelHttp()
|
||||
|
||||
// const _requestObj2 = httpFetch(`https://music.migu.cn/v3/api/comment/listCommentsById?commentId=${replyId}&pageSize=${limit}&pageNo=${page}`, {
|
||||
const _requestObj2 = httpFetch(`https://app.c.nf.migu.cn/MIGUM3.0/user/comment/stack/${replyId}/v1.0?pageSize=${limit}&queryType=2&resourceId=${musicInfo.songId}&resourceType=2&start=${(page - 1) * limit}`, {
|
||||
headers: {
|
||||
'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',
|
||||
},
|
||||
})
|
||||
const { body, statusCode } = await _requestObj2.promise
|
||||
// console.log(body)
|
||||
if (statusCode != 200 || body.code !== '000000') throw new Error('获取回复评论失败')
|
||||
const total = parseInt(body.data.replyTotalCount)
|
||||
return { source: 'mg', comments: this.filterComment(body.data.mainCommentItem.replyComments), total, page, limit, maxPage: Math.ceil(total / limit) || 1 }
|
||||
},
|
||||
filterComment(rawList) {
|
||||
return rawList.map(item => ({
|
||||
id: item.commentId,
|
||||
text: item.commentInfo,
|
||||
time: item.commentTime,
|
||||
timeStr: dateFormat2(new Date(item.commentTime).getTime()),
|
||||
userName: item.user.nickName,
|
||||
avatar: item.user.middleIcon || item.user.bigIcon || item.user.smallIcon,
|
||||
userId: item.user.userId,
|
||||
likedCount: item.opNumItem.thumbNum,
|
||||
replyNum: item.replyTotalCount,
|
||||
reply: item.replyComments.map(c => ({
|
||||
id: c.replyId,
|
||||
text: c.replyInfo,
|
||||
time: c.replyTime,
|
||||
timeStr: dateFormat2(new Date(c.replyTime).getTime()),
|
||||
userName: c.user.nickName,
|
||||
avatar: c.user.middleIcon || c.user.bigIcon || c.user.smallIcon,
|
||||
userId: c.user.userId,
|
||||
likedCount: null,
|
||||
replyNum: null,
|
||||
})),
|
||||
}))
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { httpFetch } from '../request.js'
|
||||
|
||||
export default {
|
||||
_requestObj: null,
|
||||
async getList(retryNum = 0) {
|
||||
if (this._requestObj) this._requestObj.cancelHttp()
|
||||
if (retryNum > 2) return Promise.reject(new Error('try max num'))
|
||||
|
||||
const _requestObj = httpFetch('http://jadeite.migu.cn:7090/music_search/v3/search/hotword')
|
||||
const { body, statusCode } = await _requestObj.promise
|
||||
if (statusCode != 200 || body.code !== '000000') throw new Error('获取热搜词失败')
|
||||
// console.log(body, statusCode)
|
||||
return { source: 'mg', list: this.filterList(body.data.hotwords[0].hotwordList) }
|
||||
},
|
||||
filterList(rawList) {
|
||||
return rawList.filter(item => item.resourceType == 'song').map(item => item.word)
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { apis } from '../api-source.js'
|
||||
import leaderboard from './leaderboard.js'
|
||||
import songList from './songList.js'
|
||||
import musicSearch from './musicSearch.js'
|
||||
import pic from './pic.js'
|
||||
import lyric from './lyric.js'
|
||||
import hotSearch from './hotSearch.js'
|
||||
import comment from './comment.js'
|
||||
// import tipSearch from './tipSearch.js'
|
||||
|
||||
const mg = {
|
||||
// tipSearch,
|
||||
songList,
|
||||
musicSearch,
|
||||
leaderboard,
|
||||
hotSearch,
|
||||
comment,
|
||||
getMusicUrl(songInfo, type) {
|
||||
return apis('mg').getMusicUrl(songInfo, type)
|
||||
},
|
||||
getLyric(songInfo) {
|
||||
return lyric.getLyric(songInfo)
|
||||
},
|
||||
getPic(songInfo) {
|
||||
return pic.getPic(songInfo)
|
||||
},
|
||||
getMusicDetailPageUrl(songInfo) {
|
||||
return `http://music.migu.cn/v3/music/song/${songInfo.copyrightId}`
|
||||
},
|
||||
}
|
||||
|
||||
export default mg
|
||||
@@ -0,0 +1,243 @@
|
||||
import { httpFetch } from '../request.js'
|
||||
import { filterMusicInfoList } from './musicInfo.js'
|
||||
|
||||
// const boardList = [{ id: 'mg__27553319', name: '咪咕尖叫新歌榜', bangid: '27553319' }, { id: 'mg__27186466', name: '咪咕尖叫热歌榜', bangid: '27186466' }, { id: 'mg__27553408', name: '咪咕尖叫原创榜', bangid: '27553408' }, { id: 'mg__23189800', name: '咪咕港台榜', bangid: '23189800' }, { id: 'mg__23189399', name: '咪咕内地榜', bangid: '23189399' }, { id: 'mg__19190036', name: '咪咕欧美榜', bangid: '19190036' }, { id: 'mg__23189813', name: '咪咕日韩榜', bangid: '23189813' }, { id: 'mg__23190126', name: '咪咕彩铃榜', bangid: '23190126' }, { id: 'mg__15140045', name: '咪咕KTV榜', bangid: '15140045' }, { id: 'mg__15140034', name: '咪咕网络榜', bangid: '15140034' }, { id: 'mg__23217754', name: 'MV榜', bangid: '23217754' }, { id: 'mg__23218151', name: '新专辑榜', bangid: '23218151' }, { id: 'mg__21958042', name: 'iTunes榜', bangid: '21958042' }, { id: 'mg__21975570', name: 'billboard榜', bangid: '21975570' }, { id: 'mg__22272815', name: '台湾Hito中文榜', bangid: '22272815' }, { id: 'mg__22272904', name: '中国TOP排行榜', bangid: '22272904' }, { id: 'mg__22272943', name: '韩国Melon榜', bangid: '22272943' }, { id: 'mg__22273437', name: '英国UK榜', bangid: '22273437' }]
|
||||
|
||||
// const boardList = [
|
||||
// { id: 'mg__27553319', name: '尖叫新歌榜', bangid: '27553319', webId: 'jianjiao_newsong' },
|
||||
// { id: 'mg__27186466', name: '尖叫热歌榜', bangid: '27186466', webId: 'jianjiao_hotsong' },
|
||||
// { id: 'mg__27553408', name: '尖叫原创榜', bangid: '27553408', webId: 'jianjiao_original' },
|
||||
// { id: 'mg__23189800', name: '港台榜', bangid: '23189800', webId: 'hktw' },
|
||||
// { id: 'mg__23189399', name: '内地榜', bangid: '23189399', webId: 'mainland' },
|
||||
// { id: 'mg__19190036', name: '欧美榜', bangid: '19190036', webId: 'eur_usa' },
|
||||
// { id: 'mg__23189813', name: '日韩榜', bangid: '23189813', webId: 'jpn_kor' },
|
||||
// { id: 'mg__23190126', name: '彩铃榜', bangid: '23190126', webId: 'coloring' },
|
||||
// { id: 'mg__15140045', name: 'KTV榜', bangid: '15140045', webId: 'ktv' },
|
||||
// { id: 'mg__15140034', name: '网络榜', bangid: '15140034', webId: 'network' },
|
||||
// // { id: 'mg__21958042', name: '美国iTunes榜', bangid: '21958042', webId: 'itunes' },
|
||||
// // { id: 'mg__21975570', name: '美国billboard榜', bangid: '21975570', webId: 'billboard' },
|
||||
// // { id: 'mg__22272815', name: '台湾Hito中文榜', bangid: '22272815', webId: 'hito' },
|
||||
// // { id: 'mg__22272943', name: '韩国Melon榜', bangid: '22272943', webId: 'mnet' },
|
||||
// // { id: 'mg__22273437', name: '英国UK榜', bangid: '22273437', webId: 'uk' },
|
||||
// ]
|
||||
const boardList = [
|
||||
{
|
||||
id: 'mg__27553319',
|
||||
name: '新歌榜',
|
||||
bangid: '27553319',
|
||||
source: 'mg',
|
||||
},
|
||||
{
|
||||
id: 'mg__27186466',
|
||||
name: '热歌榜',
|
||||
bangid: '27186466',
|
||||
source: 'mg',
|
||||
},
|
||||
{
|
||||
id: 'mg__27553408',
|
||||
name: '原创榜',
|
||||
bangid: '27553408',
|
||||
source: 'mg',
|
||||
},
|
||||
{
|
||||
id: 'mg__75959118',
|
||||
name: '音乐风向榜',
|
||||
bangid: '75959118',
|
||||
source: 'mg',
|
||||
},
|
||||
{
|
||||
id: 'mg__76557036',
|
||||
name: '彩铃分贝榜',
|
||||
bangid: '76557036',
|
||||
source: 'mg',
|
||||
},
|
||||
{
|
||||
id: 'mg__76557745',
|
||||
name: '会员臻爱榜',
|
||||
bangid: '76557745',
|
||||
source: 'mg',
|
||||
},
|
||||
{
|
||||
id: 'mg__23189800',
|
||||
name: '港台榜',
|
||||
bangid: '23189800',
|
||||
source: 'mg',
|
||||
},
|
||||
{
|
||||
id: 'mg__23189399',
|
||||
name: '内地榜',
|
||||
bangid: '23189399',
|
||||
source: 'mg',
|
||||
},
|
||||
{
|
||||
id: 'mg__19190036',
|
||||
name: '欧美榜',
|
||||
bangid: '19190036',
|
||||
source: 'mg',
|
||||
},
|
||||
{
|
||||
id: 'mg__83176390',
|
||||
name: '国风金曲榜',
|
||||
bangid: '83176390',
|
||||
source: 'mg',
|
||||
},
|
||||
]
|
||||
export default {
|
||||
limit: 200,
|
||||
list: [
|
||||
{
|
||||
id: 'mgyyb',
|
||||
name: '音乐榜',
|
||||
bangid: '27553319',
|
||||
},
|
||||
{
|
||||
id: 'mgysb',
|
||||
name: '影视榜',
|
||||
bangid: '23603721',
|
||||
},
|
||||
{
|
||||
id: 'mghybnd',
|
||||
name: '华语内地榜',
|
||||
bangid: '23603926',
|
||||
},
|
||||
{
|
||||
id: 'mghyjqbgt',
|
||||
name: '华语港台榜',
|
||||
bangid: '23603954',
|
||||
},
|
||||
{
|
||||
id: 'mgomb',
|
||||
name: '欧美榜',
|
||||
bangid: '23603974',
|
||||
},
|
||||
{
|
||||
id: 'mgrhb',
|
||||
name: '日韩榜',
|
||||
bangid: '23603982',
|
||||
},
|
||||
{
|
||||
id: 'mgwlb',
|
||||
name: '网络榜',
|
||||
bangid: '23604058',
|
||||
},
|
||||
{
|
||||
id: 'mgclb',
|
||||
name: '彩铃榜',
|
||||
bangid: '23604023',
|
||||
},
|
||||
{
|
||||
id: 'mgktvb',
|
||||
name: 'KTV榜',
|
||||
bangid: '23604040',
|
||||
},
|
||||
{
|
||||
id: 'mgrcb',
|
||||
name: '原创榜',
|
||||
bangid: '23604032',
|
||||
},
|
||||
],
|
||||
getUrl(id, page) {
|
||||
return `https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/querycontentbyId.do?columnId=${id}&needAll=0`
|
||||
// return `http://m.music.migu.cn/migu/remoting/cms_list_tag?nid=${id}&pageSize=${this.limit}&pageNo=${page - 1}`
|
||||
},
|
||||
successCode: '000000',
|
||||
requestBoardsObj: null,
|
||||
getBoardsData() {
|
||||
if (this.requestBoardsObj) this._requestBoardsObj.cancelHttp()
|
||||
this.requestBoardsObj = httpFetch('https://app.c.nf.migu.cn/pc/bmw/rank/rank-index/v1.0', {
|
||||
// this.requestBoardsObj = httpFetch('https://app.c.nf.migu.cn/MIGUM3.0/v1.0/template/rank-list/release', {
|
||||
// this.requestBoardsObj = httpFetch('https://app.c.nf.migu.cn/MIGUM2.0/v2.0/content/indexrank.do?templateVersion=8', {
|
||||
headers: {
|
||||
Referer: 'https://app.c.nf.migu.cn/',
|
||||
'User-Agent': 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Mobile Safari/537.36',
|
||||
channel: '0146921',
|
||||
},
|
||||
})
|
||||
return this.requestBoardsObj.promise
|
||||
},
|
||||
getData(url) {
|
||||
const requestObj = httpFetch(url)
|
||||
return requestObj.promise
|
||||
},
|
||||
// filterBoardsData(listData, list = [], ids = new Set()) {
|
||||
// for (const item of listData) {
|
||||
// if (item.rankId && !ids.has(item.rankId)) {
|
||||
// ids.add(item.rankId)
|
||||
// list.push({
|
||||
// id: 'mg__' + item.rankId,
|
||||
// name: item.rankName,
|
||||
// bangid: String(item.rankId),
|
||||
// source: 'mg',
|
||||
// })
|
||||
// } else if (item.contents) this.filterBoardsData(item.contents, list, ids)
|
||||
// }
|
||||
// return list
|
||||
// },
|
||||
// filterBoardsData(rawList) {
|
||||
// // console.log(rawList)
|
||||
// let list = []
|
||||
// for (const board of rawList) {
|
||||
// if (board.template != 'group1') continue
|
||||
// for (const item of board.itemList) {
|
||||
// if ((item.template != 'row1' && item.template != 'grid1' && !item.actionUrl) || !item.actionUrl.includes('rank-info')) continue
|
||||
|
||||
// let data = item.displayLogId.param
|
||||
// list.push({
|
||||
// id: 'mg__' + data.rankId,
|
||||
// name: data.rankName,
|
||||
// bangid: String(data.rankId),
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// return list
|
||||
// },
|
||||
async getBoards(retryNum = 0) {
|
||||
// if (++retryNum > 3) return Promise.reject(new Error('try max num'))
|
||||
// let response
|
||||
// try {
|
||||
// response = await this.getBoardsData()
|
||||
// } catch (error) {
|
||||
// return this.getBoards(retryNum)
|
||||
// }
|
||||
// // console.log(response.body.data.contentItemList)
|
||||
// if (response.statusCode !== 200 || response.body.code !== this.successCode) return this.getBoards(retryNum)
|
||||
// const list = this.filterBoardsData(response.body.data.contents)
|
||||
// console.log(list)
|
||||
// // console.log(JSON.stringify(list))
|
||||
// this.list = list
|
||||
// return {
|
||||
// list,
|
||||
// source: 'mg',
|
||||
// }
|
||||
this.list = boardList
|
||||
return {
|
||||
list: boardList,
|
||||
source: 'mg',
|
||||
}
|
||||
},
|
||||
getList(bangid, page, retryNum = 0) {
|
||||
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
|
||||
return this.getData(this.getUrl(bangid, page)).then(({ statusCode, body }) => {
|
||||
// console.log(body)
|
||||
if (statusCode !== 200 || body.code !== this.successCode) return this.getList(bangid, page, retryNum)
|
||||
const list = filterMusicInfoList(body.columnInfo.contents.map(m => m.objectInfo))
|
||||
return {
|
||||
total: list.length,
|
||||
list,
|
||||
limit: this.limit,
|
||||
page,
|
||||
source: 'mg',
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getDetailPageUrl(id) {
|
||||
if (typeof id == 'string') id = id.replace('mg__', '')
|
||||
for (const item of boardList) {
|
||||
if (item.bangid == id) {
|
||||
return `https://music.migu.cn/v3/music/top/${item.webId}`
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
import { httpFetch } from '../request.js'
|
||||
import { getMusicInfo } from './musicInfo.js'
|
||||
import { decrypt } from './utils/mrc.js'
|
||||
|
||||
const mrcTools = {
|
||||
rxps: {
|
||||
lineTime: /^\s*\[(\d+),\d+\]/,
|
||||
wordTime: /\(\d+,\d+\)/,
|
||||
wordTimeAll: /(\(\d+,\d+\))/g,
|
||||
},
|
||||
parseLyric(str) {
|
||||
str = str.replace(/\r/g, '')
|
||||
const lines = str.split('\n')
|
||||
const lxlrcLines = []
|
||||
const lrcLines = []
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.length < 6) continue
|
||||
let result = this.rxps.lineTime.exec(line)
|
||||
if (!result) continue
|
||||
|
||||
const startTime = parseInt(result[1])
|
||||
let time = startTime
|
||||
let ms = time % 1000
|
||||
time /= 1000
|
||||
let m = parseInt(time / 60).toString().padStart(2, '0')
|
||||
time %= 60
|
||||
let s = parseInt(time).toString().padStart(2, '0')
|
||||
time = `${m}:${s}.${ms}`
|
||||
|
||||
let words = line.replace(this.rxps.lineTime, '')
|
||||
|
||||
lrcLines.push(`[${time}]${words.replace(this.rxps.wordTimeAll, '')}`)
|
||||
|
||||
let times = words.match(this.rxps.wordTimeAll)
|
||||
if (!times) continue
|
||||
times = times.map(time => {
|
||||
const result = /\((\d+),(\d+)\)/.exec(time)
|
||||
return `<${parseInt(result[1]) - startTime},${result[2]}>`
|
||||
})
|
||||
const wordArr = words.split(this.rxps.wordTime)
|
||||
const newWords = times.map((time, index) => `${time}${wordArr[index]}`).join('')
|
||||
lxlrcLines.push(`[${time}]${newWords}`)
|
||||
}
|
||||
return {
|
||||
lyric: lrcLines.join('\n'),
|
||||
lxlyric: lxlrcLines.join('\n'),
|
||||
}
|
||||
},
|
||||
getText(url, tryNum = 0) {
|
||||
const requestObj = httpFetch(url, {
|
||||
headers: {
|
||||
Referer: 'https://app.c.nf.migu.cn/',
|
||||
'User-Agent': 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Mobile Safari/537.36',
|
||||
channel: '0146921',
|
||||
},
|
||||
})
|
||||
return requestObj.promise.then(({ statusCode, body }) => {
|
||||
if (statusCode == 200) return body
|
||||
if (tryNum > 5 || statusCode == 404) return Promise.reject(new Error('歌词获取失败'))
|
||||
return this.getText(url, ++tryNum)
|
||||
})
|
||||
},
|
||||
getMrc(url) {
|
||||
return this.getText(url).then(text => {
|
||||
return this.parseLyric(decrypt(text))
|
||||
})
|
||||
},
|
||||
getLrc(url) {
|
||||
return this.getText(url).then(text => ({ lxlyric: '', lyric: text }))
|
||||
},
|
||||
getTrc(url) {
|
||||
if (!url) return Promise.resolve('')
|
||||
return this.getText(url)
|
||||
},
|
||||
async getMusicInfo(songInfo) {
|
||||
return songInfo.mrcUrl == null
|
||||
? getMusicInfo(songInfo.songmid || songInfo.copyrightId)
|
||||
: songInfo
|
||||
},
|
||||
getLyric(songInfo) {
|
||||
return {
|
||||
promise: this.getMusicInfo(songInfo).then(info => {
|
||||
let p
|
||||
if (info.mrcUrl) p = this.getMrc(info.mrcUrl)
|
||||
else if (info.lrcUrl) p = this.getLrc(info.lrcUrl)
|
||||
else if (info.lyricUrl) p = this.getLrc(info.lyricUrl)
|
||||
if (p == null) return Promise.reject(new Error('获取歌词失败'))
|
||||
return Promise.all([p, this.getTrc(info.trcUrl)]).then(([lrcInfo, tlyric]) => {
|
||||
lrcInfo.tlyric = tlyric
|
||||
return lrcInfo
|
||||
})
|
||||
}),
|
||||
cancelHttp() {},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
getLyric(songInfo) {
|
||||
let requestObj = mrcTools.getLyric(songInfo)
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
import { sizeFormate, formatPlayTime } from '../index.js'
|
||||
import { createHttpFetch } from './utils/index.js'
|
||||
import { formatSingerName } from '../utils.js'
|
||||
|
||||
const createGetMusicInfosTask = (ids) => {
|
||||
let list = ids
|
||||
let tasks = []
|
||||
while (list.length) {
|
||||
tasks.push(list.slice(0, 100))
|
||||
if (list.length < 100) break
|
||||
list = list.slice(100)
|
||||
}
|
||||
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)))
|
||||
}
|
||||
|
||||
export const filterMusicInfoList = (rawList) => {
|
||||
// console.log(rawList)
|
||||
let ids = new Set()
|
||||
const list = []
|
||||
rawList.forEach(item => {
|
||||
if (!item.songId || ids.has(item.songId)) return
|
||||
ids.add(item.songId)
|
||||
const types = []
|
||||
const _types = {}
|
||||
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 intervalTest = /(\d\d:\d\d)$/.test(item.length)
|
||||
|
||||
list.push({
|
||||
singer: formatSingerName(item.artists, 'name'),
|
||||
name: item.songName,
|
||||
albumName: item.album,
|
||||
albumId: item.albumId,
|
||||
songmid: item.songId,
|
||||
copyrightId: item.copyrightId,
|
||||
source: 'mg',
|
||||
interval: intervalTest ? RegExp.$1 : null,
|
||||
img: item.albumImgs?.length ? item.albumImgs[0].img : null,
|
||||
lrc: null,
|
||||
lrcUrl: item.lrcUrl,
|
||||
mrcUrl: item.mrcUrl,
|
||||
trcUrl: item.trcUrl,
|
||||
otherSource: null,
|
||||
types,
|
||||
_types,
|
||||
typeUrl: {},
|
||||
})
|
||||
})
|
||||
return list
|
||||
}
|
||||
|
||||
export const filterMusicInfoListV5 = (rawList) => {
|
||||
// console.log(rawList)
|
||||
let ids = new Set()
|
||||
const list = []
|
||||
rawList.forEach(item => {
|
||||
if (!item.songId || ids.has(item.songId)) return
|
||||
ids.add(item.songId)
|
||||
const types = []
|
||||
const _types = {}
|
||||
item.audioFormats?.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
|
||||
}
|
||||
})
|
||||
|
||||
list.push({
|
||||
singer: formatSingerName(item.singerList, 'name'),
|
||||
name: item.songName,
|
||||
albumName: item.album,
|
||||
albumId: item.albumId,
|
||||
songmid: item.songId,
|
||||
copyrightId: item.copyrightId,
|
||||
source: 'mg',
|
||||
interval: formatPlayTime(item.duration),
|
||||
img: item.img3 || item.img2 || item.img1 || null,
|
||||
lrc: null,
|
||||
lrcUrl: item.lrcUrl,
|
||||
mrcUrl: item.mrcUrl,
|
||||
trcUrl: item.trcUrl,
|
||||
otherSource: null,
|
||||
types,
|
||||
_types,
|
||||
typeUrl: {},
|
||||
})
|
||||
})
|
||||
return list
|
||||
}
|
||||
|
||||
export const getMusicInfo = async(copyrightId) => {
|
||||
return getMusicInfos([copyrightId]).then(data => data[0])
|
||||
}
|
||||
|
||||
export const getMusicInfos = async(copyrightIds) => {
|
||||
return filterMusicInfoList((await createGetMusicInfosTask(copyrightIds)).flat())
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
import { httpFetch } from '../request.js'
|
||||
import { sizeFormate, formatPlayTime } from '../index.js'
|
||||
import { toMD5, formatSingerName } from '../utils.js'
|
||||
|
||||
export const createSignature = (time, str) => {
|
||||
const deviceId = '963B7AA0D21511ED807EE5846EC87D20'
|
||||
const signatureMd5 = '6cdc72a439cef99a3418d2a78aa28c73'
|
||||
const sign = toMD5(`${str}${signatureMd5}yyapp2d16148780a1dcc7408e06336b98cfd50${deviceId}${time}`)
|
||||
return { sign, deviceId }
|
||||
}
|
||||
|
||||
export default {
|
||||
limit: 20,
|
||||
total: 0,
|
||||
page: 0,
|
||||
allPage: 1,
|
||||
|
||||
// 旧版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) {
|
||||
const time = Date.now().toString()
|
||||
const signData = createSignature(time, str)
|
||||
const searchRequest = httpFetch(`https://jadeite.migu.cn/music_search/v3/search/searchAll?isCorrect=0&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&sid=USS`, {
|
||||
headers: {
|
||||
uiVersion: 'A_music_3.6.1',
|
||||
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',
|
||||
},
|
||||
})
|
||||
return searchRequest.promise.then(({ body }) => body)
|
||||
},
|
||||
filterData(rawData) {
|
||||
// console.log(rawData)
|
||||
const list = []
|
||||
const ids = new Set()
|
||||
|
||||
rawData.forEach(item => {
|
||||
item.forEach(data => {
|
||||
if (!data.songId || !data.copyrightId || ids.has(data.copyrightId)) return
|
||||
ids.add(data.copyrightId)
|
||||
|
||||
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 = data.img3 || data.img2 || data.img1 || null
|
||||
if (img && !/https?:/.test(data.img3)) img = 'http://d.musicapp.migu.cn' + img
|
||||
|
||||
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
|
||||
},
|
||||
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 }
|
||||
|
||||
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 {
|
||||
list,
|
||||
allPage: this.allPage,
|
||||
limit,
|
||||
total: this.total,
|
||||
source: 'mg',
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { httpFetch } from '../request.js'
|
||||
import getSongId from './songId.js'
|
||||
|
||||
export default {
|
||||
async getPicUrl(songId, tryNum = 0) {
|
||||
let requestObj = httpFetch(`http://music.migu.cn/v3/api/music/audioPlayer/getSongPic?songId=${songId}`, {
|
||||
headers: {
|
||||
Referer: 'http://music.migu.cn/v3/music/player/audio?from=migu',
|
||||
},
|
||||
})
|
||||
requestObj.promise.then(({ body }) => {
|
||||
if (body.returnCode !== '000000') {
|
||||
if (tryNum > 5) return Promise.reject(new Error('图片获取失败'))
|
||||
let tryRequestObj = this.getPic(songId, ++tryNum)
|
||||
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
|
||||
return tryRequestObj.promise
|
||||
}
|
||||
let url = body.largePic || body.mediumPic || body.smallPic
|
||||
if (!/https?:/.test(url)) url = 'http:' + url
|
||||
return url
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
async getPic(songInfo) {
|
||||
const songId = await getSongId(songInfo)
|
||||
return this.getPicUrl(songId)
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// import { httpFetch } from '../request.js'
|
||||
import { getMusicInfo } from './musicInfo.js'
|
||||
|
||||
const getSongId = async(mInfo) => {
|
||||
if (mInfo.songmid != mInfo.copyrightId) return mInfo.songmid
|
||||
const musicInfo = await getMusicInfo(mInfo.copyrightId)
|
||||
return musicInfo.songmid
|
||||
}
|
||||
|
||||
|
||||
// export const getSongId = async(musicInfo, retry = 0) => {
|
||||
// if (musicInfo.songmid != musicInfo.copyrightId) return musicInfo.songmid
|
||||
// if (++retry > 2) return Promise.reject(new Error('max retry'))
|
||||
|
||||
// const requestObj = httpFetch(`https://app.c.nf.migu.cn/MIGUM2.0/v2.0/content/listen-url?netType=00&resourceType=2&songId=${musicInfo.copyrightId}&toneFlag=PQ`, {
|
||||
// headers: {
|
||||
// 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
|
||||
// channel: '0146921',
|
||||
// },
|
||||
// })
|
||||
|
||||
// return requestObj.promise.then(({ body }) => {
|
||||
// console.log(body)
|
||||
// if (!body || body.code !== '000000') return this.getSongId(musicInfo, retry)
|
||||
// const id = body.data.songItem.songId
|
||||
// if (!id) throw new Error('failed')
|
||||
// return id
|
||||
// })
|
||||
// }
|
||||
|
||||
export default getSongId
|
||||
@@ -0,0 +1,362 @@
|
||||
import { httpFetch } from '../request.js'
|
||||
import { formatPlayCount } from '../index.js'
|
||||
import { filterMusicInfoListV5 } from './musicInfo.js'
|
||||
import { createSignature } from './musicSearch.js'
|
||||
import { createHttpFetch } from './utils/index.js'
|
||||
|
||||
// const tagData = { code: '000000', info: 'SUCCESS', columnInfo: { columnTitle: '分类', columnId: '15244430', columnPid: '15031270', opNumItem: { playNum: 0, playNumDesc: '0', keepNum: 0, keepNumDesc: '0', commentNum: 0, commentNumDesc: '0', shareNum: 0, shareNumDesc: '0', orderNumByWeek: 0, orderNumByWeekDesc: '0', orderNumByTotal: 0, orderNumByTotalDesc: '0', thumbNum: 0, thumbNumDesc: '0', followNum: 0, followNumDesc: '0', subscribeNum: 0, subscribeNumDesc: '0', livePlayNum: 0, livePlayNumDesc: '0', popularNum: 0, popularNumDesc: '0', bookingNum: 0, bookingNumDesc: '0' }, contentsCount: 6, columnStatus: 1, columnCreateTime: '2016-11-10 10:53:05.077', columntype: 2011, contents: [{ contentId: '18464615', relationType: 2011, objectInfo: { columnTitle: '热门', columnId: '18464615', columnPid: '15244430', opNumItem: { playNum: 0, playNumDesc: '0', keepNum: 0, keepNumDesc: '0', commentNum: 0, commentNumDesc: '0', shareNum: 0, shareNumDesc: '0', orderNumByWeek: 0, orderNumByWeekDesc: '0', orderNumByTotal: 0, orderNumByTotalDesc: '0', thumbNum: 0, thumbNumDesc: '0', followNum: 0, followNumDesc: '0', subscribeNum: 0, subscribeNumDesc: '0', livePlayNum: 0, livePlayNumDesc: '0', popularNum: 0, popularNumDesc: '0', bookingNum: 0, bookingNumDesc: '0' }, contentsCount: 8, columnStatus: 1, columnCreateTime: '2017-02-20 16:09:13.400', columntype: 2011, contents: [{ contentId: '1000001672', relationType: 4034, objectInfo: { tagId: '1000001672', tagName: '流行', resourceType: '2034' }, relationSort: 9 }, { contentId: '1003449727', relationType: 4034, objectInfo: { tagId: '1003449727', tagName: '厂牌', resourceType: '2034' }, relationSort: 8 }, { contentId: '1000001795', relationType: 4034, objectInfo: { tagId: '1000001795', tagName: '伤感', resourceType: '2034' }, relationSort: 7 }, { contentId: '1001076080', relationType: 4034, objectInfo: { tagId: '1001076080', tagName: '电影', resourceType: '2034' }, relationSort: 6 }, { contentId: '1000001675', relationType: 4034, objectInfo: { tagId: '1000001675', tagName: '中国风', resourceType: '2034' }, relationSort: 5 }, { contentId: '1000001635', relationType: 4034, objectInfo: { tagId: '1000001635', tagName: '经典老歌', resourceType: '2034' }, relationSort: 4 }, { contentId: '1000001831', relationType: 4034, objectInfo: { tagId: '1000001831', tagName: '翻唱', resourceType: '2034' }, relationSort: 3 }, { contentId: '1000001762', relationType: 4034, objectInfo: { tagId: '1000001762', tagName: '国语', resourceType: '2034' }, relationSort: 1 }], dataVersion: '1620410266029', customizedPicUrls: [] }, relationSort: 6 }, { contentId: '15244503', relationType: 2011, objectInfo: { columnTitle: '主题', columnId: '15244503', columnPid: '15244430', opNumItem: { playNum: 0, playNumDesc: '0', keepNum: 0, keepNumDesc: '0', commentNum: 0, commentNumDesc: '0', shareNum: 0, shareNumDesc: '0', orderNumByWeek: 0, orderNumByWeekDesc: '0', orderNumByTotal: 0, orderNumByTotalDesc: '0', thumbNum: 0, thumbNumDesc: '0', followNum: 0, followNumDesc: '0', subscribeNum: 0, subscribeNumDesc: '0', livePlayNum: 0, livePlayNumDesc: '0', popularNum: 0, popularNumDesc: '0', bookingNum: 0, bookingNumDesc: '0' }, contentsCount: 23, columnStatus: 1, columnCreateTime: '2016-11-10 10:54:10.261', columntype: 2011, contents: [{ contentId: '1003449727', relationType: 4034, objectInfo: { tagId: '1003449727', tagName: '厂牌', resourceType: '2034' }, relationSort: 29 }, { contentId: '1001076080', relationType: 4034, objectInfo: { tagId: '1001076080', tagName: '电影', resourceType: '2034' }, relationSort: 28 }, { contentId: '1001076078', relationType: 4034, objectInfo: { tagId: '1001076078', tagName: '电视剧', resourceType: '2034' }, relationSort: 27 }, { contentId: '1001076083', relationType: 4034, objectInfo: { tagId: '1001076083', tagName: '综艺', resourceType: '2034' }, relationSort: 26 }, { contentId: '1000001827', relationType: 4034, objectInfo: { tagId: '1000001827', tagName: 'KTV', resourceType: '2034' }, relationSort: 23 }, { contentId: '1000001698', relationType: 4034, objectInfo: { tagId: '1000001698', tagName: '爱情', resourceType: '2034' }, relationSort: 22 }, { contentId: '1000001635', relationType: 4034, objectInfo: { tagId: '1000001635', tagName: '经典老歌', resourceType: '2034' }, relationSort: 21 }, { contentId: '1001076096', relationType: 4034, objectInfo: { tagId: '1001076096', tagName: '网络热歌', resourceType: '2034' }, relationSort: 20 }, { contentId: '1000001780', relationType: 4034, objectInfo: { tagId: '1000001780', tagName: '儿童歌曲', resourceType: '2034' }, relationSort: 19 }, { contentId: '1000587702', relationType: 4034, objectInfo: { tagId: '1000587702', tagName: '广场舞', resourceType: '2034' }, relationSort: 18 }, { contentId: '1000587717', relationType: 4034, objectInfo: { tagId: '1000587717', tagName: '70后', resourceType: '2034' }, relationSort: 17 }, { contentId: '1000587718', relationType: 4034, objectInfo: { tagId: '1000587718', t
Line truncated
|
||||
|
||||
export default {
|
||||
_requestObj_tags: null,
|
||||
_requestObj_list: null,
|
||||
limit_list: 30,
|
||||
limit_song: 50,
|
||||
successCode: '000000',
|
||||
cachedDetailInfo: {},
|
||||
cachedUrl: {},
|
||||
sortList: [
|
||||
{
|
||||
name: '推荐',
|
||||
id: '15127315',
|
||||
// id: '1',
|
||||
},
|
||||
// {
|
||||
// name: '最新',
|
||||
// id: '15127272',
|
||||
// // id: '2',
|
||||
// },
|
||||
],
|
||||
regExps: {
|
||||
list: /<li><div class="thumb">.+?<\/li>/g,
|
||||
listInfo: /.+data-original="(.+?)".*data-id="(\d+)".*<div class="song-list-name"><a\s.*?>(.+?)<\/a>.+<i class="iconfont cf-bofangliang"><\/i>(.+?)<\/div>/,
|
||||
|
||||
// https://music.migu.cn/v3/music/playlist/161044573?page=1
|
||||
listDetailLink: /^.+\/playlist\/(\d+)(?:\?.*|&.*$|#.*$|$)/,
|
||||
},
|
||||
tagsUrl: 'https://app.c.nf.migu.cn/pc/v1.0/template/musiclistplaza-taglist/release',
|
||||
// tagsUrl: 'https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/indexTagPage.do?needAll=0',
|
||||
getSongListUrl(sortId, tagId, page) {
|
||||
// if (tagId == null) {
|
||||
// return sortId == 'recommend'
|
||||
// ? `https://music.migu.cn/v3/music/playlist?page=${page}&from=migu`
|
||||
// : `https://music.migu.cn/v3/music/playlist?sort=${sortId}&page=${page}&from=migu`
|
||||
// }
|
||||
// return `https://music.migu.cn/v3/music/playlist?tagId=${tagId}&page=${page}&from=migu`
|
||||
if (!tagId) {
|
||||
// return `https://app.c.nf.migu.cn/MIGUM2.0/v2.0/content/getMusicData.do?count=${this.limit_list}&start=${page}&templateVersion=5&type=1`
|
||||
// return `https://c.musicapp.migu.cn/MIGUM2.0/v2.0/content/getMusicData.do?count=${this.limit_list}&start=${page}&templateVersion=5&type=${sortId}`
|
||||
// https://app.c.nf.migu.cn/MIGUM2.0/v2.0/content/getMusicData.do?count=50&start=2&templateVersion=5&type=1
|
||||
// return `https://m.music.migu.cn/migu/remoting/playlist_bycolumnid_tag?playListType=2&type=1&columnId=${sortId}&startIndex=${(page - 1) * 10}`
|
||||
return `https://app.c.nf.migu.cn/pc/bmw/page-data/playlist-square-recommend/v1.0?templateVersion=2&pageNo=${page}`
|
||||
}
|
||||
// return `https://app.c.nf.migu.cn/MIGUM2.0/v2.0/content/getMusicData.do?area=2&count=${this.limit_list}&start=${page}&tags=${tagId}&templateVersion=5&type=3`
|
||||
return `https://app.c.nf.migu.cn/pc/v1.0/template/musiclistplaza-listbytag/release?pageNumber=${page}&templateVersion=2&tagId=${tagId}`
|
||||
// return `https://m.music.migu.cn/migu/remoting/playlist_bycolumnid_tag?playListType=2&type=1&tagId=${tagId}&startIndex=${(page - 1) * 10}`
|
||||
},
|
||||
getSongListDetailUrl(id, page) {
|
||||
return `https://app.c.nf.migu.cn/MIGUM3.0/resource/playlist/song/v2.0?pageNo=${page}&pageSize=${this.limit_song}&playlistId=${id}`
|
||||
// return `https://app.c.nf.migu.cn/MIGUM2.0/v1.0/user/queryMusicListSongs.do?musicListId=${id}&pageNo=${page}&pageSize=${this.limit_song}`
|
||||
},
|
||||
defaultHeaders: {
|
||||
'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',
|
||||
Referer: 'https://m.music.migu.cn/',
|
||||
// language: 'Chinese',
|
||||
// ua: 'Android_migu',
|
||||
// mode: 'android',
|
||||
// version: '6.8.5',
|
||||
},
|
||||
|
||||
getListDetailList(id, page, tryNum = 0) {
|
||||
if (tryNum > 2) return Promise.reject(new Error('try max num'))
|
||||
// https://h5.nf.migu.cn/app/v4/p/share/playlist/index.html?id=184187437&channel=0146921
|
||||
|
||||
// if (/playlist\/index\.html\?/.test(id)) {
|
||||
// id = id.replace(/.*(?:\?|&)id=(\d+)(?:&.*|$)/, '$1')
|
||||
// } else if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1')
|
||||
|
||||
const requestObj_listDetail = httpFetch(this.getSongListDetailUrl(id, page), { headers: this.defaultHeaders })
|
||||
return requestObj_listDetail.promise.then(({ body }) => {
|
||||
if (body.code !== this.successCode) return this.getListDetailList(id, page, ++tryNum)
|
||||
// console.log(JSON.stringify(body))
|
||||
// console.log(body)
|
||||
return {
|
||||
list: filterMusicInfoListV5(body.data.songList),
|
||||
page,
|
||||
limit: this.limit_song,
|
||||
total: body.data.totalCount,
|
||||
source: 'mg',
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getListDetailInfo(id, tryNum = 0) {
|
||||
if (tryNum > 2) return Promise.reject(new Error('try max num'))
|
||||
|
||||
if (this.cachedDetailInfo[id]) return Promise.resolve(this.cachedDetailInfo[id])
|
||||
const requestObj_listDetailInfo = httpFetch(`https://c.musicapp.migu.cn/MIGUM3.0/resource/playlist/v2.0?playlistId=${id}`, {
|
||||
headers: this.defaultHeaders,
|
||||
})
|
||||
return requestObj_listDetailInfo.promise.then(({ body }) => {
|
||||
if (body.code !== this.successCode) return this.getListDetail(id, ++tryNum)
|
||||
// console.log(JSON.stringify(body))
|
||||
// console.log(body)
|
||||
const cachedDetailInfo = this.cachedDetailInfo[id] = {
|
||||
name: body.data.title,
|
||||
img: body.data.imgItem.img,
|
||||
desc: body.data.summary,
|
||||
author: body.data.ownerName,
|
||||
play_count: formatPlayCount(body.data.opNumItem.playNum),
|
||||
}
|
||||
return cachedDetailInfo
|
||||
})
|
||||
},
|
||||
|
||||
async getDetailUrl(link, page, retryNum = 0) {
|
||||
if (retryNum > 3) return Promise.reject(new Error('link try max num'))
|
||||
|
||||
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,
|
||||
},
|
||||
})
|
||||
const { headers: { location }, statusCode } = await requestObj_listDetailLink.promise
|
||||
// console.log(body, location)
|
||||
if (statusCode > 400) return this.getDetailUrl(link, page, ++retryNum)
|
||||
if (location) {
|
||||
this.cachedUrl[link] = location
|
||||
return this.getListDetail(location, page)
|
||||
}
|
||||
return Promise.reject(new Error('link get failed'))
|
||||
},
|
||||
|
||||
getListDetail(id, page, retryNum = 0) { // 获取歌曲列表内的音乐
|
||||
// https://h5.nf.migu.cn/app/v4/p/share/playlist/index.html?id=184187437&channel=0146921
|
||||
// http://c.migu.cn/00bTY6?ifrom=babddaadfde4ebeda289d671ab62f236
|
||||
// https://music.migu.cn/v5/#/playlist?playlistId=221573417
|
||||
if (/\/playlist[/?]/.test(id)) {
|
||||
id = /(?:playlistId|id)=(\d+)/.exec(id)?.[1]
|
||||
if (!id) throw new Error('list detail id parse failed')
|
||||
} else if (this.regExps.listDetailLink.test(id)) {
|
||||
id = id.replace(this.regExps.listDetailLink, '$1')
|
||||
} else if ((/[?&:/]/.test(id))) {
|
||||
const url = this.cachedUrl[id]
|
||||
return url ? this.getListDetail(url, page) : this.getDetailUrl(id, page)
|
||||
}
|
||||
|
||||
return Promise.all([
|
||||
this.getListDetailList(id, page, retryNum),
|
||||
this.getListDetailInfo(id, retryNum),
|
||||
]).then(([listData, info]) => {
|
||||
listData.info = info
|
||||
return listData
|
||||
})
|
||||
},
|
||||
|
||||
// 获取列表数据
|
||||
getList(sortId, tagId, page, tryNum = 0) {
|
||||
if (this._requestObj_list) this._requestObj_list.cancelHttp()
|
||||
if (tryNum > 2) return Promise.reject(new Error('try max num'))
|
||||
this._requestObj_list = httpFetch(this.getSongListUrl(sortId, tagId, page), {
|
||||
headers: this.defaultHeaders,
|
||||
// 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',
|
||||
// },
|
||||
})
|
||||
// return this._requestObj_list.promise.then(({ statusCode, body }) => {
|
||||
// if (statusCode !== 200) return this.getList(sortId, tagId, page)
|
||||
// let list = body.replace(/[\r\n]/g, '').match(this.regExps.list)
|
||||
// if (!list) return Promise.reject(new Error('获取列表失败'))
|
||||
// return list.map(item => {
|
||||
// let info = item.match(this.regExps.listInfo)
|
||||
// return {
|
||||
// play_count: info[4],
|
||||
// id: info[2],
|
||||
// author: '',
|
||||
// name: info[3],
|
||||
// time: '',
|
||||
// img: info[1],
|
||||
// grade: 0,
|
||||
// desc: '',
|
||||
// source: 'mg',
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
// return this._requestObj_list.promise.then(({ body }) => {
|
||||
// console.log(body)
|
||||
// if (body.retCode !== '100000' || body.retMsg.code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum)
|
||||
// return {
|
||||
// list: this.filterList(body.retMsg.playlist),
|
||||
// total: parseInt(body.retMsg.countSize),
|
||||
// page,
|
||||
// limit: this.limit_list,
|
||||
// source: 'mg',
|
||||
// }
|
||||
// })
|
||||
return this._requestObj_list.promise.then(({ body }) => {
|
||||
// console.log(body)
|
||||
// if (body.retCode !== '000000') return this.getList(sortId, tagId, page, ++tryNum)
|
||||
if (body.code !== '000000') return this.getList(sortId, tagId, page, ++tryNum)
|
||||
const list = body.data.contents ? this.filterList2(body.data.contents) : this.filterList(body.data.contentItemList[1].itemList)
|
||||
return {
|
||||
list,
|
||||
total: 99999,
|
||||
page,
|
||||
limit: this.limit_list,
|
||||
source: 'mg',
|
||||
}
|
||||
})
|
||||
},
|
||||
filterList2(listData, list = [], ids = new Set()) {
|
||||
for (const item of listData) {
|
||||
if (item.contents) this.filterList2(item.contents, list, ids)
|
||||
else if (item.resType == '2021' && !ids.has(item.resId)) {
|
||||
ids.add(item.resId)
|
||||
list.push({
|
||||
id: String(item.resId),
|
||||
author: '',
|
||||
name: item.txt,
|
||||
// time: dateFormat(item.createTime, 'Y-M-D'),
|
||||
img: item.img,
|
||||
// grade: item.grade,
|
||||
// total: item.contentCount,
|
||||
desc: item.txt2,
|
||||
source: 'mg',
|
||||
})
|
||||
}
|
||||
}
|
||||
return list
|
||||
},
|
||||
filterList(rawData) {
|
||||
// console.log(rawData)
|
||||
return rawData.map(item => ({
|
||||
play_count: item.barList[0]?.title,
|
||||
id: String(item.logEvent.contentId),
|
||||
author: '',
|
||||
name: item.title,
|
||||
// time: dateFormat(item.createTime, 'Y-M-D'),
|
||||
img: item.imageUrl,
|
||||
// grade: item.grade,
|
||||
// total: item.contentCount,
|
||||
desc: '',
|
||||
source: 'mg',
|
||||
}))
|
||||
},
|
||||
|
||||
// 获取标签
|
||||
getTag(tryNum = 0) {
|
||||
if (this._requestObj_tags) this._requestObj_tags.cancelHttp()
|
||||
if (tryNum > 2) return Promise.reject(new Error('try max num'))
|
||||
this._requestObj_tags = httpFetch(this.tagsUrl, { headers: this.defaultHeaders })
|
||||
return this._requestObj_tags.promise.then(({ body }) => {
|
||||
if (body.code !== this.successCode) return this.getTag(++tryNum)
|
||||
// console.log(body)
|
||||
return this.filterTagInfo(body.data)
|
||||
})
|
||||
// return Promise.resolve(this.filterTagInfo(tagData.columnInfo.contents))
|
||||
},
|
||||
filterTagInfo(rawList) {
|
||||
return {
|
||||
hotTag: rawList[0].content.map(({ texts: [name, id] }) => ({
|
||||
id,
|
||||
name,
|
||||
source: 'mg',
|
||||
})),
|
||||
tags: rawList.slice(1).map(({ header, content }) => ({
|
||||
name: header.title,
|
||||
list: content.map(({ texts: [name, id] }) => ({
|
||||
// parent_id: objectInfo.columnId,
|
||||
// parent_name: objectInfo.columnTitle,
|
||||
id,
|
||||
name,
|
||||
source: 'mg',
|
||||
})),
|
||||
})),
|
||||
source: 'mg',
|
||||
}
|
||||
// return {
|
||||
// hotTag: rawList[0].objectInfo.contents.map(item => ({
|
||||
// id: item.objectInfo.tagId,
|
||||
// name: item.objectInfo.tagName,
|
||||
// source: 'mg',
|
||||
// })),
|
||||
// tags: rawList.slice(1).map(({ objectInfo }) => ({
|
||||
// name: objectInfo.columnTitle,
|
||||
// list: objectInfo.contents.map(item => ({
|
||||
// parent_id: objectInfo.columnId,
|
||||
// parent_name: objectInfo.columnTitle,
|
||||
// id: item.objectInfo.tagId,
|
||||
// name: item.objectInfo.tagName,
|
||||
// source: 'mg',
|
||||
// })),
|
||||
// })),
|
||||
// source: 'mg',
|
||||
// }
|
||||
},
|
||||
getTags() {
|
||||
return this.getTag()
|
||||
},
|
||||
|
||||
getDetailPageUrl(id) {
|
||||
if (/playlist\/index\.html\?/.test(id)) {
|
||||
id = id.replace(/.*(?:\?|&)id=(\d+)(?:&.*|$)/, '$1')
|
||||
} else if (this.regExps.listDetailLink.test(id)) {
|
||||
id = id.replace(this.regExps.listDetailLink, '$1')
|
||||
}
|
||||
return `https://music.migu.cn/v3/music/playlist/${id}`
|
||||
},
|
||||
|
||||
filterSongListResult(raw) {
|
||||
const list = []
|
||||
raw.forEach(item => {
|
||||
if (!item.id) return
|
||||
|
||||
const playCount = parseInt(item.playNum)
|
||||
list.push({
|
||||
play_count: isNaN(playCount) ? 0 : formatPlayCount(playCount),
|
||||
id: item.id,
|
||||
author: item.userName,
|
||||
name: item.name,
|
||||
img: item.musicListPicUrl,
|
||||
total: item.musicNum,
|
||||
source: 'mg',
|
||||
})
|
||||
})
|
||||
return list
|
||||
},
|
||||
search(text, page, limit = 20) {
|
||||
const timeStr = Date.now().toString()
|
||||
const signResult = createSignature(timeStr, text)
|
||||
return createHttpFetch(`https://jadeite.migu.cn/music_search/v3/search/searchAll?isCorrect=1&isCopyright=1&searchSwitch=%7B%22song%22%3A0%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A0%2C%22mvSong%22%3A0%2C%22bestShow%22%3A0%2C%22songlist%22%3A1%2C%22lyricSong%22%3A0%7D&pageSize=${limit}&text=${encodeURIComponent(text)}&pageNo=${page}&sort=0&sid=USS`, {
|
||||
headers: {
|
||||
uiVersion: 'A_music_3.6.1',
|
||||
deviceId: signResult.deviceId,
|
||||
timestamp: timeStr,
|
||||
sign: signResult.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 => {
|
||||
if (!body.songListResultData) throw new Error('get song list faild.')
|
||||
|
||||
const list = this.filterSongListResult(body.songListResultData.result)
|
||||
return {
|
||||
list,
|
||||
limit,
|
||||
total: parseInt(body.songListResultData.totalCount),
|
||||
source: 'mg',
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
// getList
|
||||
// getTags
|
||||
// getListDetail
|
||||
@@ -0,0 +1,195 @@
|
||||
import { httpFetch } from '../../../request.js'
|
||||
import { formatPlayTime } from '../../../index.js'
|
||||
// import { sizeFormate } from '../index.js'
|
||||
|
||||
|
||||
// const boardList = [{ id: 'mg__27553319', name: '咪咕尖叫新歌榜', bangid: '27553319' }, { id: 'mg__27186466', name: '咪咕尖叫热歌榜', bangid: '27186466' }, { id: 'mg__27553408', name: '咪咕尖叫原创榜', bangid: '27553408' }, { id: 'mg__23189800', name: '咪咕港台榜', bangid: '23189800' }, { id: 'mg__23189399', name: '咪咕内地榜', bangid: '23189399' }, { id: 'mg__19190036', name: '咪咕欧美榜', bangid: '19190036' }, { id: 'mg__23189813', name: '咪咕日韩榜', bangid: '23189813' }, { id: 'mg__23190126', name: '咪咕彩铃榜', bangid: '23190126' }, { id: 'mg__15140045', name: '咪咕KTV榜', bangid: '15140045' }, { id: 'mg__15140034', name: '咪咕网络榜', bangid: '15140034' }, { id: 'mg__23217754', name: 'MV榜', bangid: '23217754' }, { id: 'mg__23218151', name: '新专辑榜', bangid: '23218151' }, { id: 'mg__21958042', name: 'iTunes榜', bangid: '21958042' }, { id: 'mg__21975570', name: 'billboard榜', bangid: '21975570' }, { id: 'mg__22272815', name: '台湾Hito中文榜', bangid: '22272815' }, { id: 'mg__22272904', name: '中国TOP排行榜', bangid: '22272904' }, { id: 'mg__22272943', name: '韩国Melon榜', bangid: '22272943' }, { id: 'mg__22273437', name: '英国UK榜', bangid: '22273437' }]
|
||||
const boardList = [
|
||||
{ id: 'mg__27553319', name: '尖叫新歌榜', bangid: '27553319', webId: 'jianjiao_newsong' },
|
||||
{ id: 'mg__27186466', name: '尖叫热歌榜', bangid: '27186466', webId: 'jianjiao_hotsong' },
|
||||
{ id: 'mg__27553408', name: '尖叫原创榜', bangid: '27553408', webId: 'jianjiao_original' },
|
||||
{ id: 'mg__migumusic', name: '音乐榜', bangid: 'migumusic', webId: 'migumusic' },
|
||||
{ id: 'mg__movies', name: '影视榜', bangid: 'movies', webId: 'movies' },
|
||||
{ id: 'mg__23189800', name: '港台榜', bangid: '23189800', webId: 'hktw' },
|
||||
{ id: 'mg__23189399', name: '内地榜', bangid: '23189399', webId: 'mainland' },
|
||||
{ id: 'mg__19190036', name: '欧美榜', bangid: '19190036', webId: 'eur_usa' },
|
||||
{ id: 'mg__23189813', name: '日韩榜', bangid: '23189813', webId: 'jpn_kor' },
|
||||
{ id: 'mg__23190126', name: '彩铃榜', bangid: '23190126', webId: 'coloring' },
|
||||
{ id: 'mg__15140045', name: 'KTV榜', bangid: '15140045', webId: 'ktv' },
|
||||
{ id: 'mg__15140034', name: '网络榜', bangid: '15140034', webId: 'network' },
|
||||
{ id: 'mg__23217754', name: 'MV榜', bangid: '23217754', webId: 'mv' },
|
||||
{ id: 'mg__23218151', name: '新专辑榜', bangid: '23218151', webId: 'newalbum' },
|
||||
{ id: 'mg__21958042', name: '美国iTunes榜', bangid: '21958042', webId: 'itunes' },
|
||||
{ id: 'mg__21975570', name: '美国billboard榜', bangid: '21975570', webId: 'billboard' },
|
||||
{ id: 'mg__22272815', name: '台湾Hito中文榜', bangid: '22272815', webId: 'hito' },
|
||||
{ id: 'mg__22272904', name: '中国TOP排行榜', bangid: '22272904' },
|
||||
{ id: 'mg__22272943', name: '韩国Melon榜', bangid: '22272943', webId: 'mnet' },
|
||||
{ id: 'mg__22273437', name: '英国UK榜', bangid: '22273437', webId: 'uk' },
|
||||
]
|
||||
// const boardList = [
|
||||
// { id: 'mg__jianjiao_newsong', bangid: 'jianjiao_newsong', name: '尖叫新歌榜' },
|
||||
// { id: 'mg__jianjiao_hotsong', bangid: 'jianjiao_hotsong', name: '尖叫热歌榜' },
|
||||
// { id: 'mg__jianjiao_original', bangid: 'jianjiao_original', name: '尖叫原创榜' },
|
||||
// { id: 'mg__migumusic', bangid: 'migumusic', name: '音乐榜' },
|
||||
// { id: 'mg__movies', bangid: 'movies', name: '影视榜' },
|
||||
// { id: 'mg__mainland', bangid: 'mainland', name: '内地榜' },
|
||||
// { id: 'mg__hktw', bangid: 'hktw', name: '港台榜' },
|
||||
// { id: 'mg__eur_usa', bangid: 'eur_usa', name: '欧美榜' },
|
||||
// { id: 'mg__jpn_kor', bangid: 'jpn_kor', name: '日韩榜' },
|
||||
// { id: 'mg__coloring', bangid: 'coloring', name: '彩铃榜' },
|
||||
// { id: 'mg__ktv', bangid: 'ktv', name: 'KTV榜' },
|
||||
// { id: 'mg__network', bangid: 'network', name: '网络榜' },
|
||||
// { id: 'mg__newalbum', bangid: 'newalbum', name: '新专辑榜' },
|
||||
// { id: 'mg__mv', bangid: 'mv', name: 'MV榜' },
|
||||
// { id: 'mg__itunes', bangid: 'itunes', name: '美国iTunes榜' },
|
||||
// { id: 'mg__billboard', bangid: 'billboard', name: '美国billboard榜' },
|
||||
// { id: 'mg__hito', bangid: 'hito', name: 'Hito中文榜' },
|
||||
// { id: 'mg__mnet', bangid: 'mnet', name: '韩国Melon榜' },
|
||||
// { id: 'mg__uk', bangid: 'uk', name: '英国UK榜' },
|
||||
// ]
|
||||
|
||||
export default {
|
||||
limit: 10000,
|
||||
getUrl(id, page) {
|
||||
const targetBoard = boardList.find(board => board.bangid == id)
|
||||
return `https://music.migu.cn/v3/music/top/${targetBoard.webId}`
|
||||
// return `http://m.music.migu.cn/migu/remoting/cms_list_tag?nid=${id}&pageSize=${this.limit}&pageNo=${page - 1}`
|
||||
},
|
||||
successCode: '000000',
|
||||
requestBoardsObj: null,
|
||||
regExps: {
|
||||
listData: /var listData = (\{.+\})<\/script>/,
|
||||
},
|
||||
getData(url) {
|
||||
const requestObj = httpFetch(url)
|
||||
return requestObj.promise
|
||||
},
|
||||
getSinger(singers) {
|
||||
let arr = []
|
||||
singers.forEach(singer => {
|
||||
arr.push(singer.name)
|
||||
})
|
||||
return arr.join('、')
|
||||
},
|
||||
getIntv(interval) {
|
||||
if (!interval) return 0
|
||||
let intvArr = interval.split(':')
|
||||
let intv = 0
|
||||
let unit = 1
|
||||
while (intvArr.length) {
|
||||
intv += (intvArr.pop()) * unit
|
||||
unit *= 60
|
||||
}
|
||||
return parseInt(intv)
|
||||
},
|
||||
formateIntv() {
|
||||
|
||||
},
|
||||
filterData(rawData) {
|
||||
// console.log(JSON.stringify(rawData))
|
||||
// console.log(rawData)
|
||||
let ids = new Set()
|
||||
const list = []
|
||||
rawData.forEach(item => {
|
||||
if (ids.has(item.copyrightId)) return
|
||||
ids.add(item.copyrightId)
|
||||
|
||||
const types = []
|
||||
const _types = {}
|
||||
|
||||
const size = null
|
||||
types.push({ type: '128k', size })
|
||||
_types['128k'] = { size }
|
||||
|
||||
if (item.hq) {
|
||||
const size = null
|
||||
types.push({ type: '320k', size })
|
||||
_types['320k'] = { size }
|
||||
}
|
||||
if (item.sq) {
|
||||
const size = null
|
||||
types.push({ type: 'flac', size })
|
||||
_types.flac = { size }
|
||||
}
|
||||
|
||||
list.push({
|
||||
singer: this.getSinger(item.singers),
|
||||
name: item.name,
|
||||
albumName: item.album && item.album.albumName,
|
||||
albumId: item.album && item.album.albumId,
|
||||
songmid: item.id,
|
||||
copyrightId: item.copyrightId,
|
||||
source: 'mg',
|
||||
interval: item.duration ? formatPlayTime(this.getIntv(item.duration)) : null,
|
||||
img: item.mediumPic ? `https:${item.mediumPic}` : null,
|
||||
lrc: null,
|
||||
// lrcUrl: item.lrcUrl,
|
||||
otherSource: null,
|
||||
types,
|
||||
_types,
|
||||
typeUrl: {},
|
||||
})
|
||||
})
|
||||
return list
|
||||
},
|
||||
filterBoardsData(rawList) {
|
||||
// console.log(rawList)
|
||||
let list = []
|
||||
for (const board of rawList) {
|
||||
if (board.template != 'group1') continue
|
||||
for (const item of board.itemList) {
|
||||
if ((item.template != 'row1' && item.template != 'grid1' && !item.actionUrl) || !item.actionUrl.includes('rank-info')) continue
|
||||
|
||||
let data = item.displayLogId.param
|
||||
list.push({
|
||||
id: 'mg__' + data.rankId,
|
||||
name: data.rankName,
|
||||
bangid: String(data.rankId),
|
||||
})
|
||||
}
|
||||
}
|
||||
return list
|
||||
},
|
||||
async getBoards(retryNum = 0) {
|
||||
// if (++retryNum > 3) return Promise.reject(new Error('try max num'))
|
||||
// let response
|
||||
// try {
|
||||
// response = await this.getBoardsData()
|
||||
// } catch (error) {
|
||||
// return this.getBoards(retryNum)
|
||||
// }
|
||||
// // console.log(response.body.data.contentItemList)
|
||||
// if (response.statusCode !== 200 || response.body.code !== this.successCode) return this.getBoards(retryNum)
|
||||
// const list = this.filterBoardsData(response.body.data.contentItemList)
|
||||
// // console.log(list)
|
||||
// // console.log(JSON.stringify(list))
|
||||
// this.list = list
|
||||
// return {
|
||||
// list,
|
||||
// source: 'mg',
|
||||
// }
|
||||
this.list = boardList
|
||||
return {
|
||||
list: boardList,
|
||||
source: 'mg',
|
||||
}
|
||||
},
|
||||
getList(bangid, page, retryNum = 0) {
|
||||
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
|
||||
return this.getData(this.getUrl(bangid, page)).then(({ statusCode, body }) => {
|
||||
if (statusCode !== 200) return this.getList(bangid, page, retryNum)
|
||||
let listData = body.match(this.regExps.listData)
|
||||
if (!listData) return this.getList(bangid, page, retryNum)
|
||||
const datas = JSON.parse(RegExp.$1)
|
||||
// console.log(datas)
|
||||
listData = this.filterData(datas.songs.items)
|
||||
return {
|
||||
total: datas.songs.itemTotal,
|
||||
list: this.filterData(datas.songs.items),
|
||||
limit: this.limit,
|
||||
page,
|
||||
source: 'mg',
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { createHttpFetch } from './utils/index.js'
|
||||
|
||||
export default {
|
||||
requestObj: null,
|
||||
cancelTipSearch() {
|
||||
if (this.requestObj && this.requestObj.cancelHttp) this.requestObj.cancelHttp()
|
||||
},
|
||||
tipSearchBySong(str) {
|
||||
this.cancelTipSearch()
|
||||
this.requestObj = createHttpFetch(`https://music.migu.cn/v3/api/search/suggest?keyword=${encodeURIComponent(str)}`, {
|
||||
headers: {
|
||||
referer: 'https://music.migu.cn/v3',
|
||||
},
|
||||
})
|
||||
return this.requestObj.then(body => {
|
||||
return body.songs
|
||||
})
|
||||
},
|
||||
handleResult(rawData) {
|
||||
return rawData.map(info => `${info.name} - ${info.singerName}`)
|
||||
},
|
||||
async search(str) {
|
||||
return this.tipSearchBySong(str).then(result => this.handleResult(result))
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { httpFetch } from '../../request.js'
|
||||
|
||||
/**
|
||||
* 创建一个适用于MG的Http请求
|
||||
* @param {*} url
|
||||
* @param {*} options
|
||||
* @param {*} retryNum
|
||||
*/
|
||||
export const createHttpFetch = async(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 createHttpFetch(url, options, ++retryNum)
|
||||
}
|
||||
if (result.statusCode !== 200 ||
|
||||
(
|
||||
(result.body.code !== undefined
|
||||
? result.body.code
|
||||
: result.body.returnCode !== undefined
|
||||
? result.body.returnCode
|
||||
: result.body.code
|
||||
) !== '000000')
|
||||
) return createHttpFetch(url, options, ++retryNum)
|
||||
if (result.body.data) return result.body.data
|
||||
return result.body
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
|
||||
// const key = 'karakal@123Qcomyidongtiantianhaoting'
|
||||
const DELTA = 2654435769n
|
||||
const MIN_LENGTH = 32
|
||||
// const SPECIAL_CHAR = '0'
|
||||
const keyArr = [
|
||||
27303562373562475n,
|
||||
18014862372307051n,
|
||||
22799692160172081n,
|
||||
34058940340699235n,
|
||||
30962724186095721n,
|
||||
27303523720101991n,
|
||||
27303523720101998n,
|
||||
31244139033526382n,
|
||||
28992395054481524n,
|
||||
]
|
||||
|
||||
|
||||
const teaDecrypt = (data, key) => {
|
||||
const length = data.length
|
||||
const lengthBitint = BigInt(length)
|
||||
if (length >= 1) {
|
||||
// let j = data[data.length - 1];
|
||||
let j2 = data[0]
|
||||
let j3 = toLong((6n + (52n / lengthBitint)) * DELTA)
|
||||
while (true) {
|
||||
let j4 = j3
|
||||
if (j4 == 0n) break
|
||||
let j5 = toLong(3n & toLong(j4 >> 2n))
|
||||
let j6 = lengthBitint
|
||||
while (true) {
|
||||
j6--
|
||||
if (j6 > 0n) {
|
||||
let j7 = data[(j6 - 1n)]
|
||||
let i = j6
|
||||
j2 = toLong(data[i] - (toLong(toLong(j2 ^ j4) + toLong(j7 ^ key[toLong(toLong(3n & j6) ^ j5)])) ^ toLong(toLong(toLong(j7 >> 5n) ^ toLong(j2 << 2n)) + toLong(toLong(j2 >> 3n) ^ toLong(j7 << 4n)))))
|
||||
data[i] = j2
|
||||
} else break
|
||||
}
|
||||
let j8 = data[lengthBitint - 1n]
|
||||
j2 = toLong(data[0n] - toLong(toLong(toLong(key[toLong(toLong(j6 & 3n) ^ j5)] ^ j8) + toLong(j2 ^ j4)) ^ toLong(toLong(toLong(j8 >> 5n) ^ toLong(j2 << 2n)) + toLong(toLong(j2 >> 3n) ^ toLong(j8 << 4n)))))
|
||||
data[0] = j2
|
||||
j3 = toLong(j4 - DELTA)
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
const longArrToString = (data) => {
|
||||
const arrayList = []
|
||||
for (const j of data) arrayList.push(longToBytes(j).toString('utf16le'))
|
||||
return arrayList.join('')
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/29132118
|
||||
const longToBytes = (l) => {
|
||||
const result = Buffer.alloc(8)
|
||||
for (let i = 0; i < 8; i++) {
|
||||
result[i] = parseInt(l & 0xFFn)
|
||||
l >>= 8n
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
const toBigintArray = (data) => {
|
||||
const length = Math.floor(data.length / 16)
|
||||
const jArr = Array(length)
|
||||
for (let i = 0; i < length; i++) {
|
||||
jArr[i] = toLong(data.substring(i * 16, (i * 16) + 16))
|
||||
}
|
||||
return jArr
|
||||
}
|
||||
|
||||
// https://github.com/lyswhut/lx-music-desktop/issues/445#issuecomment-1139338682
|
||||
const MAX = 9223372036854775807n
|
||||
const MIN = -9223372036854775808n
|
||||
const toLong = str => {
|
||||
const num = typeof str == 'string' ? BigInt('0x' + str) : str
|
||||
if (num > MAX) return toLong(num - (1n << 64n))
|
||||
else if (num < MIN) return toLong(num + (1n << 64n))
|
||||
return num
|
||||
}
|
||||
|
||||
export const decrypt = (data) => {
|
||||
// console.log(data.length)
|
||||
// -3551594764563790630
|
||||
// console.log(toLongArrayFromArr(Buffer.from(key)))
|
||||
// console.log(teaDecrypt(toBigintArray(data), keyArr))
|
||||
// console.log(longArrToString(teaDecrypt(toBigintArray(data), keyArr)))
|
||||
// console.log(toByteArray(teaDecrypt(toBigintArray(data), keyArr)))
|
||||
return (data == null || data.length < MIN_LENGTH)
|
||||
? data
|
||||
: longArrToString(teaDecrypt(toBigintArray(data), keyArr))
|
||||
}
|
||||
|
||||
// console.log(14895149309145760986n - )
|
||||
// console.log(toLong('14895149309145760986'))
|
||||
// console.log(decrypt(str))
|
||||
// console.log(decrypt(str))
|
||||
// console.log(toByteArray([6048138644744000495n]))
|
||||
// console.log(toByteArray([16325999628386395n]))
|
||||
// console.log(toLong(90994076459972177136n))
|
||||
|
||||
Reference in new issue
Block a user