网易云音乐 NodeJS 版 API文档

后台项目

网易云音乐 Node.js API service
disoul/electron-cloud-music
darknessomi/musicbox
sqaiyan/netmusic-node
greats3an/pyncm
需要 NodeJS 8.12+ 环境
$ git clone git@github.com:Binaryify/NeteaseCloudMusicApi.git $ npm install或者
$ git clone https://github.com/Binaryify/NeteaseCloudMusicApi.git$ npm install调用前务必阅读文档的调用前须知
$ node app.js服务器启动默认端口为 3000,若不想使用 3000 端口,可使用以下命令: Mac/Linux
$ PORT=4000 node app.jswindows 下使用 git-bash 或者 cmder 等终端执行以下命令:
$ set PORT=4000 && node app.jsv4.0.8 加入了 Vercel 配置文件,可以直接在 Vercel 下部署了,不需要自己的服务器
New ProjectImport Git Repository 并选择你 fork 的此项目并点击importPERSONAL ACCOUNT 的 selectContinuePROJECT NAME自己填,FRAMEWORK PRESET 选 Other 然后直接点 Deploy 接着等部署完成即可v3.31.0后支持Node.js调用,导入的方法为module内的文件名,返回内容包含status和body,status为状态码,body为请求返回内容,参考module_example 文件夹下的 test.js
const { login_cellphone, user_cloud } = require('NeteaseCloudMusicApi')async function main() { try { const result = await login_cellphone({ phone: '手机号', password: '密码' }) console.log(result) const result2 = await user_cloud({ cookie: result.body.cookie // 凭证 }) console.log(result2.body) } catch (error) { console.log(error) }}main()// test.tsimport { banner } from 'NeteaseCloudMusicApi'banner({ type:0 }).then(res=>{ console.log(res)})文档地址
文档地址2

可以使用下列任一命令安装这个新的包:
npm install -g @vue/cli# ORyarn global add @vue/cli安装之后,你就可以在命令行中访问 命令。你可以通过简单运行 ,看看是否展示出了一份所有可用命令的帮助信息,来验证它是否安装成功。vuevue
你还可以用这个命令来检查其版本是否正确:
vue --version如需升级全局的 Vue CLI 包,请运行:
npm update -g @vue/cli# 或者yarn global upgrade --latest @vue/cli创建一个新项目:
vue create music163_app
运行项目npm run serve
所谓的适配布局,是让页面盒子的高度,宽度,内外边距,边框大小,文字的大小,定位的元素位置等能够根据屏幕宽度自动改变大小和位置,从而达到对不同的屏幕都能够做到最完美的展示,这就是rem适配布局的优秀地方。
rem.js
function remSize(){ //获取设备的宽度 var deviceWidth=document.documentElement.clientWidth || window.innerWidth if(deviceWidth>=750){ deviceWidth=750 } if(deviceWidth<=320){ deviceWidth=320 } //750px-->1rem=100px,375px-->1rem=50px document.documentElement.style.fontSize=(deviceWidth/7.5)+'px' // 设置字体大小 document.querySelector('body').style.fontSize=0.3+"rem"}remSize()// 当窗口发生变化就调用window.onresize=function(){ remSize()}在主页面引入rem布局
index.html
<script src="<%= BASE_URL %>js/rem.js"></script>
第一步:拷贝项目下面生成的symbol代码:
index.html
//at.alicdn.com/t/*****.js
第二步:加入通用css代码(引入一次就行):
.home.vue(设置全局样式)
<style type="text/css"> .icon { width: 1em; height: 1em; vertical-align: -0.15em; fill: currentColor; overflow: hidden; }</style>第三步:挑选相应图标并获取类名,应用于页面:
<svg aria-hidden="true"> <use xlink:href="#icon-xxx"></use></svg>TopNav.vue
<template> <div > <div > <svg aria-hidden="true"> <use xlink:href="#icon-31liebiao"></use> </svg> </div> <div > <span @click="$router.push('/infoUser')">我的</span> <span >发现</span> <span>云村</span> <span>视频</span> </div> <div > <svg aria-hidden="true" @click="$router.push('/search')"> <use xlink:href="#icon-sousuo"></use> </svg> </div> </div> </template><style lang="less" scoped> .topNav{ width: 100%; height: 1rem; padding: .2rem; display: flex; justify-content: space-between; align-items: center; .topContent{ width: 65%; height: 100%; display: flex; justify-content: space-around; // align-items: center; font-size: .36rem; .active{ font-weight: 900; } } }</style># 通过 npm 安装npm i vite-plugin-style-import@1.4.1 -D# 通过 yarn 安装yarn add vite-plugin-style-import@1.4.1 -D# 通过 pnpm 安装pnpm add vite-plugin-style-import@1.4.1 -Dimport vue from '@vitejs/plugin-vue';import styleImport, { VantResolve } from 'vite-plugin-style-import';export default { plugins: [ vue(), styleImport({ resolves: [VantResolve()], }), ],};plugins>index.js
import { Swipe, SwipeItem,Button,Popup } from 'vant';// 放入数组中let plugins=[ Swipe,SwipeItem,Button,Popup ]export default function getVant(app){ plugins.forEach((item)=>{ return app.use(item) })}main.js
import getVant from './plugins'const app=createApp(App)getVant(app)按需使用,插件式引入
SwpierTop.vue
<template> <div id="swiperTop"> <van-swipe :autoplay="3000" lazy-render> <van-swipe-item v-for="image in state.images" :key="image"> <img :src="image.pic" /> </van-swipe-item> </van-swipe> </div></template><script>import axios from "axios";import { getBanner } from "@/request/api/home.js";import { reactive, onMounted } from "vue";export default { setup() { const state = reactive({ images: [ "https://img.yzcdn.cn/vant/apple-1.jpg", "https://img.yzcdn.cn/vant/apple-2.jpg", ], }); onMounted(async () => { // axios.get('http://localhost:3000/banner?type=2').then((res)=>{ // console.log(res); // state.images=res.data.banners // console.log(state.images); // }) let res = await getBanner(); state.images=res.data.banners console.log(res); }); return { state }; },};</script><style lang="less">#swiperTop { //需要在上面自己添加一个id .van-swipe { width: 100%; height: 3rem; .van-swipe-item { padding: 0 0.2rem; img { width: 100%; height: 100%; border-radius: 0.2rem; } } .van-swipe__indicator--active { background-color: rgb(219, 130, 130); } }}</style>获取网易云接口数据
axios中文文档
安装axiosnpm install axios
引入axiosimport axios from "axios";
执行get请求
setup() { const state = reactive({ images: [ "https://img.yzcdn.cn/vant/apple-1.jpg", "https://img.yzcdn.cn/vant/apple-2.jpg", ], }); onMounted(async () => { //执行 GET 请求 axios.get('http://localhost:3000/banner?type=2').then((res)=>{ console.log(res); state.images=res.data.banners console.log(state.images); })页面渲染<img :src="image.pic" />
request>index.js
// 创建axios 实例,把域名基础路径抽取出来import axios from 'axios';let service=axios.create({ baseURL:"http://localhost:3000/", timeout:3000})export default servicerequest>api>home.js
import service from "..";// 获取首页轮播图的数据export function getBanner(){ return service({ method:"GET", url:"/banner?type=2", })}import { getBanner } from "@/request/api/home.js"; onMounted(async () => { let res = await getBanner(); state.images=res.data.bannersIconList.vue
<template> <div > <div > <svg aria-hidden="true"> <use xlink:href="#icon-tuijian"></use> </svg> <span>每日推荐</span> </div> <div > <svg aria-hidden="true"> <use xlink:href="#icon-zhibo"></use> </svg> <span>私人FM</span> </div> <div > <svg aria-hidden="true"> <use xlink:href="#icon-gedan"></use> </svg> <span>歌单</span> </div> <div > <svg aria-hidden="true"> <use xlink:href="#icon-paihangbang"></use> </svg> <span>排行榜</span> </div> </div></template><style lang="less" scoped> .iconList{ width: 100%; height: 2rem; margin-top: .2rem; display: flex; justify-content: space-around; align-items: center; .iconItem{ width: 25%; height: 100%; display: flex; flex-direction: column; align-items: center; .icon{ width: 1rem; height: 1rem; } } }</style>request>api
//获取发现好歌单export function getMusicList(){ return service({ method:"GET", url:"/personalized?limit=10" })}MusicList.vue
Vue2
data() { return { musicList: [],//定义数组接收数据 }; }, methods: { async getGnedan() { let res = await getMusicList(); console.log(res); this.musicList = res.data.result; }, }, mounted() { this.getGnedan(); },vue3
MusicList.vue
// Vue3 setup() { const state = reactive({ musicList: [],//定义数组接收数据,reactive()可以响应式修改数据 }); onMounted(async () => { //onMounted执行数据 let res = await getMusicList(); console.log(res); state.musicList = res.data.result; }); return { state,changeCount };//返回数据 },MusicList.vue
<van-swipe-item v-for="item in state.musicList" :key="item"> <router-link :to="{path:'/itemMusic',query:{id:item.id}}"> <img :src="item.picUrl" alt="" /> <span > <svg aria-hidden="true"> <use xlink:href="#icon-gl-play-copy"></use> </svg> {{ changeCount(item.playCount) }} </span> <span >{{ item.name }}</span> </router-link> </van-swipe-item>views>Home.vue: 1.import 2.components注册,div 引用
<div> <TopNav /> <SwpierTop /> <IconList/> <MusicList/> </div>import MusicList from "@/components/home/MusicList.vue"; components: { TopNav, SwpierTop, IconList, MusicList, },配置路由
router.js
{ path: '/itemMusic', name: 'ItemMusic', component: () => import('../views/ItemMusic.vue') },使用router-link路由跳转并query传参
MusicList.vue
<router-link :to="{path:'/itemMusic',query:{id:item.id}}"> </router-link>使用useRoute可以获取路由传递的参数
onMounted(() => { let id = useRoute().query.id; console.log(id);}request>item.js
//获取歌单详情页的数据export function getMusicItemList(data){ return service({ method:"GET", url:`/playlist/detail?id=${data}` })*调用getMusicItemList方法
setup() { const state = reactive({ playlist: {}, //数组接收歌单详情页的数据 }); onMounted(async () => { let id = useRoute().query.id;////onMounted执行数据 console.log(id); // 获取歌单详情页 let res = await getMusicItemList(id); console.log(res); state.playlist = res.data.playlist;//将获取的api数据存储到数组playlist中}return { state };//返回数据父组件ItemMusic
<ItemMusicTop :playlist="state.playlist" /> //将父组件的state.playlist传给子组件ItemMusicTop components: { ItemMusicTop, ItemMusicList, },子组件ItemMusicTop
setup(props){ console.log(playlist) } props: ["playlist"],//子组件接收playlist数据ItemMusicTop
<template> <div > <img :src="playlist.coverImgUrl" alt="" /> <div > <svg aria-hidden="true" @click="$router.go(-1)"> <use xlink:href="#icon-zuojiantou"></use> </svg> <span>歌单</span> </div> <div > <svg aria-hidden="true"> <use xlink:href="#icon-sousuo"></use> </svg> <svg aria-hidden="true"> <use xlink:href="#icon-31liebiao"></use> </svg> </div> </div> <div > <div > <img :src="playlist.coverImgUrl" alt="" /> <div > <svg aria-hidden="true"> <use xlink:href="#icon-gl-play-copy"></use> </svg> <span>{{ changeCount(playlist.playCount) }}</span> </div> </div> <div > <p >{{ playlist.name }}</p> <div > <img :src="playlist.creator.backgroundUrl" alt="" /> <span>{{ playlist.creator.nickname }}</span> <svg aria-hidden="true"> <use xlink:href="#icon-youjiantou"></use> </svg> </div> <p > <span>{{ playlist.description }}</span> <svg aria-hidden="true"> <use xlink:href="#icon-youjiantou"></use> </svg> </p> </div> </div> <div > <div > <svg aria-hidden="true"> <use xlink:href="#icon-iconfontzhizuobiaozhun023110"></use> </svg> <span>{{ playlist.commentCount }}</span> </div> <div > <svg aria-hidden="true"> <use xlink:href="#icon-fenxiang"></use> </svg> <span>{{ playlist.shareCount }}</span> </div> <div > <svg aria-hidden="true"> <use xlink:href="#icon-iconfontzhizuobiaozhun023146"></use> </svg> <span>下载</span> </div> <div > <svg aria-hidden="true"> <use xlink:href="#icon-show_duoxuan"></use> </svg> <span>多选</span> </div> </div></template>*背景虚化
.bgimg { width: 100%; height: 11rem; position: absolute; z-index: -1; filter: blur(30px); } @click="$router.go(-1)
原因:在itemmusic中onMoonunted里数据获取都是异步的,在子组件页面渲染时数据还没有获取到。为防止数据丢失,保存本地数据
父组件itemMuisc的onMoonunted
// 防止页面刷新,数据丢失,将数据保存到sessionStorage中,setItem存储value,getItem获取value sessionStorage.setItem("itemDetail", JSON.stringify(state));// 将state以json的格式保存到key为itemDetail的会话存储中判断页面刷新后数据是否为空,空则调用sessionStorage里的数据
子组件ItemMusicTop的onMounted
// 通过props进行传值,判断如果数据拿不到,就获取sessionStorage中的数据 if(props.playlist.creator=""){ props.playlist.creator = JSON.parse(sessionStorage.getItem().playlist).creator //获取sessionStorage里的value有playlist的creator,并将json格式数据转为对象数据 }*获取歌单歌曲详情数据
//获取歌单的所有歌曲export function getItemList(data){ return service({ method:"GET", url:`/playlist/track/all?id=${data.id}&limit=${data.limit}&offset=${data.offset}` })}itemMusic
const state = reactive({ itemList: [], //1.定义数组存储歌单的歌曲数据 }); //2.获取歌单的歌曲 onMounted(async () => { let result = await getItemList({ id, limit: 10, offset: 0 }); console.log(result); state.itemList = result.data.songs} return { state }; //3.返回数据//4.将父组件的state.itemList转给子组件ItemMusicList的itemList <ItemMusicList :itemList="state.itemList" />//props子组件接收数据itemList setup(props) { console.log(props); }, props: ["itemList"], <div v-for="(item, i) in itemList" :key="i"> <span >{{ i + 1 }}</span> //如果有mv显示mv图标 <svg aria-hidden="true" v-if='item.mv !=0'> <use xlink:href="#icon-shipin"></use> </svg> <span v-for="(item1, index) in item.ar" :key="index">{{ item1.name }}</span>因为底部组件全页面都存在,所以是全局组件
app.vue
//1.import 2.components注册,div 引用<template> <FooterMusic v-show="$store.state.isFooterMusic"/></template><script>import FooterMusic from "@/components/item/FooterMusic.vue"export default { components:{ FooterMusic }}stroe
playList: [{ //播放列表默认 al: { id: 89039055, name: "雨爱抖音版", pic: 109951164966568500, picUrl: "https://p1.music.126.net/2f6UgY8Jc0Dy6jufMdIZeQ==/109951164966568495.jpg", pic_str: "109951164966568495" }, id: 1446137141, name: "雨爱(抖音版)", ar:[{name: "灏灏灏仔"}] }], playListIndex: 0, //默认下标为0,当切换歌曲时凭下标切换*获取vuex里的playList数据
itemMusicList
//使用vuex的内置函数mapState 获取vuex数据,解构赋值
import { mapState } from "vuex"; computed: { ...mapState(["playList", "playListIndex", "isbtnShow", "detailShow"]), },*页面渲染
itemMusicList
<img :src="playList[playListIndex].al.picUrl" alt="" /> <div> <p>{{ playList[playListIndex].name }}</p> <span>横滑切换上下首哦</span> </div>模板字符串(template string)是增强版的字符串,用反引号``标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量,变量名写在$()中。
ref ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件.
//实例<div ref="test" @click="test">ref 测试</div>mounted(){ console.log(this.$refs.test); },*播放
<audio ref="audio" :src="`https://music.163.com/song/media/outer/url?id=${playList[playListIndex].id}.mp3`" ></audio>//因为src里的id是变量,所以用模板字符串``*添加播放方法
methods: { play: function () { this.$refs.audio.play(); } }*使用播放方法
<svg aria-hidden="true" @click="play" > <use xlink:href="#icon-weibiaoti--"></use> </svg>*播放暂停切换
store
state:{ isbtnShow: true, //暂停按钮的显示}mutations: { updateIsbtnShow: function (state, value) { state.isbtnShow = value //调用updateIsbtnShow()方法传value从而改变state.isbtnShow的布尔值, },*播放暂停切换方法
play: function () {
// 判断音乐是否播放
if (this.\(refs.audio.paused) { this.\)refs.audio.play();
this.updateIsbtnShow(false);
} else {
this.$refs.audio.pause();
this.updateIsbtnShow(true);
}
},
*解构赋值和解构方法
//解构赋值 computed: { ...mapState(["playList", "playListIndex", "isbtnShow"]), },//解构方法...mapMutations(["updateIsbtnShow"]),*页面播放暂停图标添加判断事件实现播放暂停图标的切换
<svg aria-hidden="true" @click="play" v-if="isbtnShow"> <use xlink:href="#icon-bofanganniu"></use> </svg> <svg aria-hidden="true" @click="play" v-else> <use xlink:href="#icon-weibiaoti--"></use> </svg>