mirror of
https://gitlab.com/Binaryify/neteasecloudmusicapi.git
synced 2025-05-23 22:37:41 +08:00
v4.0,新增云盘上传接口以及二维码登录相关接口和相关demo,升级部分接口加密方法 #70 , #121 ,#121 , #153 ,#248 , #705 , #716 , #745 , #1055
This commit is contained in:
parent
77775b25f5
commit
75cb79fc6a
@ -1,3 +1,4 @@
|
|||||||
static
|
static
|
||||||
docs
|
docs
|
||||||
node_modules
|
node_modules
|
||||||
|
module_example
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
# 更新日志
|
# 更新日志
|
||||||
|
### 4.0.0 | 2021.1.03
|
||||||
|
- 新增云盘上传接口,新增二维码登录相关接口和相关demo(http://localhost:3000/qrlogin.html, http://localhost:3000/cloud.html),更新 d.ts
|
||||||
|
|
||||||
|
- 升级部分接口加密方法("linuxapi" 都替换到了"api")
|
||||||
|
|
||||||
|
- 更新 `login/status` 接口(返回字段和之前不一样)
|
||||||
|
|
||||||
### 3.47.5 | 2020.12.20
|
### 3.47.5 | 2020.12.20
|
||||||
- 更新appver [#1060](https://github.com/Binaryify/NeteaseCloudMusicApi/issues/1060)
|
- 更新appver [#1060](https://github.com/Binaryify/NeteaseCloudMusicApi/issues/1060)
|
||||||
|
|
||||||
|
@ -213,6 +213,10 @@
|
|||||||
195. 关注歌手新歌
|
195. 关注歌手新歌
|
||||||
196. 关注歌手新MV
|
196. 关注歌手新MV
|
||||||
197. 歌手详情
|
197. 歌手详情
|
||||||
|
198. 云盘上传
|
||||||
|
199. 二维码登录
|
||||||
|
200. 话题详情
|
||||||
|
201. 话题详情热门动态
|
||||||
|
|
||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
@ -359,7 +363,7 @@ $ sudo docker run -d -p 3000:3000 netease-music-api
|
|||||||
|
|
||||||
!> 部分接口如登录接口不能调用太频繁 , 否则可能会触发 503 错误或者 ip 高频错误 ,若需频繁调用 , 需要准备 IP 代理池 (更新:已加入缓存机制,但仍需注意).
|
!> 部分接口如登录接口不能调用太频繁 , 否则可能会触发 503 错误或者 ip 高频错误 ,若需频繁调用 , 需要准备 IP 代理池 (更新:已加入缓存机制,但仍需注意).
|
||||||
|
|
||||||
!> 本项目仅供学习使用,请尊重版权,请勿利用此项目从事商业行为
|
!> 本项目仅供学习使用,请尊重版权,请勿利用此项目从事商业行为或进行破坏版权行为
|
||||||
|
|
||||||
!> 文档可能会有缓存 , 如果文档版本和 github 上的版本不一致,请清除缓存再查看
|
!> 文档可能会有缓存 , 如果文档版本和 github 上的版本不一致,请清除缓存再查看
|
||||||
|
|
||||||
@ -420,22 +424,34 @@ v3.30.0后支持手动传入cookie,登录接口返回内容新增 `cookie` 字
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
#### 3. 二维码登录
|
#### 3. 二维码登录
|
||||||
说明: 二维码登录涉及到3个接口
|
说明: 二维码登录涉及到3个接口,调用务必带上时间戳,防止缓存
|
||||||
1. 二维码key生成接口
|
##### 1. 二维码key生成接口
|
||||||
说明: 调用此接口可生成一个key
|
|
||||||
**接口地址 :** `/login/qr/key`
|
|
||||||
|
|
||||||
2. 二维码生成接口
|
说明: 调用此接口可生成一个key
|
||||||
|
|
||||||
|
**接口地址 :** `/login/qr/key`
|
||||||
|
##### 2. 二维码生成接口
|
||||||
说明: 调用此接口传入上一个接口生成的key可生成二维码图片的base64和二维码信息,可使用base64展示图片,或者使用二维码信息内容自行使用第三方二维码生产库渲染二维码
|
说明: 调用此接口传入上一个接口生成的key可生成二维码图片的base64和二维码信息,可使用base64展示图片,或者使用二维码信息内容自行使用第三方二维码生产库渲染二维码
|
||||||
可选参数: `qrimg` 传入后
|
|
||||||
|
必选参数: `key`,由第一个接口生成
|
||||||
|
|
||||||
|
可选参数: `qrimg` 传入后会额外返回二维码图片base64编码
|
||||||
|
|
||||||
**接口地址 :** `/login/qr/create`
|
**接口地址 :** `/login/qr/create`
|
||||||
|
|
||||||
|
**调用例子 :** `/login/qr/create?key=xxx`
|
||||||
|
|
||||||
3. 二维码检测扫码状态接口
|
|
||||||
说明: 轮询此接口可获取二维码扫码状态,801为等待扫码,802为待确认,803为授权登陆成功
|
##### 3. 二维码检测扫码状态接口
|
||||||
|
说明: 轮询此接口可获取二维码扫码状态,800为二维码过期,801为等待扫码,802为待确认,803为授权登录成功(803状态码下会返回cookies)
|
||||||
|
|
||||||
|
必选参数: `key`,由第一个接口生成
|
||||||
|
|
||||||
**接口地址 :** `/login/qr/check`
|
**接口地址 :** `/login/qr/check`
|
||||||
|
|
||||||
|
**调用例子 :** `/login/qr/check?key=xxx`
|
||||||
|
|
||||||
|
调用可参考项目文件例子`/public/qrlogin.html` (访问地址:http://localhost:3000/qrlogin.html)
|
||||||
|
|
||||||
|
|
||||||
#### 注意
|
#### 注意
|
||||||
@ -640,7 +656,7 @@ signature:用户签名
|
|||||||
**调用例子 :** `/user/update?gender=0&signature=测试签名&city=440300&nickname=binary&birthday=1525918298004&province=440000`
|
**调用例子 :** `/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/avatar_update.html)
|
说明 : 登录后调用此接口,使用`'Content-Type': 'multipart/form-data'`上传图片formData(name为'imgFile'),可更新头像(参考:https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/public/avatar_update.html),支持命令行调用,参考module_example目录下`avatar_upload.js`
|
||||||
|
|
||||||
**可选参数 :**
|
**可选参数 :**
|
||||||
|
|
||||||
@ -936,6 +952,21 @@ tags: 歌单标签
|
|||||||
|
|
||||||
**调用例子 :** `/hot/topic?limit=30&offset=30`
|
**调用例子 :** `/hot/topic?limit=30&offset=30`
|
||||||
|
|
||||||
|
### 获取话题详情
|
||||||
|
|
||||||
|
说明 : 调用此接口 , 可获取话题详情
|
||||||
|
|
||||||
|
**接口地址 :** `/topic/detail`
|
||||||
|
|
||||||
|
**调用例子 :** `/topic/detail?actid=111551188`
|
||||||
|
### 获取话题详情热门动态
|
||||||
|
|
||||||
|
说明 : 调用此接口 , 可获取话题详情热门动态
|
||||||
|
|
||||||
|
**接口地址 :** `/topic/detail/event/hot`
|
||||||
|
|
||||||
|
**调用例子 :** `/topic/detail/event/hot?actid=111551188`
|
||||||
|
|
||||||
|
|
||||||
### 云村热评
|
### 云村热评
|
||||||
说明 : 登录后调用此接口 , 可获取云村热评
|
说明 : 登录后调用此接口 , 可获取云村热评
|
||||||
@ -1238,10 +1269,6 @@ mp3url 不能直接用 , 可通过 `/song/url` 接口传入歌曲 id 获取具
|
|||||||
|
|
||||||
**调用例子 :** `/search?keywords= 海阔天空` `/cloudsearch?keywords= 海阔天空`
|
**调用例子 :** `/search?keywords= 海阔天空` `/cloudsearch?keywords= 海阔天空`
|
||||||
|
|
||||||
返回数据如下图 :
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
### 默认搜索关键词
|
### 默认搜索关键词
|
||||||
说明 : 调用此接口 , 可获取默认搜索关键词
|
说明 : 调用此接口 , 可获取默认搜索关键词
|
||||||
|
|
||||||
@ -1443,8 +1470,7 @@ mp3url 不能直接用 , 可通过 `/song/url` 接口传入歌曲 id 获取具
|
|||||||
|
|
||||||
### 歌曲评论
|
### 歌曲评论
|
||||||
|
|
||||||
说明 : 调用此接口 , 传入音乐 id 和 limit 参数 , 可获得该音乐的所有评论 ( 不需要
|
说明 : 调用此接口 , 传入音乐 id 和 limit 参数 , 可获得该音乐的所有评论 ( 不需要登录 )
|
||||||
登录 )
|
|
||||||
|
|
||||||
**必选参数 :** `id`: 音乐 id
|
**必选参数 :** `id`: 音乐 id
|
||||||
|
|
||||||
@ -2553,6 +2579,19 @@ type : 地区
|
|||||||
|
|
||||||
**调用例子 :** `/user/cloud/del`
|
**调用例子 :** `/user/cloud/del`
|
||||||
|
|
||||||
|
### 云盘上传
|
||||||
|
说明 : 登录后调用此接口,使用`'Content-Type': 'multipart/form-data'`上传mp3 formData(name为'songFile'),可上传歌曲到云盘
|
||||||
|
|
||||||
|
参考: https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/public/cloud.html
|
||||||
|
|
||||||
|
访问地址: http://localhost:3000/qrlogin.html)
|
||||||
|
|
||||||
|
支持命令行调用,参考module_example目录下`song_upload.js`
|
||||||
|
|
||||||
|
**接口地址 :** `/cloud`
|
||||||
|
|
||||||
|
**调用例子 :** `/cloud`
|
||||||
|
|
||||||
### 电台banner
|
### 电台banner
|
||||||
说明 : 调用此接口,可获取电台banner
|
说明 : 调用此接口,可获取电台banner
|
||||||
|
|
||||||
|
29
interface.d.ts
vendored
29
interface.d.ts
vendored
@ -1360,3 +1360,32 @@ export function artist_detail(
|
|||||||
id: number | string
|
id: number | string
|
||||||
} & RequestBaseConfig,
|
} & RequestBaseConfig,
|
||||||
): Promise<Response>
|
): Promise<Response>
|
||||||
|
|
||||||
|
export function cloud(params: RequestBaseConfig): Promise<Response>
|
||||||
|
|
||||||
|
export function topic_detail(
|
||||||
|
params: {
|
||||||
|
actid?: number | string
|
||||||
|
} & RequestBaseConfig,
|
||||||
|
): Promise<Response>
|
||||||
|
|
||||||
|
export function topic_detail_event_hot(
|
||||||
|
params: {
|
||||||
|
actid?: number | string
|
||||||
|
} & RequestBaseConfig,
|
||||||
|
): Promise<Response>
|
||||||
|
|
||||||
|
export function login_qr_key(params: RequestBaseConfig): Promise<Response>
|
||||||
|
|
||||||
|
export function login_qr_create(
|
||||||
|
params: {
|
||||||
|
key?: number | string
|
||||||
|
qrimg?: boolean | string
|
||||||
|
} & RequestBaseConfig,
|
||||||
|
): Promise<Response>
|
||||||
|
|
||||||
|
export function login_qr_check(
|
||||||
|
params: {
|
||||||
|
key?: number | string
|
||||||
|
} & RequestBaseConfig,
|
||||||
|
): Promise<Response>
|
||||||
|
@ -12,6 +12,6 @@ module.exports = (query, request) => {
|
|||||||
'POST',
|
'POST',
|
||||||
`https://music.163.com/api/v2/banner/get`,
|
`https://music.163.com/api/v2/banner/get`,
|
||||||
{ clientType: type },
|
{ clientType: type },
|
||||||
{ crypto: 'linuxapi', proxy: query.proxy, realIP: query.realIP },
|
{ crypto: 'api', proxy: query.proxy, realIP: query.realIP },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
132
module/cloud.js
Normal file
132
module/cloud.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
const mm = require('music-metadata')
|
||||||
|
const uploadPlugin = require('../plugins/songUpload')
|
||||||
|
const md5 = require('md5')
|
||||||
|
module.exports = async (query, request) => {
|
||||||
|
query.cookie.os = 'pc'
|
||||||
|
query.cookie.appver = '2.7.1.198277'
|
||||||
|
const bitrate = 999000
|
||||||
|
if (!query.songFile) {
|
||||||
|
return Promise.reject({
|
||||||
|
status: 500,
|
||||||
|
body: {
|
||||||
|
msg: '请上传音乐文件',
|
||||||
|
code: 500,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!query.songFile.md5) {
|
||||||
|
// 命令行上传没有md5和size信息,需要填充
|
||||||
|
query.songFile.md5 = md5(query.songFile.data)
|
||||||
|
query.songFile.size = query.songFile.data.byteLength
|
||||||
|
}
|
||||||
|
const res = await request(
|
||||||
|
'POST',
|
||||||
|
`https://interface.music.163.com/api/cloud/upload/check`,
|
||||||
|
{
|
||||||
|
bitrate: String(bitrate),
|
||||||
|
ext: '',
|
||||||
|
length: query.songFile.size,
|
||||||
|
md5: query.songFile.md5,
|
||||||
|
songId: '0',
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
crypto: 'weapi',
|
||||||
|
cookie: query.cookie,
|
||||||
|
proxy: query.proxy,
|
||||||
|
realIP: query.realIP,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
let artist = ''
|
||||||
|
let album = ''
|
||||||
|
let songName = ''
|
||||||
|
try {
|
||||||
|
const metadata = await mm.parseBuffer(query.songFile.data, 'audio/mpeg')
|
||||||
|
if (metadata.native.ID3v1) {
|
||||||
|
metadata.native.ID3v1.forEach((item) => {
|
||||||
|
// console.log(item.id, item.value)
|
||||||
|
if (item.id === 'title') {
|
||||||
|
songName = item.value
|
||||||
|
}
|
||||||
|
if (item.id === 'artist') {
|
||||||
|
artist = item.value
|
||||||
|
}
|
||||||
|
if (item.id === 'album') {
|
||||||
|
album = item.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// console.log({
|
||||||
|
// songName,
|
||||||
|
// album,
|
||||||
|
// songName,
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
const tokenRes = await request(
|
||||||
|
'POST',
|
||||||
|
`https://music.163.com/weapi/nos/token/alloc`,
|
||||||
|
{
|
||||||
|
bucket: '',
|
||||||
|
ext: 'mp3',
|
||||||
|
filename: query.songFile.name.replace('.mp3', ''),
|
||||||
|
local: false,
|
||||||
|
nos_product: 3,
|
||||||
|
type: 'audio',
|
||||||
|
md5: query.songFile.md5,
|
||||||
|
},
|
||||||
|
{ crypto: 'weapi', cookie: query.cookie, proxy: query.proxy },
|
||||||
|
)
|
||||||
|
|
||||||
|
if (res.body.needUpload) {
|
||||||
|
const uploadInfo = await uploadPlugin(query, request)
|
||||||
|
// console.log('uploadInfo', uploadInfo.body.result.resourceId)
|
||||||
|
}
|
||||||
|
// console.log(tokenRes.body.result)
|
||||||
|
const res2 = await request(
|
||||||
|
'POST',
|
||||||
|
`https://music.163.com/api/upload/cloud/info/v2`,
|
||||||
|
{
|
||||||
|
md5: query.songFile.md5,
|
||||||
|
songid: res.body.songId,
|
||||||
|
filename: query.songFile.name,
|
||||||
|
song: songName || query.songFile.name.replace('.mp3', ''),
|
||||||
|
album: album || '未知专辑',
|
||||||
|
artist: artist || '未知艺术家',
|
||||||
|
bitrate: String(bitrate),
|
||||||
|
resourceId: tokenRes.body.result.resourceId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
crypto: 'weapi',
|
||||||
|
cookie: query.cookie,
|
||||||
|
proxy: query.proxy,
|
||||||
|
realIP: query.realIP,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
// console.log({ res2, privateCloud: res2.body.privateCloud })
|
||||||
|
// console.log(res.body.songId, 'songid')
|
||||||
|
const res3 = await request(
|
||||||
|
'POST',
|
||||||
|
`https://interface.music.163.com/api/cloud/pub/v2`,
|
||||||
|
{
|
||||||
|
songid: res2.body.songId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
crypto: 'weapi',
|
||||||
|
cookie: query.cookie,
|
||||||
|
proxy: query.proxy,
|
||||||
|
realIP: query.realIP,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
// console.log({ res3 })
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: {
|
||||||
|
...res.body,
|
||||||
|
...res3.body,
|
||||||
|
// ...uploadInfo,
|
||||||
|
},
|
||||||
|
cookie: res.cookie,
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ module.exports = (query, request) => {
|
|||||||
limit: query.limit || 20,
|
limit: query.limit || 20,
|
||||||
offset: query.offset || 0,
|
offset: query.offset || 0,
|
||||||
}
|
}
|
||||||
return request('POST', `https://music.163.com/weapi/act/hot`, data, {
|
return request('POST', `https://music.163.com/api/act/hot`, data, {
|
||||||
crypto: 'weapi',
|
crypto: 'weapi',
|
||||||
cookie: query.cookie,
|
cookie: query.cookie,
|
||||||
proxy: query.proxy,
|
proxy: query.proxy,
|
||||||
|
@ -16,10 +16,8 @@ module.exports = async (query, request) => {
|
|||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
body: {
|
body: {
|
||||||
data: {
|
data: result.body,
|
||||||
...result.body,
|
code: 200,
|
||||||
code: 200,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
cookie: result.cookie,
|
cookie: result.cookie,
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ module.exports = (query, request) => {
|
|||||||
tv: -1,
|
tv: -1,
|
||||||
}
|
}
|
||||||
return request('POST', `https://music.163.com/api/song/lyric`, data, {
|
return request('POST', `https://music.163.com/api/song/lyric`, data, {
|
||||||
crypto: 'linuxapi',
|
crypto: 'api',
|
||||||
cookie: query.cookie,
|
cookie: query.cookie,
|
||||||
proxy: query.proxy,
|
proxy: query.proxy,
|
||||||
realIP: query.realIP,
|
realIP: query.realIP,
|
||||||
|
@ -7,7 +7,7 @@ module.exports = (query, request) => {
|
|||||||
s: query.s || 8,
|
s: query.s || 8,
|
||||||
}
|
}
|
||||||
return request('POST', `https://music.163.com/api/v6/playlist/detail`, data, {
|
return request('POST', `https://music.163.com/api/v6/playlist/detail`, data, {
|
||||||
crypto: 'linuxapi',
|
crypto: 'api',
|
||||||
cookie: query.cookie,
|
cookie: query.cookie,
|
||||||
proxy: query.proxy,
|
proxy: query.proxy,
|
||||||
realIP: query.realIP,
|
realIP: query.realIP,
|
||||||
|
11
module/topic_detail.js
Normal file
11
module/topic_detail.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module.exports = (query, request) => {
|
||||||
|
const data = {
|
||||||
|
actid: query.actid,
|
||||||
|
}
|
||||||
|
return request('POST', `https://music.163.com/api/act/detail`, data, {
|
||||||
|
crypto: 'weapi',
|
||||||
|
cookie: query.cookie,
|
||||||
|
proxy: query.proxy,
|
||||||
|
realIP: query.realIP,
|
||||||
|
})
|
||||||
|
}
|
11
module/topic_detail_event_hot.js
Normal file
11
module/topic_detail_event_hot.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module.exports = (query, request) => {
|
||||||
|
const data = {
|
||||||
|
actid: query.actid,
|
||||||
|
}
|
||||||
|
return request('POST', `https://music.163.com/api/act/event/hot`, data, {
|
||||||
|
crypto: 'weapi',
|
||||||
|
cookie: query.cookie,
|
||||||
|
proxy: query.proxy,
|
||||||
|
realIP: query.realIP,
|
||||||
|
})
|
||||||
|
}
|
@ -6,7 +6,7 @@ module.exports = (query, request) => {
|
|||||||
`https://music.163.com/api/toplist`,
|
`https://music.163.com/api/toplist`,
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
crypto: 'linuxapi',
|
crypto: 'api',
|
||||||
cookie: query.cookie,
|
cookie: query.cookie,
|
||||||
proxy: query.proxy,
|
proxy: query.proxy,
|
||||||
realIP: query.realIP,
|
realIP: query.realIP,
|
||||||
|
@ -5,7 +5,7 @@ module.exports = (query, request) => {
|
|||||||
limit: query.limit || 30,
|
limit: query.limit || 30,
|
||||||
offset: query.offset || 0,
|
offset: query.offset || 0,
|
||||||
}
|
}
|
||||||
return request('POST', `https://music.163.com/weapi/v1/cloud/get`, data, {
|
return request('POST', `https://music.163.com/api/v1/cloud/get`, data, {
|
||||||
crypto: 'weapi',
|
crypto: 'weapi',
|
||||||
cookie: query.cookie,
|
cookie: query.cookie,
|
||||||
proxy: query.proxy,
|
proxy: query.proxy,
|
||||||
|
23
module_example/song_upload.js
Normal file
23
module_example/song_upload.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
const { cloud, login_cellphone } = require('../main')
|
||||||
|
const fs = require('fs')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const result = await login_cellphone({
|
||||||
|
phone: '手机号',
|
||||||
|
password: '密码',
|
||||||
|
})
|
||||||
|
const filePath = './test.mp3'
|
||||||
|
try {
|
||||||
|
await cloud({
|
||||||
|
songFile: {
|
||||||
|
name: path.basename(filePath),
|
||||||
|
data: fs.readFileSync(filePath),
|
||||||
|
},
|
||||||
|
cookie: result.body.cookie,
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error, 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main()
|
BIN
module_example/test.mp3
Normal file
BIN
module_example/test.mp3
Normal file
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "NeteaseCloudMusicApi",
|
"name": "NeteaseCloudMusicApi",
|
||||||
"version": "3.47.5",
|
"version": "4.0.0",
|
||||||
"description": "网易云音乐 NodeJS 版 API",
|
"description": "网易云音乐 NodeJS 版 API",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node app.js",
|
"start": "node app.js",
|
||||||
@ -39,6 +39,8 @@
|
|||||||
"axios": "^0.20.0",
|
"axios": "^0.20.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-fileupload": "^1.1.9",
|
"express-fileupload": "^1.1.9",
|
||||||
|
"md5": "^2.3.0",
|
||||||
|
"music-metadata": "^7.5.3",
|
||||||
"pac-proxy-agent": "^4.0.0",
|
"pac-proxy-agent": "^4.0.0",
|
||||||
"qrcode": "^1.4.4",
|
"qrcode": "^1.4.4",
|
||||||
"tunnel": "^0.0.6"
|
"tunnel": "^0.0.6"
|
||||||
|
39
plugins/songUpload.js
Normal file
39
plugins/songUpload.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
const axios = require('axios')
|
||||||
|
module.exports = async (query, request) => {
|
||||||
|
// 获取key和token
|
||||||
|
const tokenRes = await request(
|
||||||
|
'POST',
|
||||||
|
`https://music.163.com/weapi/nos/token/alloc`,
|
||||||
|
{
|
||||||
|
bucket: '',
|
||||||
|
ext: 'mp3',
|
||||||
|
filename: query.songFile.name.replace('.mp3', ''),
|
||||||
|
local: false,
|
||||||
|
nos_product: 3,
|
||||||
|
type: 'audio',
|
||||||
|
md5: query.songFile.md5,
|
||||||
|
},
|
||||||
|
{ crypto: 'weapi', cookie: query.cookie, proxy: query.proxy },
|
||||||
|
)
|
||||||
|
|
||||||
|
// 上传
|
||||||
|
const objectKey = tokenRes.body.result.objectKey.replace('/', '%2F')
|
||||||
|
try {
|
||||||
|
await axios({
|
||||||
|
method: 'post',
|
||||||
|
url: `http://45.127.129.8/ymusic/${objectKey}?offset=0&complete=true&version=1.0`,
|
||||||
|
headers: {
|
||||||
|
'x-nos-token': tokenRes.body.result.token,
|
||||||
|
'Content-MD5': query.songFile.md5,
|
||||||
|
'Content-Type': 'audio/mpeg',
|
||||||
|
'Content-Length': String(query.songFile.size),
|
||||||
|
},
|
||||||
|
data: query.songFile.data,
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.log('error', error.response)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...tokenRes,
|
||||||
|
}
|
||||||
|
}
|
@ -1,82 +1,82 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh">
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>更新头像</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
<head>
|
<body>
|
||||||
<meta charset="UTF-8" />
|
<input id="file" type="file" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<img id="avatar" style="height: 200px; width: 200px; border-radius: 50%" />
|
||||||
<title>更新头像</title>
|
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js
|
||||||
</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>
|
||||||
<script>
|
<script>
|
||||||
const phone = ''
|
const phone = ''
|
||||||
const password = ''
|
const password = ''
|
||||||
let cookieToken = ''
|
let cookieToken = ''
|
||||||
if (!phone || !password) {
|
if (!phone || !password) {
|
||||||
const msg = '请设置你的手机号码和密码'
|
const msg = '请设置你的手机号码和密码'
|
||||||
alert(msg)
|
alert(msg)
|
||||||
throw new Error(msg)
|
throw new Error(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
main()
|
main()
|
||||||
login()
|
login()
|
||||||
async function main() {
|
async function main() {
|
||||||
document.querySelector('input[type="file"]').addEventListener(
|
document.querySelector('input[type="file"]').addEventListener(
|
||||||
'change',
|
'change',
|
||||||
function (e) {
|
function (e) {
|
||||||
var file = this.files[0]
|
var file = this.files[0]
|
||||||
upload(file)
|
upload(file)
|
||||||
},
|
},
|
||||||
false
|
false,
|
||||||
)
|
)
|
||||||
const res = await axios({
|
const res = await axios({
|
||||||
url: `/user/detail?uid=32953014×tamp=${Date.now()}`,
|
url: `/user/detail?uid=32953014×tamp=${Date.now()}`,
|
||||||
withCredentials: true, //关键
|
withCredentials: true, //关键
|
||||||
})
|
})
|
||||||
document.querySelector('#avatar').src = res.data.profile.avatarUrl
|
document.querySelector('#avatar').src = res.data.profile.avatarUrl
|
||||||
}
|
}
|
||||||
async function login() {
|
async function login() {
|
||||||
const res = await axios({
|
const res = await axios({
|
||||||
url: `/login/cellphone?phone=${phone}&password=${password}`,
|
url: `/login/cellphone?phone=${phone}&password=${password}`,
|
||||||
withCredentials: true, //关键
|
withCredentials: true, //关键
|
||||||
})
|
})
|
||||||
cookieToken = res.data.cookie
|
cookieToken = res.data.cookie
|
||||||
}
|
}
|
||||||
async function upload(file) {
|
async function upload(file) {
|
||||||
var formData = new FormData()
|
var formData = new FormData()
|
||||||
formData.append('imgFile', file)
|
formData.append('imgFile', file)
|
||||||
const imgSize = await getImgSize(file)
|
const imgSize = await getImgSize(file)
|
||||||
const res = await axios({
|
const res = await axios({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: `http://localhost:3000/avatar/upload?cookie=${cookieToken}&imgSize=${imgSize.width}&imgX=0&imgY=0×tamp=${Date.now()}`,
|
url: `http://localhost:3000/avatar/upload?cookie=${cookieToken}&imgSize=${
|
||||||
headers: {
|
imgSize.width
|
||||||
'Content-Type': 'multipart/form-data',
|
}&imgX=0&imgY=0×tamp=${Date.now()}`,
|
||||||
},
|
headers: {
|
||||||
data: formData,
|
'Content-Type': 'multipart/form-data',
|
||||||
})
|
},
|
||||||
document.querySelector('#avatar').src = res.data.data.url
|
data: formData,
|
||||||
}
|
})
|
||||||
function getImgSize(file) {
|
document.querySelector('#avatar').src = res.data.data.url
|
||||||
return new Promise((resolve, reject) => {
|
}
|
||||||
let reader = new FileReader()
|
function getImgSize(file) {
|
||||||
reader.readAsDataURL(file)
|
return new Promise((resolve, reject) => {
|
||||||
reader.onload = function (theFile) {
|
let reader = new FileReader()
|
||||||
let image = new Image()
|
reader.readAsDataURL(file)
|
||||||
image.src = theFile.target.result
|
reader.onload = function (theFile) {
|
||||||
image.onload = function () {
|
let image = new Image()
|
||||||
resolve({
|
image.src = theFile.target.result
|
||||||
width: this.width,
|
image.onload = function () {
|
||||||
height: this.height
|
resolve({
|
||||||
})
|
width: this.width,
|
||||||
|
height: this.height,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
}
|
</script>
|
||||||
</script>
|
</body>
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
</html>
|
55
public/cloud.html
Normal file
55
public/cloud.html
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
<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" accept="audio/mpeg" />
|
||||||
|
|
||||||
|
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const phone = ''
|
||||||
|
const password = ''
|
||||||
|
let cookieToken = ''
|
||||||
|
if (!phone || !password) {
|
||||||
|
const msg = '请设置你的手机号码和密码'
|
||||||
|
alert(msg)
|
||||||
|
throw new Error(msg)
|
||||||
|
}
|
||||||
|
login()
|
||||||
|
main()
|
||||||
|
async function login() {
|
||||||
|
const res = await axios({
|
||||||
|
url: `/login/cellphone?phone=${phone}&password=${password}`,
|
||||||
|
withCredentials: true, //关键
|
||||||
|
})
|
||||||
|
cookieToken = res.data.cookie
|
||||||
|
}
|
||||||
|
async function main() {
|
||||||
|
document
|
||||||
|
.querySelector('input[type="file"]')
|
||||||
|
.addEventListener('change', function (e) {
|
||||||
|
var file = this.files[0]
|
||||||
|
upload(file)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function upload(file) {
|
||||||
|
var formData = new FormData()
|
||||||
|
formData.append('songFile', file)
|
||||||
|
const res = await axios({
|
||||||
|
method: 'post',
|
||||||
|
url: `http://localhost:3000/cloud?time=${Date.now()}`,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
data: formData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,88 +1,89 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh">
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>歌单封面上传</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
<head>
|
<body>
|
||||||
<meta charset="UTF-8" />
|
<input id="file" type="file" name="filename" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<img
|
||||||
<title>歌单封面上传</title>
|
id="playlist_cover"
|
||||||
</head>
|
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 = ''
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
<body>
|
main()
|
||||||
|
login()
|
||||||
<input id="file" type="file" name="filename" />
|
async function main() {
|
||||||
<img id="playlist_cover" style="height: 200px; width: 200px; border-radius: 50%;" />
|
document.querySelector('input[type="file"]').addEventListener(
|
||||||
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js
|
'change',
|
||||||
"></script>
|
function (e) {
|
||||||
<script>
|
var file = this.files[0]
|
||||||
const phone = ''
|
upload(file)
|
||||||
const password = ''
|
},
|
||||||
const playlist_id = ''
|
false,
|
||||||
let cookieToken = ''
|
)
|
||||||
if (!phone || !password) {
|
const res = await axios({
|
||||||
const msg = '请设置你的手机号码和密码'
|
url: `/playlist/detail?id=${playlist_id}×tamp=${Date.now()}`,
|
||||||
alert(msg)
|
})
|
||||||
throw new Error(msg)
|
document.querySelector('#playlist_cover').src = res.data.playlist.coverImgUrl
|
||||||
}
|
}
|
||||||
if (!playlist_id) {
|
async function login() {
|
||||||
const msg = '请设置你的歌单id'
|
const res = await axios({
|
||||||
alert(msg)
|
url: `/login/cellphone?phone=${phone}&password=${password}`,
|
||||||
throw new Error(msg)
|
withCredentials: true, //关键
|
||||||
}
|
})
|
||||||
|
cookieToken = res.data.cookie
|
||||||
main()
|
}
|
||||||
login()
|
async function upload(file) {
|
||||||
async function main() {
|
var formData = new FormData()
|
||||||
document.querySelector('input[type="file"]').addEventListener(
|
formData.append('imgFile', file)
|
||||||
'change',
|
const imgSize = await getImgSize(file)
|
||||||
function (e) {
|
const res = await axios({
|
||||||
var file = this.files[0]
|
method: 'post',
|
||||||
upload(file)
|
url: `http://localhost:3000/playlist/cover/update?id=${playlist_id}&cookie=${cookieToken}&imgSize=${
|
||||||
},
|
imgSize.width
|
||||||
false
|
}&imgX=0&imgY=0×tamp=${Date.now()}`,
|
||||||
)
|
headers: {
|
||||||
const res = await axios({
|
'Content-Type': 'multipart/form-data',
|
||||||
url: `/playlist/detail?id=${playlist_id}×tamp=${Date.now()}`,
|
},
|
||||||
})
|
data: formData,
|
||||||
document.querySelector('#playlist_cover').src = res.data.playlist.coverImgUrl
|
})
|
||||||
}
|
document.querySelector('#playlist_cover').src = res.data.data.url
|
||||||
async function login() {
|
}
|
||||||
const res = await axios({
|
function getImgSize(file) {
|
||||||
url: `/login/cellphone?phone=${phone}&password=${password}`,
|
return new Promise((resolve, reject) => {
|
||||||
withCredentials: true, //关键
|
let reader = new FileReader()
|
||||||
})
|
reader.readAsDataURL(file)
|
||||||
cookieToken = res.data.cookie
|
reader.onload = function (theFile) {
|
||||||
}
|
let image = new Image()
|
||||||
async function upload(file) {
|
image.src = theFile.target.result
|
||||||
var formData = new FormData()
|
image.onload = function () {
|
||||||
formData.append('imgFile', file)
|
resolve({
|
||||||
const imgSize = await getImgSize(file)
|
width: this.width,
|
||||||
const res = await axios({
|
height: this.height,
|
||||||
method: 'post',
|
})
|
||||||
url: `http://localhost:3000/playlist/cover/update?id=${playlist_id}&cookie=${cookieToken}&imgSize=${imgSize.width}&imgX=0&imgY=0×tamp=${Date.now()}`,
|
}
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data',
|
|
||||||
},
|
|
||||||
data: formData,
|
|
||||||
})
|
|
||||||
document.querySelector('#playlist_cover').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>
|
||||||
</script>
|
</body>
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -23,36 +23,36 @@
|
|||||||
$.ajax({
|
$.ajax({
|
||||||
url: `/login/cellphone?phone=${phone}&password=${password}`,
|
url: `/login/cellphone?phone=${phone}&password=${password}`,
|
||||||
xhrFields: {
|
xhrFields: {
|
||||||
withCredentials: true //关键
|
withCredentials: true, //关键
|
||||||
},
|
},
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
console.log(data)
|
console.log(data)
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: `/recommend/resource `,
|
url: `/recommend/resource `,
|
||||||
xhrFields: {
|
xhrFields: {
|
||||||
withCredentials: true //关键
|
withCredentials: true, //关键
|
||||||
},
|
},
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
console.log(data)
|
console.log(data)
|
||||||
},
|
},
|
||||||
error: function (err) {
|
error: function (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
error: function (err) {
|
error: function (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
axios({
|
axios({
|
||||||
url: `/login/cellphone?phone=${phone}&password=${password}`,
|
url: `/login/cellphone?phone=${phone}&password=${password}`,
|
||||||
withCredentials: true //关键
|
withCredentials: true, //关键
|
||||||
}).then(function (res) {
|
}).then(function (res) {
|
||||||
console.log(res.data)
|
console.log(res.data)
|
||||||
axios({
|
axios({
|
||||||
url: `/recommend/resource`,
|
url: `/recommend/resource`,
|
||||||
withCredentials: true //关键
|
withCredentials: true, //关键
|
||||||
}).then(function (res) {
|
}).then(function (res) {
|
||||||
console.log(res.data)
|
console.log(res.data)
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user