抓获数据源思路:
1、任意打开一首歌,进入QQ音乐网页播放界面。先不播放歌曲,查看network发现,服务端传来music.fcg?-=getplaysongvkey,这是当前歌曲的vkey。
2、分析该XHR应答。在req_0->data->midurlinfo[0]->purl。就是正确参数的后半截。
3、继续分析XHR,在 req_0->data->sip中,发现可能是正确参数的前半截。
4、尝试将参数拼接访问
5、正确解析,那就是,只要获取歌曲的getplaysongkey,就能拼接出正确的Url。
注:其实这是不断尝试的过程,因为QQ音乐返回的数据源的域名并不是dl.stream.qqmusic而是变化的ip。但是sip映射的接口,也可以正确解析数据源。
可能你还没看懂,反正是一个接口抓获,以及分析的过程。接下来就是具体实现代码。
和ustbhuangyi导师一样,直接jsonp访问即可。在api/singer中
// 文件写在singer.js中
// 传入歌曲的mid即可,获得正确的songkey
export function getPlaySongKey(mid) {
const url = 'https://u.y.qq.com/cgi-bin/musicu.fcg'
const data = Object.assign({}, commonParams, {
loginUin: 0,
hostUin: 0,
platform: 'yqq.json',
needNewCode: 0,
data: `{"req_0":{"module":"vkey.GetVkeyServer","method":"CgiGetVkey","param":{"guid":"1933776370","songmid":["${mid}"],"songtype":[0],"uin":"0","loginflag":1,"platform":"20"}},"comm":{"uin":0,"format":"json","ct":24,"cv":0}}`
})
return jsonp(url, data, '')
}
// 这一段代码为的是拼接出正确参数,promise方法。你也可以不用这段代码。看你个人水平了。这是promise应答
如果不用这段代码,你直接使用getPlaySongKey,然后通过then(res => {这里是拼接参数的方法})也可以。
export function getSongUrl(mid) {
return getPlaySongKey(mid).then(res => {
if (res.code === 0) {
const purl = res.req_0.data.midurlinfo[0].purl
const host = res.req_0.data.sip[0]
return Promise.resolve({code: 0, url: host + purl})
}
})
}
我这里还是用getSongUrl来讲咯。
成功获取到歌曲的url,那么接下来就是将url保存到song对象中。我的做法呢。就是遍历song数组,给每一首song增加对应的url
比如在singer-detail.vue组件中的_normalizeSongs方法中调用getSongUrl接口,在标准化数据的同时将url获取,并放入该对象中。
methods: {
_getDetail () {
if (!this.singer.id) {
this.$router.push('/singer')
return
}
getSingerDetail(this.singer.id).then(res => {
if (res.code === ERR_OK) {
this.songs = this._normalizeSongs(res.singer.data.songlist)
// console.log(this.songs)
}
})
},
_normalizeSongs(list) {
let ret = []
list.forEach((item, index) => {
let musicData = item
ret.push(createSong(musicData))
if (musicData.id && musicData.album.id) {
// 解析正确Vkey拼装url
this._getSongUrl(musicData.mid).then(url => {
// 指向正确的url
ret[index].url = url
})
}
})
return ret
},
_getSongUrl (mid) {
return getSongUrl(mid).then(res => {
return Promise.resolve(res.url)
})
}
}
简言之,你只需要获得播放源url就可以了,至于后续的数据渲染问题,方法可以有很多,不一定要标准化数据时扔url进去,如果歌曲长度50,则会发送50个XHR请求,很耗资源。好的做法就是侦听currentSong的变化,根据变化重新发送getSongUrl并将url丢给currentSong即可。