修复网易云128k直接试听

pull/96/head
lyswhut 2019-10-16 00:40:36 +08:00
parent a104ce688e
commit 9dc9d3b1b7
13 changed files with 110 additions and 61 deletions

View File

@ -2,3 +2,4 @@
- 修复QQ源歌单无法翻页Bug - 修复QQ源歌单无法翻页Bug
- 修复默认列表没有创建时无法显示收藏列表的Bug - 修复默认列表没有创建时无法显示收藏列表的Bug
- 修复网易云128k直接试听

View File

@ -5,7 +5,7 @@ material-modal(:show="show" :bg-close="bgClose" @close="handleClose")
| {{ info.name }} | {{ info.name }}
br br
| {{ info.singer }} | {{ info.singer }}
material-btn(:class="$style.btn" :title="!checkSource(type.type) && '目前腾讯音源仅支持下载128k音质'" :disabled="!checkSource(type.type)" :key="type.type" @click="handleClick(type.type)" v-for="type in info.types") {{getTypeName(type.type)}} {{ type.type.toUpperCase() }}{{ type.size && ` - ${type.size.toUpperCase()}` }} material-btn(:class="$style.btn" :title="!checkSource(type.type) && '腾讯、网易音源仅支持下载128k音质'" :disabled="!checkSource(type.type)" :key="type.type" @click="handleClick(type.type)" v-for="type in info.types") {{getTypeName(type.type)}} {{ type.type.toUpperCase() }}{{ type.size && ` - ${type.size.toUpperCase()}` }}
</template> </template>
@ -52,7 +52,6 @@ export default {
checkSource(type) { checkSource(type) {
switch (this.musicInfo.source) { switch (this.musicInfo.source) {
case 'wy': case 'wy':
return false
case 'tx': case 'tx':
return type == '128k' return type == '128k'

View File

@ -29,9 +29,9 @@ div(:class="$style.songList")
td(style="width: 20%; padding-left: 0; padding-right: 0;") td(style="width: 20%; padding-left: 0; padding-right: 0;")
material-list-buttons(:index="index" :search-btn="true" material-list-buttons(:index="index" :search-btn="true"
:remove-btn="false" @btn-click="handleListBtnClick" :remove-btn="false" @btn-click="handleListBtnClick"
:listAdd-btn="item.source == 'kw' || (!isAPITemp && item.source != 'wy')" :listAdd-btn="item.source == 'kw' || (!isAPITemp)"
:play-btn="item.source == 'kw' || (!isAPITemp && item.source != 'wy')" :play-btn="item.source == 'kw' || (!isAPITemp)"
:download-btn="item.source == 'kw' || (!isAPITemp && item.source != 'wy')") :download-btn="item.source == 'kw' || (!isAPITemp)")
//- button.btn-info(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k'] || item._types.flac" @click.stop='openDownloadModal(index)') //- button.btn-info(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k'] || item._types.flac" @click.stop='openDownloadModal(index)')
//- button.btn-secondary(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k']" @click.stop='testPlay(index)') //- button.btn-secondary(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k']" @click.stop='testPlay(index)')
//- button.btn-success(type='button' v-if="(item._types['128k'] || item._types['192k'] || item._types['320k']) && userInfo" @click.stop='showListModal(index)') //- button.btn-success(type='button' v-if="(item._types['128k'] || item._types['192k'] || item._types['320k']) && userInfo" @click.stop='showListModal(index)')
@ -140,7 +140,7 @@ export default {
this.clickIndex = index this.clickIndex = index
return return
} }
this.emitEvent((this.source == 'kw' || (!this.isAPITemp && this.list[index].source != 'wy')) ? 'testPlay' : 'search', index) this.emitEvent((this.source == 'kw' || !this.isAPITemp) ? 'testPlay' : 'search', index)
this.clickTime = 0 this.clickTime = 0
this.clickIndex = -1 this.clickIndex = -1
}, },

View File

@ -1,41 +0,0 @@
import { httpFatch } from '../../request'
import { requestMsg } from '../../message'
import { headers, timeout } from '../options'
const api_messoer = {
getMusicUrl(songInfo, type) {
const requestObj = httpFatch(`https://v1.itooi.cn/netease/url?id=${songInfo.songmid}&quality=${type.replace(/k$/, '')}&isRedirect=0`, {
method: 'get',
timeout,
headers,
})
requestObj.promise = requestObj.promise.then(({ body }) => {
return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
})
return requestObj
},
getPic(songInfo) {
const requestObj = httpFatch(`https://v1.itooi.cn/netease/pic?id=${songInfo.songmid}&isRedirect=0`, {
method: 'get',
timeout,
headers,
})
requestObj.promise = requestObj.promise.then(({ body }) => {
return body.code === 200 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
})
return requestObj
},
getLyric(songInfo) {
const requestObj = httpFatch(`https://v1.itooi.cn/netease/lrc?id=${songInfo.songmid}&isRedirect=0`, {
method: 'get',
timeout,
headers,
})
requestObj.promise = requestObj.promise.then(({ body }) => {
return body ? Promise.resolve(body) : Promise.reject(new Error(requestMsg.fail))
})
return requestObj
},
}
export default api_messoer

View File

@ -15,8 +15,8 @@ const api_test = {
}) })
return requestObj return requestObj
}, },
getPic(songInfo) { /* getPic(songInfo) {
const requestObj = httpFetch(`http://ts.tempmusic.tk/pic/wy/${songInfo.songmid}`, { const requestObj = httpFetch(`http://localhost:3100/pic/wy/${songInfo.songmid}`, {
method: 'get', method: 'get',
timeout, timeout,
headers, headers,
@ -28,7 +28,7 @@ const api_test = {
return requestObj return requestObj
}, },
getLyric(songInfo) { getLyric(songInfo) {
const requestObj = httpFetch(`http://ts.tempmusic.tk/lrc/wy/${songInfo.songmid}`, { const requestObj = httpFetch(`http://localhost:3100/lrc/wy/${songInfo.songmid}`, {
method: 'get', method: 'get',
timeout, timeout,
headers, headers,
@ -38,7 +38,7 @@ const api_test = {
return body.code === 0 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail)) return body.code === 0 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
}) })
return requestObj return requestObj
}, }, */
} }
export default api_test export default api_test

View File

@ -1,5 +1,7 @@
import leaderboard from './leaderboard' import leaderboard from './leaderboard'
import api_source from '../api-source' import api_source from '../api-source'
import getLyric from './lyric'
import getMusicInfo from './musicInfo'
const wy = { const wy = {
leaderboard, leaderboard,
@ -7,10 +9,10 @@ const wy = {
return api_source('wy').getMusicUrl(songInfo, type) return api_source('wy').getMusicUrl(songInfo, type)
}, },
getLyric(songInfo) { getLyric(songInfo) {
return api_source('wy').getLyric(songInfo) return getLyric(songInfo.songmid)
}, },
getPic(songInfo) { getPic(songInfo) {
return api_source('wy').getPic(songInfo) return getMusicInfo(songInfo.songmid).then(info => info.al.picUrl)
}, },
} }

View File

@ -0,0 +1,27 @@
import { httpFetch } from '../../request'
import { linuxapi } from './utils/crypto'
export default songmid => {
const requestObj = httpFetch('https://music.163.com/api/linux/forward', {
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',
Referer: 'https://music.163.com/song?id=' + songmid,
origin: 'https://music.163.com',
},
form: linuxapi({
method: 'POST',
url: 'https://music.163.com/api/song/lyric?lv=-1&kv=-1&tv=-1',
params: {
id: songmid,
},
}),
})
requestObj.promise = requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.code !== 200) return Promise.reject('获取歌词失败')
return body.lrc.lyric
})
return requestObj
}

View File

@ -0,0 +1,25 @@
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/song_detail.js
import { httpFetch } from '../../request'
import { weapi } from './utils/crypto'
export default songmid => {
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',
Referer: 'https://music.163.com/song?id=' + songmid,
origin: 'https://music.163.com',
},
form: weapi({
c: `[{"id":${songmid}}]`,
ids: '[songmid]',
}),
})
requestObj.promise = requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.code !== 200 || !body.songs.length) return Promise.reject('获取歌曲信息失败')
return body.songs[0]
})
return requestObj
}

View File

@ -0,0 +1,34 @@
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/util/crypto.js
import { createCipheriv, publicEncrypt, constants, randomBytes } from 'crypto'
const iv = Buffer.from('0102030405060708')
const presetKey = Buffer.from('0CoJUm6Qyw8W8jud')
const linuxapiKey = Buffer.from('rFgB&h#%2?^eDg:Q')
const base62 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
const publicKey = '-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgtQn2JZ34ZC28NWYpAUd98iZ37BUrX/aKzmFbt7clFSs6sXqHauqKWqdtLkF2KexO40H1YTX8z2lSgBBOAxLsvaklV8k4cBFK9snQXE9/DDaFt6Rr7iVZMldczhC0JNgTz+SHXT6CBHuX3e9SdB1Ua44oncaTWz7OBGLbCiK45wIDAQAB\n-----END PUBLIC KEY-----'
const aesEncrypt = (buffer, mode, key, iv) => {
const cipher = createCipheriv('aes-128-' + mode, key, iv)
return Buffer.concat([cipher.update(buffer), cipher.final()])
}
const rsaEncrypt = (buffer, key) => {
buffer = Buffer.concat([Buffer.alloc(128 - buffer.length), buffer])
return publicEncrypt({ key: key, padding: constants.RSA_NO_PADDING }, buffer)
}
export const weapi = object => {
const text = JSON.stringify(object)
const secretKey = randomBytes(16).map(n => (base62.charAt(n % 62).charCodeAt()))
return {
params: aesEncrypt(Buffer.from(aesEncrypt(Buffer.from(text), 'cbc', presetKey, iv).toString('base64')), 'cbc', secretKey, iv).toString('base64'),
encSecKey: rsaEncrypt(secretKey.reverse(), publicKey).toString('hex'),
}
}
export const linuxapi = object => {
const text = JSON.stringify(object)
return {
eparams: aesEncrypt(Buffer.from(text), 'ecb', linuxapiKey, '').toString('hex').toUpperCase(),
}
}

View File

@ -126,6 +126,7 @@ export default {
handleAddDownloadMultiple(type) { handleAddDownloadMultiple(type) {
switch (this.source) { switch (this.source) {
// case 'kg': // case 'kg':
case 'tx':
case 'wy': case 'wy':
type = '128k' type = '128k'
} }

View File

@ -18,7 +18,7 @@
table table
tbody tbody
tr(v-for='(item, index) in list' :key='item.songmid' tr(v-for='(item, index) in list' :key='item.songmid'
@click="handleDoubleClick(index)" :class="[isPlayList && playIndex === index ? $style.active : '', (isAPITemp && item.source != 'kw') || item.source == 'wy' ? $style.disabled : '']") @click="handleDoubleClick(index)" :class="[isPlayList && playIndex === index ? $style.active : '', (isAPITemp && item.source != 'kw') ? $style.disabled : '']")
td.nobreak.center(style="width: 37px;" @click.stop) td.nobreak.center(style="width: 37px;" @click.stop)
material-checkbox(:id="index.toString()" v-model="selectdData" :value="item") material-checkbox(:id="index.toString()" v-model="selectdData" :value="item")
td.break(style="width: 25%;") td.break(style="width: 25%;")
@ -218,7 +218,7 @@ export default {
this.clickIndex = -1 this.clickIndex = -1
}, },
testPlay(index) { testPlay(index) {
if ((this.isAPITemp && this.list[index].source != 'kw') || this.list[index].source == 'wy') return if (this.isAPITemp && this.list[index].source != 'kw') return
this.setPlayList({ list: this.list, listId: this.listId, index }) this.setPlayList({ list: this.list, listId: this.listId, index })
}, },
handleRemove(index) { handleRemove(index) {
@ -228,7 +228,7 @@ export default {
switch (info.action) { switch (info.action) {
case 'download': { case 'download': {
const minfo = this.list[info.index] const minfo = this.list[info.index]
if ((this.isAPITemp && minfo.source != 'kw') || minfo.source == 'wy') return if (this.isAPITemp && minfo.source != 'kw') return
this.musicInfo = minfo this.musicInfo = minfo
this.$nextTick(() => { this.$nextTick(() => {
this.isShowDownload = true this.isShowDownload = true
@ -261,7 +261,7 @@ export default {
this.selectdData = [] this.selectdData = []
}, },
handleAddDownloadMultiple(type) { handleAddDownloadMultiple(type) {
const list = this.setting.apiSource == 'temp' ? this.selectdData.filter(s => s.source == 'kw') : this.selectdData.filter(s => s.source != 'wy') const list = this.setting.apiSource == 'temp' ? this.selectdData.filter(s => s.source == 'kw') : [...this.selectdData]
this.createDownloadMultiple({ list, type }) this.createDownloadMultiple({ list, type })
this.resetSelect() this.resetSelect()
this.isShowDownloadMultiple = false this.isShowDownloadMultiple = false

View File

@ -30,8 +30,8 @@
td.break(style="width: 25%;") {{item.albumName}} td.break(style="width: 25%;") {{item.albumName}}
td(style="width: 15%; padding-left: 0; padding-right: 0;") td(style="width: 15%; padding-left: 0; padding-right: 0;")
material-list-buttons(:index="index" :remove-btn="false" :class="$style.listBtn" material-list-buttons(:index="index" :remove-btn="false" :class="$style.listBtn"
:play-btn="item.source == 'kw' || (!isAPITemp && item.source != 'wy')" :play-btn="item.source == 'kw' || !isAPITemp"
:download-btn="item.source == 'kw' || (!isAPITemp && item.source != 'wy')" :download-btn="item.source == 'kw' || !isAPITemp"
@btn-click="handleListBtnClick") @btn-click="handleListBtnClick")
td(style="width: 10%;") {{item.interval || '--/--'}} td(style="width: 10%;") {{item.interval || '--/--'}}
div(:class="$style.pagination") div(:class="$style.pagination")
@ -189,7 +189,7 @@ export default {
targetSong = this.selectdData[0] targetSong = this.selectdData[0]
this.listAddMultiple({ id: 'default', list: this.filterList(this.selectdData) }) this.listAddMultiple({ id: 'default', list: this.filterList(this.selectdData) })
} else { } else {
if ((this.isAPITemp && this.listInfo.list[index].source != 'kw') || this.listInfo.list[index].source == 'wy') return if (this.isAPITemp && this.listInfo.list[index].source != 'kw') return
targetSong = this.listInfo.list[index] targetSong = this.listInfo.list[index]
this.listAdd({ id: 'default', musicInfo: targetSong }) this.listAdd({ id: 'default', musicInfo: targetSong })
} }
@ -238,7 +238,7 @@ export default {
} }
}, },
filterList(list) { filterList(list) {
return this.setting.apiSource == 'temp' ? list.filter(s => s.source == 'kw') : list.filter(s => s.source != 'wy') return this.setting.apiSource == 'temp' ? list.filter(s => s.source == 'kw') : [...list]
}, },
handleListAddModalClose(isSelect) { handleListAddModalClose(isSelect) {
if (isSelect) this.resetSelect() if (isSelect) this.resetSelect()

View File

@ -200,6 +200,7 @@ export default {
handleAddDownloadMultiple(type) { handleAddDownloadMultiple(type) {
switch (this.source) { switch (this.source) {
// case 'kg': // case 'kg':
case 'tx':
case 'wy': case 'wy':
type = '128k' type = '128k'
} }