前言
前段闲暇时间想了解一下网易云音乐的加密方式,想写个具体实现,flutter我放弃了,还是小程序简单点,即衍生出该项目,纯原生书写,仿安卓V6.25版本。网易云音乐加密方式稍后更新。
开发前的准备
- VScode代码编辑器。
- 微信开发者工具
- Android网易云音乐(V6.25版本)
- 网易云音乐API(node版本,这可能是github上最全的了,手动点赞)
- 本人复写Java版本个别接口(若需)
- 阿里巴巴矢量图标库
注:项目中http请求都是使用wx.request且未进行API封装,不是很优雅,有兴趣可自行封装
已实现功能及展示截图(GIF)
一、首页
项目中主要使用弹性盒子布局
首页主要加入了scroll-view滑动区分四个模块,目前只实现其中一个
首页数据有(若需电台、MV数据可自行添加):
- banner 轮播图
- 推荐歌单
- 推荐新音乐
- 新碟
/**
* 获取轮播图数据
*/
getBanner() {
let that = this;
wx.request({
url: baseUrl + 'banner',
header: {
'Content-Type': 'application/json'
},
success: function (res) {
// console.log(res);
if (res.data.code == 200) {
that.setData({
banner: res.data.banners
})
}
}
})
},
/**
* 获取歌单信息
*/
getPersonalized() {
let that = this;
wx.request({
url: baseUrl + 'personalized?limit=6',
header: {
'Content-Type': 'application/json'
},
success: function (res) {
if (res.data.code == 200) {
let pageData = res.data.result;
// 播放量四舍五入精确到万
pageData.forEach(function (item, index) {
item.playCount = (item.playCount / 10000).toFixed(0)
})
that.setData({
personalized: pageData
})
}
}
})
},
/**
* 推荐新音乐
*/
getNewsong() {
let that = this;
wx.request({
url: baseUrl + 'personalized/newsong',
header: {
'Content-Type': 'application/json'
},
success: function (res) {
// console.log(res.data.result);
if (res.data.code == 200) {
that.setData({
newsong: res.data.result
})
}
}
})
},
/**
* 获取新碟信息
*/
getNewest() {
let that = this;
wx.request({
url: baseUrl + 'album/newest',
header: {
'Content-Type': 'application/json'
},
success: function (res) {
// console.log(res);
if (res.data.code == 200) {
that.setData({
newest: res.data.albums
})
}
}
})
},
wxml页面渲染即可,具体实现详见page/index
二、搜索
这一块实现也是花费了不少时间(手动吐槽)
(1)数据项比较多,样式需要细调
(2)scroll-view超出屏幕宽度之外的滑动(写完才发现尴尬问题)
(3)swiper高度问题,这玩意太烦了,导致onReachBottom事件只触发一次,最后偷鸡解决了
功能详解
- 搜索建议
给input绑定实时监测输入框数据项(bindinput="inputext"),不为空时发送http请求
searchSuggest() {
let that = this;
wx.request({
url: baseUrl + 'search/suggest?keywords=' + this.data.searchKey + "&type=mobile",
header: {
'Content-Type': 'application/json'
},
success: function (res) {
// console.log(res)
if (res.data.code == 200) {
that.setData({
searchsuggest: res.data.result.allMatch
})
}
}
})
},
- 搜索历史
当用户点击搜索、搜索历史、热搜榜数据进行搜索时将数据存储到StorageSync中
if (key != '') {
let history = wx.getStorageSync("history") || [];
if (history.includes(key)) {
for (var i = 0; i < history.length; i++) {
if (key == history[i]) {
history.splice(i, 1);
}
}
}
history.push(key)
wx.setStorageSync("history", history);
}
- 热搜榜(请求渲染即可)
getSearchHotDetail() {
let that = this;
wx.request({
url: baseUrl + 'search/hot/detail',
header: {
'Content-Type': 'application/json'
},
success: function (res) {
wx.hideLoading()
// console.log(res.data);
if (res.data.code == 200) {
that.setData({
searchHotDetail: res.data.data
})
}
}
})
},
搜索事件:详见searhFinput方法
上划加载更多
swiper高度问题,导致至加载一次,后改用scroll-view的bindscrolltolower="loadMore"来实现
具体实现详见page/search
三、歌单
这块还是页面书写为注,毕竟非专业前端,比较费劲,具体请求数据有
// 全部歌单
gplaylist: function (isadd) {
//分类歌单列表
var that = this;
// console.log(that.data.catelist.checked.name)
wx.request({
url: baseUrl + 'top/playlist',
data: {
limit: that.data.playlist.limit,
offset: that.data.playlist.offset,
cat: that.data.catelist.checked.name
},
complete: function (res) {
// console.log(res)
that.data.playlist.loading = true;
if (!isadd) {
that.data.playlist.list = res.data
} else {
res.data.playlists = that.data.playlist.list.playlists.concat(res.data.playlists);
that.data.playlist.list = res.data
}
that.data.playlist.offset += res.data.playlists.length;
that.setData({
loading: false,
playlist: that.data.playlist
})
}
})
},
// 歌单选择
cateselect: function (e) {
var t = e.currentTarget.dataset.catype;
this.data.catelist.checked = t
this.setData({
playlist: {
list: {},
offset: 0,
limit: 30
},
loading: true,
cateisShow: !this.data.cateisShow,
catelist: this.data.catelist
});
this.gplaylist();
},
// 数据初始化
init: function () {
var that = this
wx.request({
url: baseUrl + 'playlist/catlist',
complete: function (res) {
that.setData({
catelist: {
isShow: false,
res: res.data,
checked: res.data.all
}
})
}
})
},
具体实现详见page/gedanSquare
四、排行榜
排行榜类型我是根据name来分类的(略微尴尬),榜单较多,就不一个个找id了
/**
* 所有榜单内容摘要
*/
getToplistDetail() {
let that = this;
wx.request({
url: baseUrl + 'toplist/detail',
header: {
'Content-Type': 'application/json'
},
success: function(res) {
if (res.data.code == 200) {
var list = res.data.list;
var officialList = []
var recommendationList = []
var globalList = []
var moreList = []
for (var index in list) {
var name = list[index].name
if (name == "云音乐飙升榜" || name == "云音乐新歌榜" || name == "网易原创歌曲榜" || name == "云音乐热歌榜") {
officialList.push(list[index])
} else if (name == "江小白YOLO云音乐说唱榜" || name == "说唱TOP榜" || name == "云音乐电音榜" || name == "云音乐ACG音乐榜" || name == "云音乐欧美新歌榜" || name == "抖音排行榜") {
recommendationList.push(list[index])
} else if (name == "美国Billboard周榜" || name == "UK排行榜周榜" || name == "Beatport全球电子舞曲榜" || name == "日本Oricon周榜" || name == "iTunes榜" || name == "香港电台中文歌曲龙虎榜") {
globalList.push(list[index])
} else {
moreList.push(list[index])
}
}
that.setData({
officialList,
recommendationList,
globalList,
moreList,
loading: false,
})
}
}
})
},
此处有一个地方需要注意:
查看排行榜详情中,node接口作者将id简化做了一层转意,某些榜单无法查看详情
解决方案,使用本人覆写Java版本,点击事件改为openTopList即可
具体实现详见page/rankingList
五、新歌速递
调用接口渲染即可,数据较多,小程序渲染有卡顿,小伙伴们自行优化
书写仓促代码未优化(回头看重复代码有点多)
为了播放正常,此处数据项需要做一致性处理(没办法,先写的歌单播放,返回数据格式不同)
具体实现详见page/newsong
六、MV
这边是区分两类数据的歌手的MV及用户上传的视频需要注意
/**
* 获取MV数据
*/
getMvDetail(id) {
let that = this;
wx.request({
url: baseUrl + 'mv/detail?mvid=' + id,
header: {
'Content-Type': 'application/json'
},
success: function (res) {
// console.log(res);
if (res.data.code == 200) {
that.setData({
mvDetail: res.data.data,
mvId: id
})
}
}
})
},
/**
* 获取video数据
*/
getVideoDetail(id) {
let that = this;
wx.request({
url: baseUrl + 'video/detail?id=' + id,
header: {
'Content-Type': 'application/json'
},
success: function (res) {
if (res.data.code == 200) {
that.setData({
'mvDetail.cover': res.data.data.coverUrl,
'mvDetail.name': res.data.data.title,
'mvDetail.publishTime': util.formatTimeCommit(res.data.data.publishTime, 3),
'mvDetail.desc': res.data.data.description,
'mvDetail.playCount': res.data.data.playTime,
'mvDetail.likeCount': res.data.data.praisedCount,
'mvDetail.subCount': res.data.data.subscribeCount,
'mvDetail.commentCount': res.data.data.commentCount,
'mvDetail.shareCount': res.data.data.shareCount,
mvId: id
})
}
}
})
},
具体实现详见page/mv
七、评论
(1)分歌单评论及歌曲评论,注意区分请求
(2)评论中emoji需要转换才能显示,详见具体方法
具体实现详见page/comment
getPlaylistComment(id) {
console.log("歌单评论:" + id)
let that = this;
wx.request({
url: baseUrl + 'comment/playlist?id=' + id,
header: {
'Content-Type': 'application/json'
},
success: function (res) {
// 评论表情与日期需要转换(!!!)
var data = res.data;
for (let i in data.hotComments) {
data.hotComments[i].time = util.formatTimeCommit(data.hotComments[i].time, 2);
data.hotComments[i].content = util.emoji(data.hotComments[i].content)
if (data.hotComments[i].beReplied[0]) {
data.hotComments[i].beReplied[0].content = util.emoji(data.hotComments[i].beReplied[0].content)
}
}
for (let i in data.comments) {
data.comments[i].time = util.formatTimeCommit(data.comments[i].time, 2);
data.comments[i].content = util.emoji(data.comments[i].content)
if (data.comments[i].beReplied[0]) {
data.comments[i].beReplied[0].content = util.emoji(data.comments[i].beReplied[0].content)
}
};
that.setData({
hotComments: data.hotComments,
comments: data.comments,
total: data.total,
loading: false
})
wx.setNavigationBarTitle({
title: '评论(' + (data.total || 0) + ")"
})
}
})
},
具体实现详见page/comment
八、歌曲播放
项目核心功能,写(抄)的不是很优秀,自行领悟
里面用到了微信小程序-通知广播WxNotificationCenter,别漏掉了!!!
具体实现详见page/player
九、播放栏
播放栏使用了模板template,在项目中template/pageplay中有具体样式
在需要的地方引入即可,别忘了通知广播,否则不能同步状态
目前只在首页及歌单页面添加了,如有需要请参照歌单页面方式进行添加。
写在最后
非专业前端,css可能不是很优雅,时间有限,小程序未进行优化。
本人第一个较完整微信小程序,耗时较长,再加上工作比较繁忙,所以暂不打算更新了,感兴趣的小伙伴自行完善。
数据源至
node版本:https://github.com/Binaryify/NeteaseCloudMusicApi
Java版本:本人覆写移步至https://www.jianshu.com/p/bb9ed6ef41b6
小程序参考
https://github.com/sqaiyan/NeteaseMusicWxMiniApp
https://github.com/zhongjunhaoz/CloudMusic