改变排行榜布局,新增更多排行榜

pull/277/head
lyswhut 2020-05-23 22:24:20 +08:00
parent a47be148c8
commit fe833033df
33 changed files with 815 additions and 368 deletions

80
package-lock.json generated
View File

@ -2262,74 +2262,6 @@
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
"dev": true
},
"autoprefixer": {
"version": "9.8.0",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.0.tgz",
"integrity": "sha512-D96ZiIHXbDmU02dBaemyAg53ez+6F5yZmapmgKcjm35yEe1uVDYI8hGW3VYoGRaG290ZFf91YxHrR518vC0u/A==",
"dev": true,
"requires": {
"browserslist": "^4.12.0",
"caniuse-lite": "^1.0.30001061",
"chalk": "^2.4.2",
"normalize-range": "^0.1.2",
"num2fraction": "^1.2.2",
"postcss": "^7.0.30",
"postcss-value-parser": "^4.1.0"
},
"dependencies": {
"caniuse-lite": {
"version": "1.0.30001062",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001062.tgz",
"integrity": "sha512-ei9ZqeOnN7edDrb24QfJ0OZicpEbsWxv7WusOiQGz/f2SfvBgHHbOEwBJ8HKGVSyx8Z6ndPjxzR6m0NQq+0bfw==",
"dev": true
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"postcss": {
"version": "7.0.30",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.30.tgz",
"integrity": "sha512-nu/0m+NtIzoubO+xdAlwZl/u5S5vi/y6BCsoL8D+8IxsD3XvBS8X4YEADNIVXKVuQvduiucnRv+vPIqj56EGMQ==",
"dev": true,
"requires": {
"chalk": "^2.4.2",
"source-map": "^0.6.1",
"supports-color": "^6.1.0"
},
"dependencies": {
"supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"postcss-value-parser": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
"integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
@ -10568,12 +10500,6 @@
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true
},
"normalize-range": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
"integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
"dev": true
},
"normalize-url": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
@ -10617,12 +10543,6 @@
"boolbase": "~1.0.0"
}
},
"num2fraction": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
"integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
"dev": true
},
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",

View File

@ -55,7 +55,7 @@
"lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-formatter-friendly --fix src"
},
"browserslist": [
"Electron 8.2.5"
"Electron 9.0.0"
],
"engines": {
"node": ">= 12"
@ -158,7 +158,6 @@
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/polyfill": "^7.8.7",
"@babel/preset-env": "^7.9.6",
"autoprefixer": "^9.8.0",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"babel-minify-webpack-plugin": "^0.3.1",

View File

@ -1,4 +1,4 @@
const autoprefixer = require('autoprefixer')
// const autoprefixer = require('autoprefixer')
const pxtorem = require('postcss-pxtorem')
module.exports = {
@ -28,6 +28,6 @@ module.exports = {
minPixelValue: 0,
exclude: [/node_modules/i],
}),
autoprefixer(),
// autoprefixer(),
],
}

View File

@ -4,6 +4,7 @@
- 新增当前音频输出设备改变时是否暂停播放的设置,默认关闭
- 新增歌曲列表右键菜单
- 新增自定义列表,创建列表的按钮在表头`#`左侧,鼠标移上去才会显示;编辑列表名字时,按`ESC`键可快速取消编辑,按回车键或使输入框失去焦点即可保存列表名字,右击列表可编辑已创建的列表,“试听列表”与“我的收藏”两个列表固定不可编辑
- 改变排行榜布局,新增更多排行榜
### 优化
@ -18,8 +19,14 @@
- 修复某些情况下无法开始下载任务的问题
- 修复 tab 组件边框溢出问题
- 修复错误更新试听列表外的歌曲时间的问题
- 修复网易音乐源歌单、排行榜歌曲列表加载显示的数量与实际不对的问题
- 修复歌曲图片链接没有扩展名的情况下无法嵌入图片的问题
### 更变
- 修改设置-列表-是否显示歌曲源的默认设置为选中(该变更不影响之前的设置)
- 移除浮动按钮,现在在多选完成后可鼠标右击随意一项在弹出的右键菜单中进行原来悬浮按钮的操作
### 其他
- 更新 Electron 到 9.0.0

View File

@ -16,7 +16,7 @@ electronDebug({
// Install `vue-devtools`
electron.app.on('ready', () => {
installExtension(VUEJS_DEVTOOLS)
.then(name => console.log(`Added Extension: ${name}`))
.then(info => console.log(`Added Extension: ${info.name}`))
.catch(err => console.log('An error occurred: ', err))
})

View File

@ -15,3 +15,5 @@ require('./tray')
require('./updateSetting')
require('./xm_verify')
require('./kw_decodeLyric')

View File

@ -0,0 +1,45 @@
const { inflate } = require('zlib')
const iconv = require('iconv-lite')
const { mainHandle } = require('../../common/ipc')
const handleInflate = data => new Promise((resolve, reject) => {
inflate(data, (err, result) => {
if (err) return reject(err)
resolve(result)
})
})
const buf_key = Buffer.from('yeelion')
const buf_key_len = buf_key.length
const decodeLyric = async(buf, isGetLyricx) => {
// const info = buf.slice(0, index).toString()
// if (!info.startsWith('tp=content')) return null
// const isLyric = info.includes('\r\nlrcx=0\r\n')
if (buf.toString('utf8', 0, 10) != 'tp=content') return ''
// const index = buf.indexOf('\r\n\r\n') + 4
const lrcData = await handleInflate(buf.slice(buf.indexOf('\r\n\r\n') + 4))
if (!isGetLyricx) return iconv.decode(lrcData, 'gb18030')
const buf_str = Buffer.from(lrcData.toString(), 'base64')
const buf_str_len = buf_str.length
const output = new Uint16Array(buf_str_len)
let i = 0
while (i < buf_str_len) {
let j = 0
while (j < buf_key_len && i < buf_str_len) {
output[i] = buf_str[i] ^ buf_key[j]
i++
j++
}
}
return iconv.decode(Buffer.from(output), 'gb18030')
}
mainHandle('kw_decodeLyric', async(event, { lrcBase64, isGetLyricx }) => {
if (!global.mainWindow) throw new Error('mainwindow is undefined')
const lrc = await decodeLyric(Buffer.from(lrcBase64, 'base64'), isGetLyricx)
return Buffer.from(lrc).toString('base64')
})

View File

@ -64,7 +64,8 @@ module.exports = (filePath, meta) => {
if (!/^http/.test(picUrl)) {
return writeMeta(filePath, meta)
}
let picPath = filePath.replace(/\.flac$/, '') + path.extname(picUrl).replace(extReg, '$1')
let ext = path.extname(picUrl)
let picPath = filePath.replace(/\.flac$/, '') + (ext ? ext.replace(extReg, '$1') : '.jpg')
request(picUrl)
.on('response', respones => {

View File

@ -10,7 +10,8 @@ module.exports = (filePath, meta) => {
delete meta.APIC
return NodeID3.write(meta, filePath)
}
let picPath = filePath.replace(/\.mp3$/, '') + path.extname(meta.APIC).replace(extReg, '$1')
let ext = path.extname(meta.APIC)
let picPath = filePath.replace(/\.mp3$/, '') + (ext ? ext.replace(extReg, '$1') : '.jpg')
request(meta.APIC)
.on('response', respones => {
if (respones.statusCode !== 200 && respones.statusCode != 206) {

View File

@ -47,7 +47,7 @@
"search_history_title": "是否显示历史搜索记录",
"search_history": "搜索历史",
"search_focus_search_box_title": "启动时是否自动聚焦搜索框",
"search_focus_search_box": "聚焦搜索框",
"search_focus_search_box": "启动时是否聚焦搜索框",
"list": "列表设置",
"list_source_title": "是否显示歌曲源",

View File

@ -45,7 +45,7 @@
"search_history_title": "是否顯示歷史搜索記錄",
"search_history": "搜索歷史",
"search_focus_search_box_title": "啟動時是否自動聚焦搜索框",
"search_focus_search_box": "聚焦搜索框",
"search_focus_search_box": "啟動時是否聚焦搜索框",
"list": "列表設置",
"list_source_title": "是否顯示歌曲源",
"list_source": "是否顯示歌曲源(僅對我的音樂分類有效)",

View File

@ -47,7 +47,7 @@
"search_history_title": "Select whether to show search history",
"search_history": "Search history",
"search_focus_search_box_title": "Whether the search box is automatically focused on startup",
"search_focus_search_box": "Focus Search Box",
"search_focus_search_box": "Whether the search box is focused on startup",
"list": "List",
"list_source_title": "Select whether to show music source",

View File

@ -3,13 +3,14 @@ const sourceList = {}
const sources = []
for (const source of music.sources) {
const leaderboard = music[source.id].leaderboard
if (!leaderboard) continue
sourceList[source.id] = leaderboard.list
if (!leaderboard || !leaderboard.getBoards) continue
sourceList[source.id] = []
sources.push(source)
}
// state
const state = {
boards: sourceList,
list: [],
total: 0,
page: 1,
@ -19,8 +20,11 @@ const state = {
// getters
const getters = {
sourceInfo(state, getters, rootState, { sourceNames }) {
return { sources: sources.map(item => ({ id: item.id, name: sourceNames[item.id] })), sourceList }
sources(state, getters, rootState, { sourceNames }) {
return sources.map(item => ({ id: item.id, name: sourceNames[item.id] }))
},
boards(state) {
return state.boards
},
list(state) {
return state.list
@ -36,18 +40,32 @@ const getters = {
// actions
const actions = {
getList({ state, rootState, commit }, page) {
getBoardsList({ state, rootState, commit }) {
// if (state.boards.length)
let source = rootState.setting.leaderboard.source
// let tabId = rootState.setting.leaderboard.tabId
// let key = `${source}${tabId}${page}`
// if (state.list.length && state.key == key) return true
// commit('clearList')
if (state.boards[source].length) return
return music[source].leaderboard.getBoards().then(result => commit('setBoardsList', { boards: result, source }))
},
getList({ state, rootState, commit }, page) {
// let source = rootState.setting.leaderboard.source
let tabId = rootState.setting.leaderboard.tabId
let [source, bangId] = tabId.split('__')
let key = `${source}${tabId}${page}`
if (state.list.length && state.key == key) return true
commit('clearList')
return music[source].leaderboard.getList(tabId, page).then(result => commit('setList', { result, key }))
return music[source].leaderboard.getList(bangId, page).then(result => commit('setList', { result, key }))
},
}
// mitations
const mutations = {
setBoardsList(state, { boards, source }) {
state.boards[source] = boards.list
},
setList(state, { result, key }) {
state.list = result.list
state.total = result.total

View File

@ -135,7 +135,13 @@ const mutations = {
state.listDetail.limit = result.limit
state.listDetail.page = page
state.listDetail.key = key
state.listDetail.info = result.info || {}
state.listDetail.info = result.info || {
name: state.selectListInfo.name,
img: state.selectListInfo.img,
desc: state.selectListInfo.desc,
author: state.selectListInfo.author,
play_count: state.selectListInfo.play_count,
}
cache.set(key, result)
},
setVisibleListDetail(state, bool) {

View File

@ -2,6 +2,22 @@ import { httpFetch } from '../../request'
// import { formatPlayTime } from '../../index'
// import jshtmlencode from 'js-htmlencode'
const boardList = [
// { id: 'bd__601', name: '歌单榜', bangid: '601' },
{ id: 'bd__2', name: '热歌榜', bangid: '2' },
{ id: 'bd__20', name: '华语金曲榜', bangid: '20' },
{ id: 'bd__25', name: '网络歌曲榜', bangid: '25' },
{ id: 'bd__1', name: '新歌榜', bangid: '1' },
{ id: 'bd__21', name: '欧美金曲榜', bangid: '21' },
{ id: 'bd__200', name: '原创音乐榜', bangid: '200' },
{ id: 'bd__22', name: '经典老歌榜', bangid: '22' },
{ id: 'bd__24', name: '影视金曲榜', bangid: '24' },
{ id: 'bd__23', name: '情歌对唱榜', bangid: '23' },
{ id: 'bd__11', name: '摇滚榜', bangid: '11' },
{ id: 'bd__105', name: '好童星榜', bangid: '105' },
{ id: 'bd__106', name: '雅克•藏羌彝原创音乐榜', bangid: '106' },
]
export default {
limit: 20,
list: [
@ -111,14 +127,20 @@ export default {
// return rawData.map(item => JSON.parse(item.replace(this.regExps.item, '$1').replace(/&quot;/g, '"').replace(/\\\//g, '/').replace(/(@s_1,w_)\d+(,h_)\d+/, '$1500$2500')))
return rawData.map(item => JSON.parse(item.replace(this.regExps.item, '$1').replace(/&quot;/g, '"').replace(/\\\//g, '/')))
},
getList(id, page) {
let type = this.list.find(s => s.id === id)
if (!type) return Promise.reject()
return this.getData(this.getUrl(type.bangid, page)).then(({ body }) => {
async getBoards(retryNum = 0) {
this.list = boardList
return {
list: boardList,
source: 'bd',
}
},
getList(bangid, page, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
return this.getData(this.getUrl(bangid, page)).then(({ body }) => {
let result = body.match(this.regExps.item)
if (!result) return Promise.reject(new Error('匹配list失败'))
if (!result) return this.getList(bangid, page, retryNum)
let info = body.match(this.regExps.info)
if (!info) return Promise.reject(new Error('匹配info失败'))
if (!info) return this.getList(bangid, page, retryNum)
const list = this.filterData(this.parseData(result))
this.limit = parseInt(info[2])
return {

View File

@ -8,7 +8,7 @@ export default {
_requestObj_listRecommend: null,
_requestObj_listDetail: null,
limit_list: 20,
limit_song: 1000,
limit_song: 10000,
successCode: 22000,
sortList: [
{

View File

@ -1,6 +1,8 @@
import { httpGet, cancelHttp } from '../../request'
import { httpGet, cancelHttp, httpFetch } from '../../request'
import { formatPlayTime, sizeFormate } from '../../index'
let boardList = [{ id: 'kg__6666', name: '酷狗飙升榜', bangid: '6666' }, { id: 'kg__8888', name: '酷狗TOP500', bangid: '8888' }, { id: 'kg__37361', name: '酷狗雷达榜', bangid: '37361' }, { id: 'kg__23784', name: '网络红歌榜', bangid: '23784' }, { id: 'kg__24971', name: 'DJ热歌榜', bangid: '24971' }, { id: 'kg__35811', name: '会员专享热歌榜', bangid: '35811' }, { id: 'kg__31308', name: '华语新歌榜', bangid: '31308' }, { id: 'kg__31310', name: '欧美新歌榜', bangid: '31310' }, { id: 'kg__31311', name: '韩国新歌榜', bangid: '31311' }, { id: 'kg__31312', name: '日本新歌榜', bangid: '31312' }, { id: 'kg__31313', name: '粤语新歌榜', bangid: '31313' }, { id: 'kg__33162', name: 'ACG新歌榜', bangid: '33162' }, { id: 'kg__21101', name: '酷狗分享榜', bangid: '21101' }, { id: 'kg__30972', name: '腾讯音乐人原创榜', bangid: '30972' }, { id: 'kg__22603', name: '5sing音乐榜', bangid: '22603' }, { id: 'kg__33160', name: '电音热歌榜', bangid: '33160' }, { id: 'kg__21335', name: '繁星音乐榜', bangid: '21335' }, { id: 'kg__33161', name: '古风新歌榜', bangid: '33161' }, { id: 'kg__33163', name: '影视金曲榜', bangid: '33163' }, { id: 'kg__33166', name: '欧美金曲榜', bangid: '33166' }, { id: 'kg__33165', name: '粤语金曲榜', bangid: '33165' }, { id: 'kg__36107', name: '小语种热歌榜', bangid: '36107' }, { id: 'kg__4681', name: '美国BillBoard榜', bangid: '4681' }, { id: 'kg__4680', name: '英国单曲榜', bangid: '4680' }, { id: 'kg__4673', name: '日本公信榜', bangid: '4673' }, { id: 'kg__38623', name: '韩国Melon音乐榜', bangid: '38623' }, { id: 'kg__42807', name: 'joox本地热歌榜', bangid: '42807' }, { id: 'kg__42808', name: '台湾KKBOX风云榜', bangid: '42808' }]
export default {
list: [
{
@ -53,11 +55,11 @@ export default {
name: 'DJ热歌榜',
bangid: '24971',
},
// {
// id: 'kghyxgb',
// name: '华语新歌榜',
// bangid: '31308',
// },
{
id: 'kghyxgb',
name: '华语新歌榜',
bangid: '31308',
},
],
getUrl(p, id) {
return `http://www2.kugou.kugou.com/yueku/v9/rank/home/${p}-${id}.html`
@ -68,8 +70,14 @@ export default {
limit: /pagesize: '(\d+)',/,
listData: /global\.features = (\[.+\]);/,
},
_requestBoardsObj: null,
_requestObj: null,
_cancelPromiseCancelFn: null,
getBoardsData() {
if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp()
this._requestBoardsObj = httpFetch('http://mobilecdnbj.kugou.com/api/v3/rank/list?version=9108&plat=0&showtype=2&parentid=0&apiver=6&area_code=1&withsong=1')
return this._requestBoardsObj.promise
},
getData(url) {
if (this._requestObj != null) {
cancelHttp(this._requestObj)
@ -142,10 +150,45 @@ export default {
}
})
},
getList(id, page) {
let type = this.list.find(s => s.id === id)
if (!type) return Promise.reject()
return this.getData(this.getUrl(page, type.bangid)).then(html => {
filterBoardsData(rawList) {
// console.log(rawList)
let list = []
for (const board of rawList) {
if (board.isvol != 1) continue
list.push({
id: 'kg__' + board.rankid,
name: board.rankname,
bangid: String(board.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)
// if (response.statusCode !== 200 || response.body.errcode !== 0) return this.getBoards(retryNum)
// const list = this.filterBoardsData(response.body.data.info)
// // console.log(list)
// this.list = list
// return {
// list,
// source: 'kg',
// }
this.list = boardList
return {
list: boardList,
source: 'kg',
}
},
getList(bangid, page) {
return this.getData(this.getUrl(page, bangid)).then(html => {
let total = html.match(this.regExps.total)
if (total) total = parseInt(RegExp.$1)
page = html.match(this.regExps.page)

View File

@ -8,7 +8,7 @@ export default {
_requestObj_list: null,
_requestObj_listRecommend: null,
_requestObj_listDetail: null,
listDetailLimit: 100,
listDetailLimit: 10000,
currentTagInfo: {
id: undefined,
info: undefined,

View File

@ -36,9 +36,9 @@ const kw = {
leaderboard,
songList,
hotSearch,
getLyric(songInfo) {
getLyric(songInfo, isGetLyricx) {
// let singer = songInfo.singer.indexOf('、') > -1 ? songInfo.singer.split('、')[0] : songInfo.singer
return lyric.getLyric(songInfo.songmid)
return lyric.getLyric(songInfo.songmid, isGetLyricx)
},
handleMusicInfo(songInfo) {
return this.getMusicInfo(songInfo).then(info => {

View File

@ -1,6 +1,8 @@
import { httpGet, cancelHttp } from '../../request'
import { httpGet, cancelHttp, httpFetch } from '../../request'
import { formatPlayTime, decodeName } from '../../index'
import { formatSinger, getToken, matchToken } from './util'
import { formatSinger } from './util'
const boardList = [{ id: 'kw__93', name: '酷我飙升榜', bangid: '93' }, { id: 'kw__17', name: '酷我新歌榜', bangid: '17' }, { id: 'kw__16', name: '酷我热歌榜', bangid: '16' }, { id: 'kw__158', name: '抖音热歌榜', bangid: '158' }, { id: 'kw__284', name: '酷我热评榜', bangid: '284' }, { id: 'kw__290', name: 'ACG新歌榜', bangid: '290' }, { id: 'kw__286', name: '台湾KKBOX榜', bangid: '286' }, { id: 'kw__279', name: '春日浅唱榜', bangid: '279' }, { id: 'kw__281', name: '巴士随身听榜', bangid: '281' }, { id: 'kw__255', name: 'KTV点唱榜', bangid: '255' }, { id: 'kw__280', name: '家务进行曲榜', bangid: '280' }, { id: 'kw__282', name: '熬夜修仙榜', bangid: '282' }, { id: 'kw__283', name: '枕边轻音乐榜', bangid: '283' }, { id: 'kw__278', name: '古风音乐榜', bangid: '278' }, { id: 'kw__264', name: 'Vlog音乐榜', bangid: '264' }, { id: 'kw__242', name: '酷我电音榜', bangid: '242' }, { id: 'kw__187', name: '流行趋势榜', bangid: '187' }, { id: 'kw__204', name: '现场音乐榜', bangid: '204' }, { id: 'kw__186', name: 'ACG神曲榜', bangid: '186' }, { id: 'kw__185', name: '最强翻唱榜', bangid: '185' }, { id: 'kw__26', name: '经典怀旧榜', bangid: '26' }, { id: 'kw__104', name: '酷我华语榜', bangid: '104' }, { id: 'kw__182', name: '酷我粤语榜', bangid: '182' }, { id: 'kw__22', name: '酷我欧美榜', bangid: '22' }, { id: 'kw__184', name: '酷我韩语榜', bangid: '184' }, { id: 'kw__183', name: '酷我日语榜', bangid: '183' }, { id: 'kw__145', name: '会员畅听榜', bangid: '145' }, { id: 'kw__153', name: '网红新歌榜', bangid: '153' }, { id: 'kw__64', name: '影视金曲榜', bangid: '64' }, { id: 'kw__176', name: 'DJ嗨歌榜', bangid: '176' }, { id: 'kw__106', name: '酷我真声音', bangid: '106' }, { id: 'kw__12', name: 'Billboard榜', bangid: '12' }, { id: 'kw__49', name: 'iTunes音乐榜', bangid: '49' }, { id: 'kw__180', name: 'beatport电音榜', bangid: '180' }, { id: 'kw__13', name: '英国UK榜', bangid: '13' }, { id: 'kw__164', name: '百大DJ榜', bangid: '164' }, { id: 'kw__246', name: 'YouTube音乐排行榜', bangid: '246' }, { id: 'kw__265', name: '韩国Genie榜', bangid: '265' }, { id: 'kw__14', name: '韩国M-net榜', bangid: '14' }, { id: 'kw__8', name: '香港电台榜', bangid: '8' }, { id: 'kw__15', name: '日本公信榜', bangid: '15' }, { id: 'kw__151', name: '腾讯音乐人原创榜', bangid: '151' }]
export default {
list: [
@ -61,15 +63,19 @@ export default {
},
],
getUrl: (p, l, id) => `http://kbangserver.kuwo.cn/ksong.s?from=pc&fmt=json&pn=${p - 1}&rn=${l}&type=bang&data=content&id=${id}&show_copyright_off=0&pcmp4=1&isbang=1`,
getUrl2: (p, l, id) => `http://www.kuwo.cn/api/www/bang/bang/musicList?bangId=${id}&pn=${p}&rn=${l}`,
regExps: {
},
limit: 30,
limit: 100,
_requestBoardsObj: null,
_cancelRequestObj: null,
_cancelPromiseCancelFn: null,
_cancelRequestObj2: null,
_cancelPromiseCancelFn2: null,
getBoardsData() {
if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp()
this._requestBoardsObj = httpFetch('http://qukudata.kuwo.cn/q.k?op=query&cont=tree&node=2&pn=0&rn=1000&fmt=json&level=2')
return this._requestBoardsObj.promise
},
getData(url) {
if (this._cancelRequestObj != null) {
cancelHttp(this._cancelRequestObj)
@ -77,45 +83,18 @@ export default {
}
return new Promise((resolve, reject) => {
this._cancelPromiseCancelFn = reject
this._cancelRequestObj = httpGet(url, (err, resp, body) => {
this._cancelRequestObj = httpGet(url, (err, resp) => {
this._cancelRequestObj = null
this._cancelPromiseCancelFn = null
if (err) {
console.log(err)
reject(err)
}
resolve(body)
resolve(resp)
})
})
},
async getData2(url) {
if (this._cancelRequestObj2 != null) {
cancelHttp(this._cancelRequestObj2)
this._cancelPromiseCancelFn2(new Error('取消http请求'))
}
let token = window.kw_token.token
if (!token) token = await getToken()
return new Promise((resolve, reject) => {
this._cancelPromiseCancelFn2 = reject
this._cancelRequestObj2 = httpGet(url, {
headers: {
Referer: 'http://www.kuwo.cn/',
csrf: token,
cookie: 'kw_token=' + token,
},
}, (err, resp, body) => {
this._cancelRequestObj2 = null
this._cancelPromiseCancelFn2 = null
if (err) {
console.log(err)
return reject(err)
}
window.kw_token.token = matchToken(resp.headers)
resolve(body)
})
})
},
filterData(rawList, rawList2) {
filterData(rawList) {
// console.log(rawList)
// console.log(rawList.length, rawList2.length)
return rawList.map((item, inedx) => {
@ -160,7 +139,7 @@ export default {
albumId: item.albumid,
songmid: item.id,
source: 'kw',
interval: rawList2[inedx] && formatPlayTime(rawList2[inedx].duration),
interval: formatPlayTime(parseInt(item.song_duration)),
img: item.pic,
lrc: null,
types,
@ -169,29 +148,53 @@ export default {
}
})
},
loadData(p1, p2, page, bangid, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
return Promise.all([p1, p2]).then(([data1, data2]) => {
// console.log(data1, data2)
if (!data1.musiclist.length) {
return this.loadData(this.getData(this.getUrl(page, this.limit, bangid)),
data2.data.musicList.length
? Promise.resolve(data2)
: this.getData2(this.getUrl2(page, this.limit, bangid)), page, bangid, retryNum)
}
if (!data2.data.musicList.length) {
return this.loadData(Promise.resolve(data1), this.getData2(this.getUrl2(page, this.limit, bangid)), page, bangid, retryNum)
}
return Promise.resolve([data1, data2])
})
filterBoardsData(rawList) {
// console.log(rawList)
let list = []
for (const board of rawList) {
if (board.source != '1') continue
list.push({
id: 'kw__' + board.sourceid,
name: board.name,
bangid: String(board.sourceid),
})
}
return list
},
getList(id, page) {
let type = this.list.find(s => s.id === id)
if (!type) return Promise.reject()
return this.loadData(this.getData(this.getUrl(page, this.limit, type.bangid)), this.getData2(this.getUrl2(page, this.limit, type.bangid)), page, type.bangid).then(([data1, data2]) => {
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)
// if (response.statusCode !== 200 || !response.body.child) return this.getBoards(retryNum)
// const list = this.filterBoardsData(response.body.child)
// // console.log(list)
// console.log(JSON.stringify(list))
// this.list = list
// return {
// list,
// source: 'kw',
// }
this.list = boardList
return {
list: boardList,
source: 'kw',
}
},
getList(id, page, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
return this.getData(this.getUrl(page, this.limit, id)).then(({ statusCode, body }) => {
// console.log(body)
if (statusCode !== 200 || !body.musiclist) return this.getList(id, page, retryNum)
// console.log(data1.musiclist, data2.data)
let total = parseInt(data1.num)
let list = this.filterData(data1.musiclist, data2.data.musicList)
let total = parseInt(body.num)
let list = this.filterData(body.musiclist)
return {
total,
list,

View File

@ -1,19 +1,26 @@
import { httpFetch } from '../../request'
import { decodeName } from '../../index'
import { decodeLyric } from './util'
export default {
formatTime(time) {
let m = parseInt(time / 60)
let s = (time % 60).toFixed(2)
return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s)
lrcInfoRxp: /<lyric>(.+?)<\/lyric>[\s\S]+<lyric_zz>(.+?)<\/lyric_zz>/,
parseLyricInfo(str) {
let result = str.match(this.lrcInfoRxp)
if (!result) return null
return result ? { lyric: result[1], lyric_zz: result[2] } : null
},
transformLrc({ songinfo, lrclist }) {
return `[ti:${songinfo.songName}]\n[ar:${songinfo.artist}]\n[al:${songinfo.album}]\n[by:]\n[offset:0]\n${lrclist ? lrclist.map(l => `[${this.formatTime(l.time)}]${l.lineLyric}\n`).join('') : '暂无歌词'}`
},
getLyric(songId) {
const requestObj = httpFetch(`http://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId=${songId}`)
requestObj.promise = requestObj.promise.then(({ body }) => {
return decodeName(this.transformLrc(body.data))
getLyric(songId, isGetLyricx = false) {
const requestObj = httpFetch(`http://player.kuwo.cn/webmusic/st/getNewMuiseByRid?rid=MUSIC_${songId}`)
requestObj.promise = requestObj.promise.then(({ statusCode, body }) => {
if (statusCode != 200) return Promise.reject(new Error(JSON.stringify(body)))
let info = this.parseLyricInfo(body)
if (!info) return Promise.reject(new Error(JSON.stringify(body)))
Object.assign(requestObj, httpFetch(`http://newlyric.kuwo.cn/newlyric.lrc?${isGetLyricx ? info.lyric_zz : info.lyric}`))
return requestObj.promise.then(({ statusCode, body, raw }) => {
if (statusCode != 200) return Promise.reject(new Error(JSON.stringify(body)))
return decodeLyric({ lrcBase64: raw.toString('base64'), isGetLyricx }).then(base64 => {
return Buffer.from(base64, 'base64').toString()
})
})
})
return requestObj
},

View File

@ -8,7 +8,7 @@ export default {
_requestObj_list: null,
_requestObj_listDetail: null,
limit_list: 25,
limit_song: 1000,
limit_song: 10000,
successCode: 200,
sortList: [
{

View File

@ -1,4 +1,5 @@
import { httpGet } from '../../request'
import { rendererInvoke } from '../../../../common/ipc'
if (!window.kw_token) {
window.kw_token = {
@ -32,3 +33,5 @@ export const getToken = () => new Promise((resolve, reject) => {
resolve(token)
})
})
export const decodeLyric = base64Data => rendererInvoke('kw_decodeLyric', base64Data)

View File

@ -1,14 +1,16 @@
import { httpFetch } from '../../request'
// import { formatPlayTime } from '../../index'
import { sizeFormate } from '../../index'
// import jshtmlencode from 'js-htmlencode'
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' }]
export default {
limit: 200,
list: [
{
id: 'mgyyb',
name: '音乐榜',
bangid: '23603703',
bangid: '27553319',
},
{
id: 'mgysb',
@ -57,74 +59,149 @@ export default {
},
],
getUrl(id, page) {
return `http://m.music.migu.cn/migu/remoting/cms_list_tag?nid=${id}&pageSize=${this.limit}&pageNo=${page - 1}`
return `https://app.c.nf.migu.cn/MIGUM3.0/v1.0/template/rank-detail/release?columnId=${id}`
// return `http://m.music.migu.cn/migu/remoting/cms_list_tag?nid=${id}&pageSize=${this.limit}&pageNo=${page - 1}`
},
successCode: '000000',
requestBoardsObj: null,
requestObj: null,
getBoardsData() {
if (this.requestBoardsObj) this._requestBoardsObj.cancelHttp()
this.requestBoardsObj = httpFetch('https://app.c.nf.migu.cn/MIGUM2.0/v2.0/content/indexrank.do?templateVersion=8', {
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.requestBoardsObj.promise
},
getData(url) {
if (this.requestObj) this.requestObj.cancelHttp()
this.requestObj = httpFetch(url)
return this.requestObj.promise
},
filterData(rawList) {
// console.log(rawList)
getSinger(singers) {
let arr = []
singers.forEach(singer => {
arr.push(singer.name)
})
return arr.join('、')
},
filterData(rawData) {
// console.log(JSON.stringify(rawData))
// console.log(rawData)
let ids = new Set()
const list = []
rawList.forEach(({ songData }) => {
if (!songData) return
if (ids.has(songData.copyrightId)) return
ids.add(songData.copyrightId)
rawData.forEach(item => {
if (ids.has(item.copyrightId)) return
ids.add(item.copyrightId)
const types = []
const _types = {}
let size = null
types.push({ type: '128k', size })
_types['128k'] = {
size,
}
if (songData.hasHQqq === '1') {
types.push({ type: '320k', size })
_types['320k'] = {
size,
item.rateFormats && item.rateFormats.forEach(type => {
let size
switch (type.formatType) {
case 'PQ':
size = sizeFormate(type.size)
types.push({ type: '128k', size })
_types['128k'] = {
size,
}
break
case 'HQ':
size = sizeFormate(type.size)
types.push({ type: '320k', size })
_types['320k'] = {
size,
}
break
case 'SQ':
size = sizeFormate(type.size)
types.push({ type: 'flac', size })
_types.flac = {
size,
}
break
}
}
if (songData.hasSQqq === '1') {
types.push({ type: 'flac', size })
_types.flac = {
size,
}
}
// types.reverse()
})
list.push({
singer: songData.singerName.join('、'),
name: songData.songName,
// albumName: songData.album_title,
// albumId: songData.album_id,
singer: this.getSinger(item.artists),
name: item.songName,
albumName: item.album,
albumId: item.albumId,
songmid: item.copyrightId,
copyrightId: item.copyrightId,
source: 'mg',
interval: null,
songmid: songData.copyrightId,
copyrightId: songData.copyrightId,
img: songData.picL || songData.M || songData.picS,
img: item.albumImgs && item.albumImgs.length ? item.albumImgs[0].img : null,
lrc: null,
lrcUrl: item.lrcUrl,
types,
_types,
typeUrl: {},
})
})
return list
},
getList(id, page) {
let type = this.list.find(s => s.id === id)
if (!type) return Promise.reject()
return this.getData(this.getUrl(type.bangid, page)).then(({ statusCode, body }) => {
if (statusCode !== 200) return Promise.reject(new Error('获取列表失败'))
const list = this.filterData(body.result.results)
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 }) => {
// console.log(body)
if (statusCode !== 200 || body.code !== this.successCode) return this.getList(bangid, page, retryNum)
const list = this.filterData(body.data.columnInfo.dataList)
return {
total: body.result.totalCount,
total: list.length,
list,
limit: body.result.pageSize,
limit: this.limit,
page,
source: 'mg',
}

View File

@ -6,16 +6,18 @@ export default {
_requestObj_list: null,
_requestObj_listDetail: null,
limit_list: 10,
limit_song: 1000,
limit_song: 10000,
successCode: '000000',
sortList: [
{
name: '推荐',
id: '15127315',
// id: '1',
},
{
name: '最新',
id: '15127272',
// id: '2',
},
],
regExps: {
@ -34,8 +36,12 @@ export default {
// }
// return `http://music.migu.cn/v3/music/playlist?tagId=${tagId}&page=${page}&from=migu`
if (tagId == null) {
// return `http://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}`
// http://app.c.nf.migu.cn/MIGUM2.0/v2.0/content/getMusicData.do?count=50&start=2&templateVersion=5&type=1
return `http://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/MIGUM2.0/v2.0/content/getMusicData.do?area=2&count=${this.limit_list}&start=${page}&tags=${tagId}&templateVersion=5&type=3`
return `http://m.music.migu.cn/migu/remoting/playlist_bycolumnid_tag?playListType=2&type=1&tagId=${tagId}&startIndex=${(page - 1) * 10}`
},
getSongListDetailUrl(id, page) {
@ -68,19 +74,20 @@ export default {
return this._requestObj_listDetail.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getListDetail(id, page, ++tryNum)
// console.log(JSON.stringify(body))
console.log(body)
return {
list: this.filterListDetail(body.list),
page,
limit: this.limit_song,
total: body.totalCount,
source: 'mg',
info: {
// name: body.result.info.list_title,
// img: body.result.info.list_pic,
// desc: body.result.info.list_desc,
// author: body.result.info.userinfo.username,
// play_count: this.formatPlayCount(body.result.listen_num),
},
// info: {
// // name: body.result.info.list_title,
// // img: body.result.info.list_pic,
// // desc: body.result.info.list_desc,
// // author: body.result.info.userinfo.username,
// // play_count: this.formatPlayCount(body.result.listen_num),
// },
}
})
},
@ -145,7 +152,18 @@ export default {
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))
this._requestObj_list = httpFetch(this.getSongListUrl(sortId, tagId, page), {
// 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)
@ -167,6 +185,7 @@ export default {
// })
return this._requestObj_list.promise.then(({ body }) => {
if (body.retCode !== '100000' || body.retMsg.code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum)
// console.log(body)
return {
list: this.filterList(body.retMsg.playlist),
total: parseInt(body.retMsg.countSize),
@ -175,6 +194,18 @@ export default {
source: 'mg',
}
})
// return this._requestObj_list.promise.then(({ body }) => {
// if (body.retCode !== '100000') return this.getList(sortId, tagId, page, ++tryNum)
// // if (body.code !== '000000') return this.getList(sortId, tagId, page, ++tryNum)
// console.log(body)
// // return {
// // list: this.filterList(body.data.contentItemList[0].itemList),
// // total: parseInt(body.retMsg.countSize),
// // page,
// // limit: this.limit_list,
// // source: 'mg',
// // }
// })
},
filterList(rawData) {
return rawData.map(item => ({

View File

@ -1,6 +1,8 @@
import { httpGet, cancelHttp } from '../../request'
import { httpGet, cancelHttp, httpFetch } from '../../request'
import { formatPlayTime, sizeFormate } from '../../index'
let boardList = [{ id: 'tx__4', name: '流行指数榜', bangid: '4' }, { id: 'tx__26', name: '热歌榜', bangid: '26' }, { id: 'tx__27', name: '新歌榜', bangid: '27' }, { id: 'tx__62', name: '飙升榜', bangid: '62' }, { id: 'tx__58', name: '说唱榜', bangid: '58' }, { id: 'tx__57', name: '电音榜', bangid: '57' }, { id: 'tx__28', name: '网络歌曲榜', bangid: '28' }, { id: 'tx__5', name: '内地榜', bangid: '5' }, { id: 'tx__3', name: '欧美榜', bangid: '3' }, { id: 'tx__59', name: '香港地区榜', bangid: '59' }, { id: 'tx__16', name: '韩国榜', bangid: '16' }, { id: 'tx__60', name: '抖音排行榜', bangid: '60' }, { id: 'tx__29', name: '影视金曲榜', bangid: '29' }, { id: 'tx__17', name: '日本榜', bangid: '17' }, { id: 'tx__52', name: '腾讯音乐人原创榜', bangid: '52' }, { id: 'tx__36', name: 'K歌金曲榜', bangid: '36' }, { id: 'tx__61', name: '台湾地区榜', bangid: '61' }, { id: 'tx__63', name: 'DJ舞曲榜', bangid: '63' }, { id: 'tx__64', name: '综艺新歌榜', bangid: '64' }, { id: 'tx__65', name: '国风热歌榜', bangid: '65' }, { id: 'tx__66', name: 'ACG新歌榜', bangid: '66' }, { id: 'tx__67', name: '听歌识曲榜', bangid: '67' }, { id: 'tx__70', name: '达人音乐榜', bangid: '70' }]
export default {
limit: 300,
list: [
@ -54,11 +56,11 @@ export default {
name: '日本榜',
bangid: 17,
},
// {
// id: 'txtybb',
// name: 'YouTube榜',
// bangid: 128,
// },
{
id: 'txtybb',
name: 'YouTube榜',
bangid: 128,
},
],
getUrl(id, period, limit) {
return `https://u.y.qq.com/cgi-bin/musicu.fcg?format=json&inCharset=utf8&outCharset=utf-8&platform=yqq.json&needNewCode=0&data=${encodeURIComponent(JSON.stringify({
@ -83,8 +85,14 @@ export default {
},
periods: {},
periodUrl: 'https://c.y.qq.com/node/pc/wk_v15/top.html',
_requestBoardsObj: null,
_cancelRequestObj: null,
_cancelPromiseCancelFn: null,
getBoardsData() {
if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp()
this._requestBoardsObj = httpFetch('https://c.y.qq.com/v8/fcg-bin/fcg_myqq_toplist.fcg?g_tk=1928093487&inCharset=utf-8&outCharset=utf-8&notice=0&format=json&uin=0&needNewCode=1&platform=h5')
return this._requestBoardsObj.promise
},
getData(url) {
if (this._cancelRequestObj != null) {
cancelHttp(this._cancelRequestObj)
@ -182,15 +190,57 @@ export default {
return info && info.period
})
},
getList(id, page) {
let type = this.list.find(s => s.id === id)
if (!type) return Promise.reject()
let info = this.periods[type.bangid]
let p = info ? Promise.resolve(info.period) : this.getPeriods(type.bangid)
filterBoardsData(rawList) {
// console.log(rawList)
let list = []
for (const board of rawList) {
// 排除 MV榜
if (board.id == 201) continue
if (board.topTitle.startsWith('巅峰榜·')) {
board.topTitle = board.topTitle.substring(4, board.topTitle.length)
}
if (!board.topTitle.endsWith('榜')) board.topTitle += '榜'
list.push({
id: 'tx__' + board.id,
name: board.topTitle,
bangid: String(board.id),
})
}
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)
// if (response.statusCode !== 200 || response.body.code !== 0) return this.getBoards(retryNum)
// const list = this.filterBoardsData(response.body.data.topList)
// // console.log(list)
// this.list = list
// return {
// list,
// source: 'tx',
// }
this.list = boardList
return {
list: boardList,
source: 'tx',
}
},
getList(bangid, page, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
bangid = parseInt(bangid)
let info = this.periods[bangid]
let p = info ? Promise.resolve(info.period) : this.getPeriods(bangid)
return p.then(period => {
return this.getData(this.getUrl(type.bangid, period, this.limit)).then(data => {
return this.getData(this.getUrl(bangid, period, this.limit)).then(data => {
// console.log(data)
if (data.code !== 0) return Promise.reject()
if (data.code !== 0) return this.getList(bangid, page, retryNum)
return {
total: data.toplist.data.songInfoList.length,
list: this.filterData(data.toplist.data.songInfoList),

View File

@ -9,7 +9,7 @@ export default {
_requestObj_listDetail: null,
_requestObj_listDetailLink: null,
limit_list: 36,
limit_song: 10000000,
limit_song: 100000,
successCode: 0,
sortList: [
{

View File

@ -1,8 +1,50 @@
import { httpGet, cancelHttp } from '../../request'
import { formatPlayTime } from '../../index'
import { weapi } from './utils/crypto'
import { httpFetch } from '../../request'
import musicDetailApi from './musicDetail'
const topList = [
{ id: 'wy__19723756', bangid: '19723756', name: '云音乐飙升榜' },
{ id: 'wy__3778678', bangid: '3778678', name: '云音乐热歌榜' },
{ id: 'wy__3779629', bangid: '3779629', name: '云音乐新歌榜' },
{ id: 'wy__2884035', bangid: '2884035', name: '云音乐原创榜' },
{ id: 'wy__2250011882', bangid: '2250011882', name: '抖音排行榜' },
{ id: 'wy__1978921795', bangid: '1978921795', name: '云音乐电音榜' },
{ id: 'wy__4395559', bangid: '4395559', name: '华语金曲榜' },
{ id: 'wy__71384707', bangid: '71384707', name: '云音乐古典音乐榜' },
{ id: 'wy__10520166', bangid: '10520166', name: '云音乐国电榜' },
{ id: 'wy__2006508653', bangid: '2006508653', name: '电竞音乐榜' },
{ id: 'wy__991319590', bangid: '991319590', name: '云音乐说唱榜' },
{ id: 'wy__180106', bangid: '180106', name: 'UK排行榜周榜' },
{ id: 'wy__60198', bangid: '60198', name: '美国Billboard周榜' },
{ id: '21845217', bangid: '21845217', name: 'KTV嗨榜' },
{ id: 'wy__11641012', bangid: '11641012', name: 'iTunes榜' },
{ id: 'wy__120001', bangid: '120001', name: 'Hit FM Top榜' },
{ id: 'wy__60131', bangid: '60131', name: '日本Oricon周榜' },
{ id: 'wy__3733003', bangid: '3733003', name: '韩国Melon排行榜周榜' },
{ id: 'wy__60255', bangid: '60255', name: '韩国Mnet排行榜周榜' },
{ id: 'wy__46772709', bangid: '46772709', name: '韩国Melon原声周榜' },
{ id: 'wy__64016', bangid: '64016', name: '中国TOP排行榜(内地榜)' },
{ id: 'wy__112504', bangid: '112504', name: '中国TOP排行榜(港台榜)' },
{ id: 'wy__3112516681', bangid: '3112516681', name: '中国新乡村音乐排行榜' },
{ id: 'wy__10169002', bangid: '10169002', name: '香港电台中文歌曲龙虎榜' },
{ id: 'wy__27135204', bangid: '27135204', name: '法国 NRJ EuroHot 30周榜' },
{ id: 'wy__1899724', bangid: '1899724', name: '中国嘻哈榜' },
{ id: 'wy__112463', bangid: '112463', name: '台湾Hito排行榜' },
{ id: 'wy__3812895', bangid: '3812895', name: 'Beatport全球电子舞曲榜' },
{ id: 'wy__2617766278', bangid: '2617766278', name: '新声榜' },
{ id: 'wy__745956260', bangid: '745956260', name: '云音乐韩语榜' },
{ id: 'wy__2847251561', bangid: '2847251561', name: '说唱TOP榜' },
{ id: 'wy__2023401535', bangid: '2023401535', name: '英国Q杂志中文版周榜' },
{ id: 'wy__2809513713', bangid: '2809513713', name: '云音乐欧美热歌榜' },
{ id: 'wy__2809577409', bangid: '2809577409', name: '云音乐欧美新歌榜' },
{ id: 'wy__71385702', bangid: '71385702', name: '云音乐ACG音乐榜' },
{ id: 'wy__3001835560', bangid: '3001835560', name: '云音乐ACG动画榜' },
{ id: 'wy__3001795926', bangid: '3001795926', name: '云音乐ACG游戏榜' },
{ id: 'wy__3001890046', bangid: '3001890046', name: '云音乐ACG VOCALOID榜' },
]
export default {
limit: 300,
limit: 100000,
list: [
{
id: 'wybsb',
@ -61,102 +103,100 @@ export default {
regExps: {
list: /<textarea id="song-list-pre-data" style="display:none;">(.+?)<\/textarea>/,
},
_cancelRequestObj: null,
_cancelPromiseCancelFn: null,
getData(url) {
if (this._cancelRequestObj != null) {
cancelHttp(this._cancelRequestObj)
this._cancelPromiseCancelFn(new Error('取消http请求'))
}
return new Promise((resolve, reject) => {
this._cancelPromiseCancelFn = reject
this._cancelRequestObj = httpGet(url, (err, resp, body) => {
this._cancelRequestObj = null
this._cancelPromiseCancelFn = null
if (err) {
console.log(err)
reject(err)
}
resolve(body)
})
_requestBoardsObj: null,
_requestBoardsDetailObj: null,
getBoardsData() {
if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp()
this._requestBoardsObj = httpFetch('https://music.163.com/weapi/toplist', {
method: 'post',
form: weapi({}),
})
return this._requestBoardsObj.promise
},
getSinger(singers) {
let arr = []
singers.forEach(singer => {
arr.push(singer.name)
getData(id) {
if (this._requestBoardsDetailObj) this._requestBoardsDetailObj.cancelHttp()
this._requestBoardsDetailObj = httpFetch('https://music.163.com/weapi/v3/playlist/detail', {
method: 'post',
form: weapi({
id,
n: 100000,
p: 1,
}),
})
return arr.join('、')
return this._requestBoardsDetailObj.promise
},
filterData(rawList) {
filterBoardsData(rawList) {
// console.log(rawList)
return rawList.map(item => {
const types = []
const _types = {}
let size
switch (item.privilege.maxbr) {
case 999000:
size = null
types.push({ type: 'flac', size })
_types.flac = {
size,
}
case 320000:
size = null
types.push({ type: '320k', size })
_types['320k'] = {
size,
}
// case 192000:
// case 190000:
// size = null
// types.push({ type: '192k', size })
// _types['192k'] = {
// size,
// }
// case '160000':
default:
size = null
types.push({ type: '128k', size })
_types['128k'] = {
size,
}
break
}
types.reverse()
return {
singer: this.getSinger(item.artists),
name: item.name,
albumName: item.album.name,
albumId: item.album.id,
source: 'wy',
interval: formatPlayTime(item.duration / 1000),
songmid: item.id,
img: item.album.picUrl,
lrc: null,
types,
_types,
typeUrl: {},
}
})
let list = []
for (const board of rawList) {
// 排除 MV榜
// if (board.id == 201) continue
list.push({
id: 'wy__' + board.id,
name: board.name,
bangid: String(board.id),
})
}
return list
},
getList(id, page) {
let type = this.list.find(s => s.id === id)
if (!type) return Promise.reject()
return this.getData(this.getUrl(type.bangid)).then(html => {
let result = html.match(this.regExps.list)
if (!result) return Promise.reject()
const list = this.filterData(JSON.parse(RegExp.$1))
return {
total: list.length,
list,
limit: this.limit,
page,
source: 'tx',
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)
// if (response.statusCode !== 200 || response.body.code !== 200) return this.getBoards(retryNum)
// const list = this.filterBoardsData(response.body.list)
// console.log(list)
// console.log(JSON.stringify(list))
// this.list = list
// return {
// list,
// source: 'wy',
// }
this.list = topList
return {
list: topList,
source: 'wy',
}
},
async getList(bangid, page, retryNum = 0) {
if (++retryNum > 6) return Promise.reject(new Error('try max num'))
// console.log(bangid)
let resp
try {
resp = await this.getData(bangid)
} catch (err) {
if (err.message == 'try max num') {
throw err
} else {
return this.getList(bangid, page, retryNum)
}
})
}
if (resp.statusCode !== 200 || resp.body.code !== 200) return this.getList(bangid, page, retryNum)
// console.log(resp.body)
let musicDetail
try {
musicDetail = await musicDetailApi.getList(resp.body.playlist.trackIds.map(trackId => trackId.id))
} catch (err) {
console.log(err)
if (err.message == 'try max num') {
throw err
} else {
return this.getList(bangid, page, retryNum)
}
}
// console.log(musicDetail)
return {
total: musicDetail.list.length,
list: musicDetail.list,
limit: this.limit,
page,
source: 'wy',
}
},
}

View File

@ -0,0 +1,90 @@
import { httpFetch } from '../../request'
import { weapi } from './utils/crypto'
import { formatPlayTime, sizeFormate } from '../../.'
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/song_detail.js
export default {
_requestObj: null,
getSinger(singers) {
let arr = []
singers.forEach(singer => {
arr.push(singer.name)
})
return arr.join('、')
},
filterList({ songs, privileges }) {
// console.log(tracks, 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
switch (privilege.maxbr) {
case 999000:
size = null
types.push({ type: 'flac', size })
_types.flac = {
size,
}
case 320000:
if (item.h) {
size = sizeFormate(item.h.size)
types.push({ type: '320k', size })
_types['320k'] = {
size,
}
}
case 128000:
if (item.l) {
size = sizeFormate(item.l.size)
types.push({ type: '128k', size })
_types['128k'] = {
size,
}
}
}
types.reverse()
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,
types,
_types,
typeUrl: {},
})
})
return list
},
async getList(ids = [], retryNum = 0) {
if (this._requestObj) this._requestObj.cancelHttp()
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) }
},
}

View File

@ -6,6 +6,7 @@
import { weapi, linuxapi } from './utils/crypto'
import { httpFetch } from '../../request'
import { formatPlayTime, sizeFormate } from '../../index'
import musicDetailApi from './musicDetail'
export default {
_requestObj_tags: null,
@ -80,13 +81,26 @@ export default {
},
}),
})
const { body } = await this._requestObj_listDetail.promise
if (body.code !== this.successCode) return this.getListDetail(id, page, ++tryNum)
const { statusCode, body } = await this._requestObj_listDetail.promise
if (statusCode !== 200 || body.code !== this.successCode) return this.getListDetail(id, page, ++tryNum)
console.log(body)
let musicDetail
try {
musicDetail = await musicDetailApi.getList(body.playlist.trackIds.map(trackId => trackId.id))
} catch (err) {
console.log(err)
if (err.message == 'try max num') {
throw err
} else {
return this.getListDetail(id, page, ++tryNum)
}
}
console.log(musicDetail)
return {
list: this.filterListDetail(body),
list: musicDetail.list,
page,
limit: this.limit_song,
total: body.playlist.tracks.length,
total: musicDetail.list.length,
source: 'wy',
info: {
play_count: this.formatPlayCount(body.playlist.playCount),

View File

@ -2,6 +2,8 @@ import { xmRequest } from './util'
import { formatPlayTime, sizeFormate } from '../../index'
// import jshtmlencode from 'js-htmlencode'
let boardList = [{ id: 'xm__102', name: '新歌榜', bangid: '102' }, { id: 'xm__103', name: '热歌榜', bangid: '103' }, { id: 'xm__104', name: '原创榜', bangid: '104' }, { id: 'xm__306', name: 'K歌榜', bangid: '306' }, { id: 'xm__332', name: '抖音热歌榜', bangid: '332' }, { id: 'xm__305', name: '歌单收录榜', bangid: '305' }, { id: 'xm__327', name: '趴间热歌榜', bangid: '327' }, { id: 'xm__324', name: '影视原声榜', bangid: '324' }, { id: 'xm__204', name: '美国Billboard单曲榜', bangid: '204' }, { id: 'xm__206', name: '韩国MNET音乐排行榜', bangid: '206' }, { id: 'xm__201', name: 'Hito 中文排行榜', bangid: '201' }, { id: 'xm__203', name: '英国UK单曲榜', bangid: '203' }, { id: 'xm__205', name: 'oricon公信单曲榜', bangid: '205' }, { id: 'xm__328', name: '美国iTunes榜', bangid: '328' }, { id: 'xm__329', name: 'Beatport电音榜', bangid: '329' }, { id: 'xm__330', name: '香港商业电台榜', bangid: '330' }]
export default {
limit: 200,
list: [
@ -56,7 +58,13 @@ export default {
bangid: '324',
},
],
requestBoardsObj: null,
requestObj: null,
getBoardsData() {
if (this.requestBoardsObj) this.requestBoardsObj.cancelHttp()
this.requestBoardsObj = xmRequest('/api/billboard/getBillboards')
return this.requestBoardsObj.promise
},
getData(id) {
if (this.requestObj) this.requestObj.cancelHttp()
this.requestObj = xmRequest('/api/billboard/getBillboardDetail', { billboardId: id })
@ -126,12 +134,66 @@ export default {
return list
},
getList(id, page, retryNum = 0) {
filterBoardsData(rawList) {
// console.log(rawList)
let list = []
if (rawList.xiamiBillboards) {
for (const board of rawList.xiamiBillboards) {
if (board.itemType != 1) continue
list.push({
id: 'xm__' + board.billboardId,
name: board.name,
bangid: String(board.billboardId),
})
}
}
if (rawList.spBillboards) {
for (const board of rawList.spBillboards) {
if (board.itemType != 1) continue
list.push({
id: 'xm__' + board.billboardId,
name: board.name,
bangid: String(board.billboardId),
})
}
}
if (rawList.globalBillboards) {
for (const board of rawList.globalBillboards) {
if (board.itemType != 1) continue
list.push({
id: 'xm__' + board.billboardId,
name: board.name,
bangid: String(board.billboardId),
})
}
}
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)
// }
// if (response.statusCode !== 200 || response.body.code !== 'SUCCESS') return this.getBoards(retryNum)
// const list = this.filterBoardsData(response.body.result.data)
// this.list = list
// return {
// list,
// source: 'xm',
// }
this.list = boardList
return {
list: boardList,
source: 'xm',
}
},
getList(bangid, page, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
let type = this.list.find(s => s.id === id)
if (!type) return Promise.reject()
return this.getData(type.bangid).then(({ statusCode, body }) => {
if (statusCode !== 200 || body.code !== 'SUCCESS') return this.getList(id, page, retryNum)
return this.getData(bangid).then(({ statusCode, body }) => {
if (statusCode !== 200 || body.code !== 'SUCCESS') return this.getList(bangid, page, retryNum)
// console.log(body)
const list = this.filterData(body.result.data.billboard.songs)

View File

@ -67,11 +67,11 @@ const buildHttpPromose = (url, options) => {
})
obj.cancelHttp = () => {
if (!obj.requestObj) return obj.isCancelled = true
obj.cancelFn(new Error(requestMsg.cancelRequest))
cancelHttp(obj.requestObj)
obj.requestObj = null
obj.cancelFn = null
obj.promise = obj.cancelHttp = null
obj.cancelFn(new Error(requestMsg.cancelRequest))
obj.cancelFn = null
}
return obj
}

View File

@ -1,17 +1,17 @@
<template lang="pug">
div(:class="$style.leaderboard")
//- div(:class="$style.header")
material-tab(:class="$style.tab" :list="types" align="left" item-key="id" item-name="name" v-model="tabId")
material-select(:class="$style.select" :list="sourceInfo.sources" item-key="id" item-name="name" v-model="source")
material-tab(:class="$style.tab" :list="boards" align="left" item-key="id" item-name="name" v-model="tabId")
material-select(:class="$style.select" :list="sources.sources" item-key="id" item-name="name" v-model="source")
div(:class="$style.lists" ref="dom_lists")
div(:class="$style.listsSelect")
//- h2(:class="$style.listsTitle") {{$t('core.aside.my_list')}}
material-selection(:class="$style.select" :list="sourceInfo.sources" item-key="id" item-name="name" v-model="source")
material-selection(:class="$style.select" :list="sources" item-key="id" item-name="name" v-model="source")
//- button(:class="$style.listsAdd" @click="handleShowNewList" :title="$t('view.list.lists_new_list_btn')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='70%' viewBox='0 0 24 24' space='preserve')
use(xlink:href='#icon-list-add')
ul.scroll(:class="$style.listsContent" ref="dom_lists_list")
li(:class="[$style.listsItem, item.id == tabId ? $style.active : null]" :title="item.name" v-for="item in types" :key="item.id" @click="handleToggleList(item.id)")
li(:class="[$style.listsItem, item.id == tabId ? $style.active : null]" :title="item.name" v-for="item in boardList" :key="item.id" @click="handleToggleList(item.id)")
span(:class="$style.listsLabel") {{item.name}}
div(:class="$style.list")
material-song-list(v-model="selectdData" :rowWidth="{r1: '5%', r2: 'auto', r3: '22%', r4: '22%', r5: '9%', r6: '15%'}" @action="handleSongListAction" :source="source" :page="page" :limit="info.limit" :total="info.total" :noItem="$t('material.song_list.loding_list')" :list="list")
@ -40,23 +40,29 @@ export default {
},
computed: {
...mapGetters(['setting']),
...mapGetters('leaderboard', ['sourceInfo', 'list', 'info']),
...mapGetters('leaderboard', ['sources', 'boards', 'list', 'info']),
...mapGetters('list', ['defaultList']),
types() {
return this.source ? this.sourceInfo.sourceList[this.source] : []
boardList() {
return this.source && this.boards[this.source] ? this.boards[this.source] : []
},
},
watch: {
tabId(n, o) {
this.setLeaderboard({ tabId: n })
if (!o && this.page !== 1) return
if (!n || (!o && this.page !== 1)) return
this.getList(1).then(() => {
this.page = this.info.page
})
},
source(n, o) {
this.setLeaderboard({ source: n })
if (o) this.tabId = this.types[0] && this.types[0].id
if (o) this.tabId = null
this.getBoardsList().then(() => {
if (this.tabId != null) return
this.$nextTick(() => {
this.tabId = this.boardList[0] && this.boardList[0].id
})
})
},
},
mounted() {
@ -66,7 +72,7 @@ export default {
},
methods: {
...mapMutations(['setLeaderboard']),
...mapActions('leaderboard', ['getList']),
...mapActions('leaderboard', ['getBoardsList', 'getList']),
...mapActions('download', ['createDownload', 'createDownloadMultiple']),
...mapMutations('list', ['listAdd', 'listAddMultiple']),
...mapMutations('player', ['setList']),