摘要:我自己一直喜欢用网易云音乐,于是乎就做了这个。后台后台采用做为后台提供系统请求所用的源码,原理很简单就是用伪装成一个客户端去访问网易云音乐的然后,把返回的数据转发出来。
技术栈</>复制代码
前言:自己学习VUEJS也一段时间,但一直没有做出来一东西。我自己一直喜欢用网易云音乐app,于是乎就做了这个app。
vue全家桶 (vue vue-router vuex)
axios
Muse-UI(一个基于Vue2.x的material design 风格UI框架)
功能与思路分析我之前学习JS的时候对Html5 audio研究过,也写过一些例子,那时的功能并不是很全面。在写这个程序之前,我好好的查阅了当前的HTML5中的audio标签,发现园子上一位园友总结的很不错(这里)。于是就先把网易云音乐最基本的功能实现,歌单部分(这也是我喜欢网易云音乐的原因这一),然后实现音乐的上一曲、下一曲,播放、暂停。列表功能。
后台后台采用.net做为后台提供系统请求所用的API(源码),原理很简单就是用.net伪装成一个客户端去访问网易云音乐的API然后,把返回的json数据转发出来。同时服务端做下跨域处理。
核心代码:
</>复制代码
///
/// 请求网易云音乐接口
///
/// 要请求的接口类型
/// 要请求的接口类型的对象
/// 请求结果(JSON)
public static string Request(T config) where T : RequestData, new()
{
// 请求URL
string requestURL = config.Url;
// 将数据包对象转换成QueryString形式的字符串
string @params = config.FormData.ParseQueryString();
bool isPost = config.Method.Equals("post", StringComparison.CurrentCultureIgnoreCase);
if (!isPost)
{
// get方式 拼接请求url
string sep = requestURL.Contains("?") ? "&" : "?";
requestURL += sep + @params;
}
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(requestURL);
req.Accept = "*/*";
req.Headers.Add("Accept-Language", "zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4");
// 如果服务端启用了GZIP,那么下面必须解压,否则一直乱码。
// 参见:http://www.crifan.com/set_accept_encoding_header_to_gzip_deflate_return_messy_code/
req.Headers.Add("Accept-Encoding", "gzip,deflate,sdch");
req.ContentType = "application/x-www-form-urlencoded";
req.KeepAlive = true;
req.Host = "music.163.com";
req.Referer = "http://music.163.com/search/";
req.UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537";
// 设置cookies
req.Headers["Cookie"] = "appver=1.5.2";
req.Method = config.Method;
req.AutomaticDecompression = DecompressionMethods.GZip;
if (isPost)
{
// 写入post请求包
byte[] formData = Encoding.UTF8.GetBytes(@params);
// 设置HTTP请求头 参考:https://github.com/darknessomi/musicbox/blob/master/NEMbox/api.py
req.GetRequestStream().Write(formData, 0, formData.Length);
}
// 发送http请求 并读取响应内容返回
return new StreamReader(req.GetResponse().GetResponseStream(), Encoding.GetEncoding("UTF-8")).ReadToEnd();
}
vuejs部分
项目结构
</>复制代码
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── playBar.vue
│ └── ...
└── store
│ └── index.js # 整个项目的vuex部分
└── router
│ └── router.js # 整个项目的路由
└── utils # 一些工具类模块
│
└── views # 项目中的一些route-view
</>复制代码
说项目的路由之前,先来看一张效果图
对于整个项目来说:视图区别在于顶部导航,下面的bar的是否出来取决于,当前系统列表中是否有歌曲,如果有就会出现。
router.js核心部分
</>复制代码
const router = new VueRouter({
mode: "history",
routes: [{
path: "/index",
component: require("../views/index"),
children: [
{
path: "rage",
component: require("../views/rage")
},
{
path: "songList",
component: require("../views/songList")
},
{
path: "leaderBoard",
component: require("../views/leaderBoard")
},
{
path: "hotSinger",
component: require("../views/hotSinger")
}
]
}, {
name: "playerDetail",
path: "/playerDetail/:id",
component: require("../views/playerDetail")
}, {
path: "/playListDetail/:id",
name: "playListDetail",
component: require("../views/playListDetail")
}, {
path: "*", redirect: "/index/rage"
}],
// 让每个页面都滚动到顶部,改变模式为mode: history
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
})
vuex部分
这部分,主要是歌曲这一块,因为不同的页面有不同的使用到了歌曲信息,把把这部分数据放到vuex中做统一的数据处理!
sotre/index.js
</>复制代码
const store = new Vuex.Store({
state: {
audio: {
"id": 0,
"name": "歌曲名称",
"singer": "演唱者",
"albumPic": "/static/player-bar.png",
"location": "",
"album": ""
},
lyric: "正在加载中。。",
currentIndex: 0, // 当前播放的歌曲位置
playing: false, // 是否正在播放
loading: false, // 是否正在加载中
showDetail: false,
songList: [], // 播放列表
currentTime: 0,
tmpCurrentTime: 0,
durationTime: 0,
bufferedTime: 0,
change: false // 判断是更改的时间还是播放的时间
},
getters: {
audio: state => state.audio,
playing: state => state.playing,
loading: state => state.loading,
showDetail: state => state.showDetail,
durationTime: state => state.durationTime,
currentIndex: state => state.currentIndex,
bufferedTime: state => state.bufferedTime,
tmpCurrentTime: state => state.tmpCurrentTime,
songList: state => state.songList,
change: state => state.change,
currentTime: state => state.currentTime,
prCurrentTime: state => {
return state.currentTime / state.durationTime * 100
},
prBufferedTime: state => {
return state.bufferedTime / state.durationTime * 100
}
},
mutations: {
play (state) {
state.playing = true
},
pause (state) {
state.playing = false
},
toggleDetail (state) {
state.showDetail = !state.showDetail
},
setAudio (state) {
state.audio = state.songList[state.currentIndex - 1]
},
setAudioIndex (state, index) {
state.audio = state.songList[index]
state.currentIndex = index + 1
},
removeAudio (state, index) {
state.songList.splice(index, 1)
state.audio = state.songList[index - 1]
state.currentIndex = state.currentIndex - 1
if (state.songList.length === 0) {
state.audio = {
"id": 0,
"name": "歌曲名称",
"singer": "演唱者",
"albumPic": "/static/player-bar.png",
"location": "",
"album": ""
}
state.playing = false
}
},
setChange (state, flag) {
state.change = flag
},
setLocation (state, location) {
state.audio.location = location
},
updateCurrentTime (state, time) {
state.currentTime = time
},
updateDurationTime (state, time) {
state.durationTime = time
},
updateBufferedTime (state, time) {
state.bufferedTime = time
},
changeTime (state, time) {
state.tmpCurrentTime = time
},
openLoading (state) {
state.loading = true
},
closeLoading (state) {
state.loading = false
},
resetAudio (state) {
state.currentTime = 0
},
playNext (state) { // 播放下一曲
state.currentIndex++
if (state.currentIndex > state.songList.length) {
state.currentIndex = 1
}
state.audio = state.songList[state.currentIndex - 1]
},
playPrev (state) { // 播放上一曲
state.currentIndex--
if (state.currentIndex < 1) {
state.currentIndex = state.songList.length
}
state.audio = state.songList[state.currentIndex - 1]
},
addToList (state, item) {
var flag = false
state.songList.forEach(function (element, index) { // 检测歌曲重复
if (element.id === item.id) {
flag = true
state.currentIndex = index + 1
}
})
if (!flag) {
state.songList.push(item)
state.currentIndex = state.songList.length
}
},
setLrc (state, lrc) {
state.lyric = lrc
}
},
// 异步的数据操作
actions: {
getSong ({commit, state}, id) {
commit("openLoading")
Axios.get(api.getSong(id)).then(res => {
// 统一数据模型,方便后台接口的改变
var url = res.data.data[0].url
commit("setAudio")
commit("setLocation", url)
})
},
getLrc ({commit, state}, id) {
commit("setLrc", "[txt](加载中。。。")
Axios.get(api.getLrc(id)).then(res => {
// 1、先判断是否有歌词
if (res.data.nolyric) {
commit("setLrc", "[txt](⊙0⊙) 暂无歌词")
} else {
console.log(res.data.lrc.lyric)
commit("setLrc", res.data.lrc.lyric)
}
})
}
}
})
</>复制代码
最后上点项目截图
github项目地址:https://github.com/javaSwing/NeteaseCloudWebApp
目前只完成app歌单部分,也是最核心的部分。这个项目会一直更新!如果觉的不错就给个star吧
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/88138.html
摘要:高仿低配网页版网易云音乐播放器前言没有使用任何框架,只是想用最简单纯的代码实现下前台后台是参考网上的例子写的,代码是在的基础上重新写的还有她的姊妹篇网易云音乐移动端,请查看这里写在前头的话鄙人野生前端一只,专业,自学前端已经一年多了 HTML+CSS+JAVASCRIPT 高仿低配网页版网易云音乐播放器 showImg(https://segmentfault.com/img/remo...
摘要:高仿低配网页版网易云音乐播放器前言没有使用任何框架,只是想用最简单纯的代码实现下前台后台是参考网上的例子写的,代码是在的基础上重新写的还有她的姊妹篇网易云音乐移动端,请查看这里写在前头的话鄙人野生前端一只,专业,自学前端已经一年多了 HTML+CSS+JAVASCRIPT 高仿低配网页版网易云音乐播放器 showImg(https://segmentfault.com/img/remo...
摘要:如何能低成本地快速获取大量目标用户,而不是与竞争对手持久战这在如今的互联网上并不常见。产品经理需要寻找那些对产品价值最大的需求,而低成本地快速获取大量目标用户,就是重要的思考路径之一。 此文已由作者王诗沐授权网易云社区发布。 欢迎访问网易云社区,了解更多网易技术产品运营经验。 如何能低成本地快速获取大量目标用户,而不是与竞争对手持久战? 这在如今的互联网上并不常见。现在,大部分行业都已...
阅读 1127·2022-07-19 10:19
阅读 1973·2021-09-02 15:15
阅读 1089·2019-08-30 15:53
阅读 2742·2019-08-30 13:45
阅读 2725·2019-08-26 13:57
阅读 2081·2019-08-26 12:13
阅读 1076·2019-08-26 10:55
阅读 631·2019-08-26 10:46