提交 75cb79fc 编写于 作者: B binaryify

v4.0,新增云盘上传接口以及二维码登录相关接口和相关demo,升级部分接口加密方法 #70 , #121 ,#121 , #153 ,#248 ,...

v4.0,新增云盘上传接口以及二维码登录相关接口和相关demo,升级部分接口加密方法 #70 , #121 ,#121 , #153 ,#248 , #705 , #716 , #745 , #1055
上级 77775b25
static static
docs docs
node_modules node_modules
\ No newline at end of file module_example
# 更新日志 # 更新日志
### 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 @@ ...@@ -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 ...@@ -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` 字 ...@@ -420,22 +424,34 @@ v3.30.0后支持手动传入cookie,登录接口返回内容新增 `cookie` 字
} }
``` ```
#### 3. 二维码登录 #### 3. 二维码登录
说明: 二维码登录涉及到3个接口 说明: 二维码登录涉及到3个接口,调用务必带上时间戳,防止缓存
1. 二维码key生成接口 ##### 1. 二维码key生成接口
说明: 调用此接口可生成一个key
说明: 调用此接口可生成一个key
**接口地址 :** `/login/qr/key` **接口地址 :** `/login/qr/key`
##### 2. 二维码生成接口
说明: 调用此接口传入上一个接口生成的key可生成二维码图片的base64和二维码信息,可使用base64展示图片,或者使用二维码信息内容自行使用第三方二维码生产库渲染二维码
必选参数: `key`,由第一个接口生成
可选参数: `qrimg` 传入后会额外返回二维码图片base64编码
**接口地址 :** `/login/qr/create`
**调用例子 :** `/login/qr/create?key=xxx`
2. 二维码生成接口
说明: 调用此接口传入上一个接口生成的key可生成二维码图片的base64和二维码信息,可使用base64展示图片,或者使用二维码信息内容自行使用第三方二维码生产库渲染二维码
可选参数: `qrimg` 传入后
**接口地址 :** `/login/qr/create`
##### 3. 二维码检测扫码状态接口
说明: 轮询此接口可获取二维码扫码状态,800为二维码过期,801为等待扫码,802为待确认,803为授权登录成功(803状态码下会返回cookies)
3. 二维码检测扫码状态接口 必选参数: `key`,由第一个接口生成
说明: 轮询此接口可获取二维码扫码状态,801为等待扫码,802为待确认,803为授权登陆成功
**接口地址 :** `/login/qr/check` **接口地址 :** `/login/qr/check`
**调用例子 :** `/login/qr/check?key=xxx`
调用可参考项目文件例子`/public/qrlogin.html` (访问地址:http://localhost:3000/qrlogin.html)
#### 注意 #### 注意
...@@ -640,7 +656,7 @@ signature:用户签名 ...@@ -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: 歌单标签 ...@@ -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 获取具 ...@@ -1238,10 +1269,6 @@ mp3url 不能直接用 , 可通过 `/song/url` 接口传入歌曲 id 获取具
**调用例子 :** `/search?keywords= 海阔天空` `/cloudsearch?keywords= 海阔天空` **调用例子 :** `/search?keywords= 海阔天空` `/cloudsearch?keywords= 海阔天空`
返回数据如下图 :
![搜索音乐](https://raw.githubusercontent.com/Binaryify/NeteaseCloudMusicApi/master/static/%E6%90%9C%E7%B4%A2.png)
### 默认搜索关键词 ### 默认搜索关键词
说明 : 调用此接口 , 可获取默认搜索关键词 说明 : 调用此接口 , 可获取默认搜索关键词
...@@ -1443,8 +1470,7 @@ mp3url 不能直接用 , 可通过 `/song/url` 接口传入歌曲 id 获取具 ...@@ -1443,8 +1470,7 @@ mp3url 不能直接用 , 可通过 `/song/url` 接口传入歌曲 id 获取具
### 歌曲评论 ### 歌曲评论
说明 : 调用此接口 , 传入音乐 id 和 limit 参数 , 可获得该音乐的所有评论 ( 不需要 说明 : 调用此接口 , 传入音乐 id 和 limit 参数 , 可获得该音乐的所有评论 ( 不需要登录 )
登录 )
**必选参数 :** `id`: 音乐 id **必选参数 :** `id`: 音乐 id
...@@ -2553,6 +2579,19 @@ type : 地区 ...@@ -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
......
...@@ -1360,3 +1360,32 @@ export function artist_detail( ...@@ -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) => { ...@@ -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 },
) )
} }
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) => { ...@@ -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) => { ...@@ -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) => { ...@@ -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) => { ...@@ -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,
......
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,
})
}
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) => { ...@@ -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) => { ...@@ -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,
......
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()
{ {
"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 @@ ...@@ -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"
......
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,
}
}
<!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&timestamp=${Date.now()}`, url: `/user/detail?uid=32953014&timestamp=${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&timestamp=${Date.now()}`, url: `http://localhost:3000/avatar/upload?cookie=${cookieToken}&imgSize=${
headers: { imgSize.width
'Content-Type': 'multipart/form-data', }&imgX=0&imgY=0&timestamp=${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>
\ No newline at end of file
<!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>
<!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}&timestamp=${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&timestamp=${Date.now()}`,
) headers: {
const res = await axios({ 'Content-Type': 'multipart/form-data',
url: `/playlist/detail?id=${playlist_id}&timestamp=${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&timestamp=${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>
\ No newline at end of file
...@@ -23,36 +23,36 @@ ...@@ -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)
}) })
...@@ -60,4 +60,4 @@ ...@@ -60,4 +60,4 @@
</script> </script>
</body> </body>
</html> </html>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册