mirror of
https://gitlab.com/Binaryify/neteasecloudmusicapi.git
synced 2025-05-23 22:37:41 +08:00
parent
0ad746dad4
commit
8df5d7a790
@ -1,4 +1,8 @@
|
||||
# 更新日志
|
||||
### 3.37.0 | 2020.08.03
|
||||
- 新增`更新头像`,`歌单封面上传`接口和相关例子 [#403](https://github.com/Binaryify/NeteaseCloudMusicApi/issues/403) [#857](https://github.com/Binaryify/NeteaseCloudMusicApi/issues/857)
|
||||
- 加入`axios`依赖
|
||||
|
||||
### 3.36.0 | 2020.07.26
|
||||
- 新增`全部新碟`,`数字专辑-新碟上架`,`数字专辑&数字单曲-榜单`,`数字专辑-语种风格馆`,`数字专辑详情`接口 [#852](https://github.com/Binaryify/NeteaseCloudMusicApi/issues/852)
|
||||
- 更新`新碟上架`接口,修改传入参数,返回数据结构有变化
|
||||
|
@ -182,6 +182,8 @@
|
||||
161. 数字专辑&数字单曲-榜单
|
||||
162. 数字专辑-语种风格馆
|
||||
163. 数字专辑详情
|
||||
164. 更新头像
|
||||
165. 歌单封面上传
|
||||
|
||||
## 环境要求
|
||||
|
||||
|
10
app.js
10
app.js
@ -7,6 +7,7 @@ const packageJSON = require('./package.json')
|
||||
const exec = require('child_process').exec
|
||||
const cache = require('./util/apicache').middleware
|
||||
const { cookieToJson } = require('./util/index')
|
||||
const fileUpload = require('express-fileupload');
|
||||
// version check
|
||||
exec('npm info NeteaseCloudMusicApi version', (err, stdout, stderr) => {
|
||||
if(!err){
|
||||
@ -47,12 +48,15 @@ app.use((req, res, next) => {
|
||||
app.use(bodyParser.json())
|
||||
app.use(bodyParser.urlencoded({extended: false}))
|
||||
|
||||
// cache
|
||||
app.use(cache('2 minutes', ((req, res) => res.statusCode === 200)))
|
||||
app.use(fileUpload());
|
||||
|
||||
|
||||
|
||||
// static
|
||||
app.use(express.static(path.join(__dirname, 'public')))
|
||||
|
||||
// cache
|
||||
app.use(cache('2 minutes', ((req, res) => res.statusCode === 200)))
|
||||
// router
|
||||
const special = {
|
||||
'daily_signin.js': '/daily_signin',
|
||||
@ -69,7 +73,7 @@ fs.readdirSync(path.join(__dirname, 'module')).reverse().forEach(file => {
|
||||
if(typeof req.query.cookie === 'string'){
|
||||
req.query.cookie = cookieToJson(req.query.cookie)
|
||||
}
|
||||
let query = Object.assign({}, {cookie: req.cookies}, req.query, req.body )
|
||||
let query = Object.assign({}, {cookie: req.cookies}, req.query, req.body, req.files )
|
||||
|
||||
question(query, request)
|
||||
.then(answer => {
|
||||
|
@ -179,6 +179,8 @@
|
||||
161. 数字专辑&数字单曲-榜单
|
||||
162. 数字专辑-语种风格馆
|
||||
163. 数字专辑详情
|
||||
164. 更新头像
|
||||
165. 歌单封面上传
|
||||
|
||||
## 安装
|
||||
|
||||
@ -525,6 +527,20 @@ signature:用户签名
|
||||
|
||||
**调用例子 :** `/user/update?gender=0&signature=测试签名&city=440300&nickname=binary&birthday=1525918298004&province=440000`
|
||||
|
||||
### 更新头像
|
||||
说明 : 登陆后调用此接口,使用`'Content-Type': 'multipart/form-data'`上传图片formData(name为'imgFile'),可更新头像(参考:https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/public/upload.html)
|
||||
|
||||
**可选参数 :**
|
||||
|
||||
`imgSize` : 图片尺寸,默认为300
|
||||
|
||||
`imgX` : 水平裁剪偏移,方形图片可不传,默认为0
|
||||
`imgY` : 垂直裁剪偏移,方形图片可不传,默认为0
|
||||
|
||||
**接口地址 :** `/avatar/upload`
|
||||
|
||||
**调用例子 :** `/avatar/upload?imgSize=200`
|
||||
|
||||
### 国家编码列表
|
||||
说明 : 调用此接口,可获取国家编码列表
|
||||
|
||||
@ -607,6 +623,25 @@ tags: 歌单标签
|
||||
|
||||
**调用例子 :** `/playlist/tags/update?id=24381616&tags=学习`
|
||||
|
||||
|
||||
### 歌单封面上传
|
||||
说明 : 登陆后调用此接口,使用`'Content-Type': 'multipart/form-data'`上传图片formData(name为'imgFile'),可更新歌单封面(参考:https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/public//playlist_cover_update.html)
|
||||
|
||||
**必选参数 :**
|
||||
`id`: 歌单id 3143833470
|
||||
|
||||
**可选参数 :**
|
||||
|
||||
`imgSize` : 图片尺寸,默认为300
|
||||
|
||||
`imgX` : 水平裁剪偏移,方形图片可不传,默认为0
|
||||
`imgY` : 垂直裁剪偏移,方形图片可不传,默认为0
|
||||
|
||||
**接口地址 :** `/playlist/cover/update`
|
||||
|
||||
**调用例子 :** `/playlist/cover/update?id=3143833470&imgSize=200`
|
||||
|
||||
|
||||
### 调整歌单顺序
|
||||
说明 : 登陆后调用此接口,可以根据歌单id顺序调整歌单顺序
|
||||
|
||||
|
22
module/avatar_upload.js
Normal file
22
module/avatar_upload.js
Normal file
@ -0,0 +1,22 @@
|
||||
const uploadPlugin = require('../plugins/upload')
|
||||
module.exports = async (query, request) => {
|
||||
const uploadInfo = await uploadPlugin(query, request)
|
||||
const res = await request(
|
||||
'POST',
|
||||
`https://music.163.com/weapi/user/avatar/upload/v1`,
|
||||
{
|
||||
imgid: uploadInfo.imgId
|
||||
},
|
||||
{ crypto: 'weapi', cookie: query.cookie, proxy: query.proxy }
|
||||
)
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
code: 200,
|
||||
data: {
|
||||
...uploadInfo,
|
||||
...res.body,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
23
module/playlist_cover_update.js
Normal file
23
module/playlist_cover_update.js
Normal file
@ -0,0 +1,23 @@
|
||||
const uploadPlugin = require('../plugins/upload')
|
||||
module.exports = async (query, request) => {
|
||||
const uploadInfo = await uploadPlugin(query, request)
|
||||
const res = await request(
|
||||
'POST',
|
||||
`https://music.163.com/weapi/playlist/cover/update`,
|
||||
{
|
||||
id: query.id,
|
||||
coverImgId: uploadInfo.imgId
|
||||
},
|
||||
{ crypto: 'weapi', cookie: query.cookie, proxy: query.proxy }
|
||||
)
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
code: 200,
|
||||
data: {
|
||||
...uploadInfo,
|
||||
...res.body,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@ module.exports = (query, request) => {
|
||||
need_preview_url: 'true',
|
||||
total: true
|
||||
}
|
||||
console.log({data})
|
||||
// /api/videotimeline/otherclient/get
|
||||
return request(
|
||||
'POST', `https://music.163.com/api/videotimeline/otherclient/get`, data, {
|
||||
|
97
package.json
97
package.json
@ -1,51 +1,52 @@
|
||||
{
|
||||
"name": "NeteaseCloudMusicApi",
|
||||
"version": "3.36.0",
|
||||
"description": "网易云音乐 NodeJS 版 API",
|
||||
"scripts": {
|
||||
"start": "node app.js",
|
||||
"test": "mocha -r intelli-espower-loader -t 20000 app.test.js --exit",
|
||||
"lint-fix": "eslint --fix --ext .js app.js module/ util/ test/ "
|
||||
},
|
||||
"keywords": [
|
||||
"网易云音乐",
|
||||
"网易云",
|
||||
"音乐",
|
||||
"网易云音乐nodejs"
|
||||
],
|
||||
"main": "main.js",
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"eslint --fix",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"author": "binaryify",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"module",
|
||||
"util"
|
||||
],
|
||||
"dependencies": {
|
||||
"express": "^4.17.1",
|
||||
"pac-proxy-agent": "^3.0.1",
|
||||
"request": "^2.88.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^6.6.0",
|
||||
"eslint-plugin-html": "^6.0.0",
|
||||
"husky": "^3.0.9",
|
||||
"intelli-espower-loader": "^1.0.1",
|
||||
"lint-staged": "^9.4.2",
|
||||
"mocha": "^6.2.2",
|
||||
"power-assert": "^1.6.1"
|
||||
"name": "NeteaseCloudMusicApi",
|
||||
"version": "3.37.0",
|
||||
"description": "网易云音乐 NodeJS 版 API",
|
||||
"scripts": {
|
||||
"start": "node app.js",
|
||||
"test": "mocha -r intelli-espower-loader -t 20000 app.test.js --exit",
|
||||
"lint-fix": "eslint --fix --ext .js app.js module/ util/ test/ "
|
||||
},
|
||||
"keywords": [
|
||||
"网易云音乐",
|
||||
"网易云",
|
||||
"音乐",
|
||||
"网易云音乐nodejs"
|
||||
],
|
||||
"main": "main.js",
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"eslint --fix",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"author": "binaryify",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"module",
|
||||
"util"
|
||||
],
|
||||
"dependencies": {
|
||||
"axios": "^0.19.2",
|
||||
"express": "^4.17.1",
|
||||
"pac-proxy-agent": "^3.0.1",
|
||||
"request": "^2.88.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^6.6.0",
|
||||
"eslint-plugin-html": "^6.0.0",
|
||||
"husky": "^3.0.9",
|
||||
"intelli-espower-loader": "^1.0.1",
|
||||
"lint-staged": "^9.4.2",
|
||||
"mocha": "^6.2.2",
|
||||
"power-assert": "^1.6.1"
|
||||
}
|
||||
}
|
||||
|
48
plugins/upload.js
Normal file
48
plugins/upload.js
Normal file
@ -0,0 +1,48 @@
|
||||
const axios = require('axios')
|
||||
module.exports = async (query, request) => {
|
||||
const data = {
|
||||
bucket: 'yyimgs',
|
||||
ext: 'jpg',
|
||||
filename: query.imgFile.name,
|
||||
local: false,
|
||||
nos_product: 0,
|
||||
return_body: `{"code":200,"size":"$(ObjectSize)"}`,
|
||||
type: 'other',
|
||||
}
|
||||
// 获取key和token
|
||||
const res = await request(
|
||||
'POST',
|
||||
`https://music.163.com/weapi/nos/token/alloc`,
|
||||
data,
|
||||
{ crypto: 'weapi', cookie: query.cookie, proxy: query.proxy }
|
||||
)
|
||||
// 上传图片
|
||||
const res2 = await axios({
|
||||
method: 'post',
|
||||
url: `https://nosup-hz1.127.net/yyimgs/${res.body.result.objectKey}?offset=0&complete=true&version=1.0`,
|
||||
headers: {
|
||||
'x-nos-token': res.body.result.token,
|
||||
'Content-Type': 'image/jpeg',
|
||||
},
|
||||
data: query.imgFile.data,
|
||||
})
|
||||
// 获取裁剪后图片的id
|
||||
const imgSize = query.imgSize || 300
|
||||
const imgX = query.imgX || 0
|
||||
const imgY = query.imgY || 0
|
||||
const res3 = await request(
|
||||
'POST',
|
||||
`https://music.163.com/upload/img/op?id=${res.body.result.docId}&op=${imgX}y${imgY}y${imgSize}y${imgSize}`,
|
||||
{},
|
||||
{ crypto: 'weapi', cookie: query.cookie, proxy: query.proxy }
|
||||
)
|
||||
|
||||
return {
|
||||
// ...res.body.result,
|
||||
// ...res2.data,
|
||||
// ...res3.body,
|
||||
url_pre: 'https://p1.music.126.net/' + res.body.result.objectKey,
|
||||
url: res3.body.url,
|
||||
imgId: res3.body.id
|
||||
}
|
||||
}
|
80
public/avatar_update.html
Normal file
80
public/avatar_update.html
Normal file
@ -0,0 +1,80 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>更新头像</title>
|
||||
</head>
|
||||
<body>
|
||||
<input id="file" type="file" name="filename" />
|
||||
<img id="avatar" style="height: 200px; width: 200px; border-radius: 50%;" />
|
||||
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js
|
||||
"></script>
|
||||
<script>
|
||||
const phone = ''
|
||||
const password = ''
|
||||
const port = 3000
|
||||
let cookieToken = ''
|
||||
if (!phone || !password) {
|
||||
const msg = '请设置你的手机号码和密码'
|
||||
alert(msg)
|
||||
throw new Error(msg)
|
||||
}
|
||||
|
||||
main()
|
||||
login()
|
||||
async function main() {
|
||||
document.querySelector('input[type="file"]').addEventListener(
|
||||
'change',
|
||||
function (e) {
|
||||
var file = this.files[0]
|
||||
upload(file)
|
||||
},
|
||||
false
|
||||
)
|
||||
const res = await axios({
|
||||
url: `http://localhost:${port}/user/detail?uid=32953014`,
|
||||
withCredentials: true, //关键
|
||||
})
|
||||
document.querySelector('#avatar').src = res.data.profile.avatarUrl
|
||||
}
|
||||
async function login() {
|
||||
const res = await axios({
|
||||
url: `http://localhost:${port}/login/cellphone?phone=${phone}&password=${password}`,
|
||||
withCredentials: true, //关键
|
||||
})
|
||||
cookieToken = res.data.cookie
|
||||
}
|
||||
async function upload(file) {
|
||||
var formData = new FormData()
|
||||
formData.append('imgFile', file)
|
||||
const imgSize = await getImgSize(file)
|
||||
const res = await axios({
|
||||
method: 'post',
|
||||
url: `http://localhost:3000/avatar/upload?cookie=${cookieToken}&imgSize=${imgSize.width}&imgX=0&imgY=0`,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
data: formData,
|
||||
})
|
||||
document.querySelector('#avatar').src = res.data.data.url
|
||||
}
|
||||
function getImgSize(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader()
|
||||
reader.readAsDataURL(file)
|
||||
reader.onload = function (theFile) {
|
||||
let image = new Image()
|
||||
image.src = theFile.target.result
|
||||
image.onload = function () {
|
||||
resolve({
|
||||
width: this.width,
|
||||
height: this.height
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
86
public/playlist_cover_update.html
Normal file
86
public/playlist_cover_update.html
Normal file
@ -0,0 +1,86 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>歌单封面上传</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<input id="file" type="file" name="filename" />
|
||||
<img id="avatar" style="height: 200px; width: 200px; border-radius: 50%;" />
|
||||
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js
|
||||
"></script>
|
||||
<script>
|
||||
const phone = ''
|
||||
const password = ''
|
||||
const playlist_id = ''
|
||||
const port = 3000
|
||||
let cookieToken = ''
|
||||
if (!phone || !password) {
|
||||
const msg = '请设置你的手机号码和密码'
|
||||
alert(msg)
|
||||
throw new Error(msg)
|
||||
}
|
||||
if (!playlist_id) {
|
||||
const msg = '请设置你的歌单id'
|
||||
alert(msg)
|
||||
throw new Error(msg)
|
||||
}
|
||||
|
||||
main()
|
||||
login()
|
||||
async function main() {
|
||||
document.querySelector('input[type="file"]').addEventListener(
|
||||
'change',
|
||||
function (e) {
|
||||
var file = this.files[0]
|
||||
upload(file)
|
||||
},
|
||||
false
|
||||
)
|
||||
const res = await axios({
|
||||
url: `http://localhost:${port}/playlist/detail?id=${playlist_id}`,
|
||||
})
|
||||
document.querySelector('#avatar').src = res.data.playlist.coverImgUrl
|
||||
}
|
||||
async function login() {
|
||||
const res = await axios({
|
||||
url: `http://localhost:${port}/login/cellphone?phone=${phone}&password=${password}`,
|
||||
withCredentials: true, //关键
|
||||
})
|
||||
cookieToken = res.data.cookie
|
||||
}
|
||||
async function upload(file) {
|
||||
var formData = new FormData()
|
||||
formData.append('imgFile', file)
|
||||
const imgSize = await getImgSize(file)
|
||||
const res = await axios({
|
||||
method: 'post',
|
||||
url: `http://localhost:3000/playlist/cover/update?id=${playlist_id}&cookie=${cookieToken}&imgSize=${imgSize.width}&imgX=0&imgY=0`,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
data: formData,
|
||||
})
|
||||
document.querySelector('#avatar').src = res.data.data.url
|
||||
}
|
||||
function getImgSize(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader()
|
||||
reader.readAsDataURL(file)
|
||||
reader.onload = function (theFile) {
|
||||
let image = new Image()
|
||||
image.src = theFile.target.result
|
||||
image.onload = function () {
|
||||
resolve({
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user